ktutorial-commits Mailing List for KTutorial (Page 2)
Status: Alpha
Brought to you by:
danxuliu
You can subscribe to this list here.
2010 |
Jan
(1) |
Feb
(36) |
Mar
(117) |
Apr
(11) |
May
(8) |
Jun
(1) |
Jul
|
Aug
(2) |
Sep
(21) |
Oct
(16) |
Nov
|
Dec
|
---|---|---|---|---|---|---|---|---|---|---|---|---|
2011 |
Jan
(1) |
Feb
|
Mar
(6) |
Apr
(6) |
May
(15) |
Jun
(15) |
Jul
(6) |
Aug
|
Sep
(1) |
Oct
(4) |
Nov
|
Dec
|
2012 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
(10) |
Jul
(4) |
Aug
(29) |
Sep
(4) |
Oct
|
Nov
|
Dec
(2) |
From: <dan...@us...> - 2012-08-10 18:06:01
|
Revision: 365 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=365&view=rev Author: danxuliu Date: 2012-08-10 18:05:55 +0000 (Fri, 10 Aug 2012) Log Message: ----------- Fix the exported CamelCase variable names when the name of the remote object used in the WaitFor includes ancestor names. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.cpp trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.h trunk/ktutorial/ktutorial-editor/tests/unit/serialization/JavascriptExporterTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.cpp 2012-08-10 11:40:27 UTC (rev 364) +++ trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.cpp 2012-08-10 18:05:55 UTC (rev 365) @@ -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 * @@ -546,7 +546,7 @@ return ""; } - text.remove(QRegExp("[^\\w ]")); + text.replace(QRegExp("[^\\w ]"), " "); QStringList words = text.split(' ', QString::SkipEmptyParts); QString upperCamelCase; Modified: trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.h 2012-08-10 11:40:27 UTC (rev 364) +++ trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.h 2012-08-10 18:05:55 UTC (rev 365) @@ -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 * @@ -328,7 +328,8 @@ /** * Returns the lowerCamelCase version of the given text. * Note that the returned text contains only letters, digits or underscores. - * Any other character is removed. + * Any other character is removed, and the next valid character is upper + * cased. * * @param text The string to get its lowerCamelCase version. * @return The lowerCamelCase version of the text. @@ -338,7 +339,8 @@ /** * Returns the UpperCamelCase version of the given text. * Note that the returned text contains only letters, digits or underscores. - * Any other character is removed. + * Any other character is removed, and the next valid character is upper + * cased. * * @param text The string to get its UpperCamelCase version. * @return The UpperCamelCase version of the text. Modified: trunk/ktutorial/ktutorial-editor/tests/unit/serialization/JavascriptExporterTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/serialization/JavascriptExporterTest.cpp 2012-08-10 11:40:27 UTC (rev 364) +++ trunk/ktutorial/ktutorial-editor/tests/unit/serialization/JavascriptExporterTest.cpp 2012-08-10 18:05:55 UTC (rev 365) @@ -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,23 +82,29 @@ void testReactionOptionCustomCodeWithEscapeSequences(); void testReactionOptionCustomCodeWithoutOptionNameOrCustomCode(); void testReactionConditionNextStep(); + void testReactionConditionNextStepWithAncestorNames(); void testReactionConditionNextStepWithEscapeSequences(); void testReactionConditionNextStepWithoutConditionOrStepId(); void testReactionConditionCustomCode(); + void testReactionConditionCustomCodeWithAncestorNames(); void testReactionConditionCustomCodeWithEscapeSequences(); void testReactionConditionCustomCodeWithoutConditionOrCustomCode(); void testWaitForEvent(); + void testWaitForEventWithAncestorNames(); void testWaitForEventWithEscapeSequences(); void testWaitForEventWithoutReceiverNameOrEventName(); void testWaitForProperty(); + void testWaitForPropertyWithAncestorNames(); void testWaitForPropertyWithEscapeSequences(); void testWaitForPropertyWithoutObjectNameOrPropertyNameOrValue(); void testWaitForSignal(); + void testWaitForSignalWithAncestorNames(); void testWaitForSignalWithEscapeSequences(); void testWaitForSignalWithoutEmitterNameOrSignalName(); void testWaitForStepActivation(); void testWaitForWindow(); + void testWaitForWindowWithAncestorNames(); void testWaitForWindowWithEscapeSequences(); void testWaitForWindowWithoutWindowObjectName(); void testWaitForComposed(); @@ -818,6 +824,44 @@ QCOMPARE(exportedTutorial, expected); } +void JavascriptExporterTest::testReactionConditionNextStepWithAncestorNames() { + Tutorial tutorial; + Step* step = new Step(); + step->setId("The id"); + tutorial.addStep(step); + + WaitForSignal* waitForSignal = new WaitForSignal(); + waitForSignal->setEmitterName("Grandparent/parent/the emitter name"); + waitForSignal->setSignalName("theSignalName(Argument1Type, Argument2Type)"); + + Reaction* reaction = new Reaction(); + reaction->setTriggerType(Reaction::ConditionMet); + reaction->setWaitFor(waitForSignal); + reaction->setResponseType(Reaction::NextStep); + reaction->setNextStepId("Another step"); + step->addReaction(reaction); + + JavascriptExporter exporter; + QString exportedTutorial = exporter.exportTutorial(&tutorial); + + QString expected = +TUTORIAL_EMPTY_INFORMATION_CODE +STEP_WITH_ID_THE_ID_AND_EMPTY_TEXT_START_CODE +"function theIdStepSetup(step) {\n" +" waitForTheSignalNameByGrandparentParentTheEmitterName = \ +ktutorial.newWaitFor(\"WaitForSignal\");\n" +" waitForTheSignalNameByGrandparentParentTheEmitterName.setSignal(\ +ktutorial.findObject(\"Grandparent/parent/the emitter name\"), \ +\"theSignalName(Argument1Type, Argument2Type)\");\n" +" step.addWaitFor(waitForTheSignalNameByGrandparentParentTheEmitterName, \ +\"Another step\");\n" +"}\n" +CONNECT_STEP_SETUP +STEP_WITH_ID_THE_ID_AND_EMPTY_TEXT_END_CODE; + + QCOMPARE(exportedTutorial, expected); +} + void JavascriptExporterTest:: testReactionConditionNextStepWithEscapeSequences() { Tutorial tutorial; @@ -947,6 +991,50 @@ } void JavascriptExporterTest:: + testReactionConditionCustomCodeWithAncestorNames() { + Tutorial tutorial; + Step* step = new Step(); + step->setId("The id"); + tutorial.addStep(step); + + WaitForSignal* waitForSignal = new WaitForSignal(); + waitForSignal->setEmitterName("Grandparent/parent/the emitter name"); + waitForSignal->setSignalName("theSignalName(Argument1Type, Argument2Type)"); + + Reaction* reaction = new Reaction(); + reaction->setTriggerType(Reaction::ConditionMet); + reaction->setWaitFor(waitForSignal); + reaction->setResponseType(Reaction::CustomCode); + reaction->setCustomCode("The custom\ncode"); + step->addReaction(reaction); + + JavascriptExporter exporter; + QString exportedTutorial = exporter.exportTutorial(&tutorial); + + QString expected = +TUTORIAL_EMPTY_INFORMATION_CODE +STEP_WITH_ID_THE_ID_AND_EMPTY_TEXT_START_CODE +"function theIdStepSetup(step) {\n" +" waitForTheSignalNameByGrandparentParentTheEmitterName = \ +ktutorial.newWaitFor(\"WaitForSignal\");\n" +" waitForTheSignalNameByGrandparentParentTheEmitterName.setSignal(\ +ktutorial.findObject(\"Grandparent/parent/the emitter name\"), \ +\"theSignalName(Argument1Type, Argument2Type)\");\n" +" step.addWaitFor(waitForTheSignalNameByGrandparentParentTheEmitterName, \ +self, \"theIdStepWaitForTheSignalNameByGrandparentParentTheEmitterNameConditionMet()\");\n" +"}\n" +CONNECT_STEP_SETUP +"function theIdStepWaitForTheSignalNameByGrandparentParentTheEmitterNameConditionMet() {\n" +" The custom\n" +" code\n" +"}\n" +"\n" +STEP_WITH_ID_THE_ID_AND_EMPTY_TEXT_END_CODE; + + QCOMPARE(exportedTutorial, expected); +} + +void JavascriptExporterTest:: testReactionConditionCustomCodeWithEscapeSequences() { Tutorial tutorial; Step* step = new Step(); @@ -1083,6 +1171,44 @@ QCOMPARE(exportedTutorial, expected); } +void JavascriptExporterTest::testWaitForEventWithAncestorNames() { + Tutorial tutorial; + Step* step = new Step(); + step->setId("The id"); + tutorial.addStep(step); + + WaitForEvent* waitForEvent = new WaitForEvent(); + waitForEvent->setReceiverName("Grandparent/parent/the receiver name"); + waitForEvent->setEventName("TheEventName"); + + Reaction* reaction = new Reaction(); + reaction->setTriggerType(Reaction::ConditionMet); + reaction->setWaitFor(waitForEvent); + reaction->setResponseType(Reaction::NextStep); + reaction->setNextStepId("Another step"); + step->addReaction(reaction); + + JavascriptExporter exporter; + QString exportedTutorial = exporter.exportTutorial(&tutorial); + + QString expected = +TUTORIAL_EMPTY_INFORMATION_CODE +STEP_WITH_ID_THE_ID_AND_EMPTY_TEXT_START_CODE +"function theIdStepSetup(step) {\n" +" waitForTheEventNameInGrandparentParentTheReceiverName = \ +ktutorial.newWaitFor(\"WaitForEvent\");\n" +" waitForTheEventNameInGrandparentParentTheReceiverName.setEvent(\ +ktutorial.findObject(\"Grandparent/parent/the receiver name\"), \ +\"TheEventName\");\n" +" step.addWaitFor(waitForTheEventNameInGrandparentParentTheReceiverName, \ +\"Another step\");\n" +"}\n" +CONNECT_STEP_SETUP +STEP_WITH_ID_THE_ID_AND_EMPTY_TEXT_END_CODE; + + QCOMPARE(exportedTutorial, expected); +} + void JavascriptExporterTest::testWaitForEventWithEscapeSequences() { Tutorial tutorial; Step* step = new Step(); @@ -1203,6 +1329,45 @@ QCOMPARE(exportedTutorial, expected); } +void JavascriptExporterTest::testWaitForPropertyWithAncestorNames() { + Tutorial tutorial; + Step* step = new Step(); + step->setId("The id"); + tutorial.addStep(step); + + WaitForProperty* waitForProperty = new WaitForProperty(); + waitForProperty->setObjectName("Grandparent/parent/the object name"); + waitForProperty->setPropertyName("thePropertyName"); + waitForProperty->setValue("TheValue"); + + Reaction* reaction = new Reaction(); + reaction->setTriggerType(Reaction::ConditionMet); + reaction->setWaitFor(waitForProperty); + reaction->setResponseType(Reaction::NextStep); + reaction->setNextStepId("Another step"); + step->addReaction(reaction); + + JavascriptExporter exporter; + QString exportedTutorial = exporter.exportTutorial(&tutorial); + + QString expected = +TUTORIAL_EMPTY_INFORMATION_CODE +STEP_WITH_ID_THE_ID_AND_EMPTY_TEXT_START_CODE +"function theIdStepSetup(step) {\n" +" waitForThePropertyNameInGrandparentParentTheObjectName = \ +ktutorial.newWaitFor(\"WaitForProperty\");\n" +" waitForThePropertyNameInGrandparentParentTheObjectName.setProperty(\ +ktutorial.findObject(\"Grandparent/parent/the object name\"), \ +\"thePropertyName\", TheValue);\n" +" step.addWaitFor(waitForThePropertyNameInGrandparentParentTheObjectName, \ +\"Another step\");\n" +"}\n" +CONNECT_STEP_SETUP +STEP_WITH_ID_THE_ID_AND_EMPTY_TEXT_END_CODE; + + QCOMPARE(exportedTutorial, expected); +} + void JavascriptExporterTest::testWaitForPropertyWithEscapeSequences() { Tutorial tutorial; Step* step = new Step(); @@ -1340,6 +1505,44 @@ QCOMPARE(exportedTutorial, expected); } +void JavascriptExporterTest::testWaitForSignalWithAncestorNames() { + Tutorial tutorial; + Step* step = new Step(); + step->setId("The id"); + tutorial.addStep(step); + + WaitForSignal* waitForSignal = new WaitForSignal(); + waitForSignal->setEmitterName("Grandparent/parent/the emitter name"); + waitForSignal->setSignalName("theSignalName(Argument1Type, Argument2Type)"); + + Reaction* reaction = new Reaction(); + reaction->setTriggerType(Reaction::ConditionMet); + reaction->setWaitFor(waitForSignal); + reaction->setResponseType(Reaction::NextStep); + reaction->setNextStepId("Another step"); + step->addReaction(reaction); + + JavascriptExporter exporter; + QString exportedTutorial = exporter.exportTutorial(&tutorial); + + QString expected = +TUTORIAL_EMPTY_INFORMATION_CODE +STEP_WITH_ID_THE_ID_AND_EMPTY_TEXT_START_CODE +"function theIdStepSetup(step) {\n" +" waitForTheSignalNameByGrandparentParentTheEmitterName = \ +ktutorial.newWaitFor(\"WaitForSignal\");\n" +" waitForTheSignalNameByGrandparentParentTheEmitterName.setSignal(\ +ktutorial.findObject(\"Grandparent/parent/the emitter name\"), \ +\"theSignalName(Argument1Type, Argument2Type)\");\n" +" step.addWaitFor(waitForTheSignalNameByGrandparentParentTheEmitterName, \ +\"Another step\");\n" +"}\n" +CONNECT_STEP_SETUP +STEP_WITH_ID_THE_ID_AND_EMPTY_TEXT_END_CODE; + + QCOMPARE(exportedTutorial, expected); +} + void JavascriptExporterTest::testWaitForSignalWithEscapeSequences() { Tutorial tutorial; Step* step = new Step(); @@ -1490,6 +1693,43 @@ QCOMPARE(exportedTutorial, expected); } +void JavascriptExporterTest::testWaitForWindowWithAncestorNames() { + Tutorial tutorial; + Step* step = new Step(); + step->setId("The id"); + tutorial.addStep(step); + + WaitForWindow* waitForWindow = new WaitForWindow(); + waitForWindow->setWindowObjectName("Grandparent/parent/" + "the window object name"); + + Reaction* reaction = new Reaction(); + reaction->setTriggerType(Reaction::ConditionMet); + reaction->setWaitFor(waitForWindow); + reaction->setResponseType(Reaction::NextStep); + reaction->setNextStepId("Another step"); + step->addReaction(reaction); + + JavascriptExporter exporter; + QString exportedTutorial = exporter.exportTutorial(&tutorial); + + QString expected = +TUTORIAL_EMPTY_INFORMATION_CODE +STEP_WITH_ID_THE_ID_AND_EMPTY_TEXT_START_CODE +"function theIdStepSetup(step) {\n" +" waitForGrandparentParentTheWindowObjectNameToBeShown = \ +ktutorial.newWaitFor(\"WaitForWindow\");\n" +" waitForGrandparentParentTheWindowObjectNameToBeShown.setWindowObjectName(\ +\"Grandparent/parent/the window object name\");\n" +" step.addWaitFor(waitForGrandparentParentTheWindowObjectNameToBeShown, \ +\"Another step\");\n" +"}\n" +CONNECT_STEP_SETUP +STEP_WITH_ID_THE_ID_AND_EMPTY_TEXT_END_CODE; + + QCOMPARE(exportedTutorial, expected); +} + void JavascriptExporterTest::testWaitForWindowWithEscapeSequences() { Tutorial tutorial; Step* step = new Step(); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2012-08-10 11:40:33
|
Revision: 364 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=364&view=rev Author: danxuliu Date: 2012-08-10 11:40:27 +0000 (Fri, 10 Aug 2012) Log Message: ----------- Fix documentation. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/view/WaitForSignalWidget.h Modified: trunk/ktutorial/ktutorial-editor/src/view/WaitForSignalWidget.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/WaitForSignalWidget.h 2012-08-10 11:37:32 UTC (rev 363) +++ trunk/ktutorial/ktutorial-editor/src/view/WaitForSignalWidget.h 2012-08-10 11:40:27 UTC (rev 364) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 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 * @@ -92,7 +92,7 @@ #ifdef QT_QTDBUS_FOUND /** - * Sets the emitter name based in the chosen remote object. + * Sets the signal completion based on the chosen remote object. * * @param remoteObject The chosen RemoteObject. */ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2012-08-10 11:37:38
|
Revision: 363 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=363&view=rev Author: danxuliu Date: 2012-08-10 11:37:32 +0000 (Fri, 10 Aug 2012) Log Message: ----------- Fix typo. Modified Paths: -------------- 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 Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectChooserTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectChooserTest.cpp 2012-08-10 11:28:21 UTC (rev 362) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectChooserTest.cpp 2012-08-10 11:37:32 UTC (rev 363) @@ -102,7 +102,7 @@ mPath = QApplication::applicationDirPath() + "/../targetapplication/TargetApplicationStub"; - //Avoid signals from previews tests to be delivered to the next ones + //Avoid signals from previous tests to be delivered to the next ones //setting a new TargetApplication delete TargetApplication::sSelf; TargetApplication::sSelf = new TargetApplication(); Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameRegisterTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameRegisterTest.cpp 2012-08-10 11:28:21 UTC (rev 362) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameRegisterTest.cpp 2012-08-10 11:37:32 UTC (rev 363) @@ -88,7 +88,7 @@ mPath = QApplication::applicationDirPath() + "/../targetapplication/TargetApplicationStub"; - //Avoid signals from previews tests to be delivered to the next ones + //Avoid signals from previous tests to be delivered to the next ones //setting a new TargetApplication delete TargetApplication::sSelf; TargetApplication::sSelf = new TargetApplication(); Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp 2012-08-10 11:28:21 UTC (rev 362) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp 2012-08-10 11:37:32 UTC (rev 363) @@ -114,7 +114,7 @@ mPath = QApplication::applicationDirPath() + "/../targetapplication/TargetApplicationStub"; - //Avoid signals from previews tests to be delivered to the next ones + //Avoid signals from previous tests to be delivered to the next ones //setting a new TargetApplication delete TargetApplication::sSelf; TargetApplication::sSelf = new TargetApplication(); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2012-08-10 11:28:28
|
Revision: 362 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=362&view=rev Author: danxuliu Date: 2012-08-10 11:28:21 +0000 (Fri, 10 Aug 2012) Log Message: ----------- Remove "clear" method, as it is used nowhere. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.cpp trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.h trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteObjectMapperTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.cpp 2012-08-10 11:19:33 UTC (rev 361) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.cpp 2012-08-10 11:28:21 UTC (rev 362) @@ -59,11 +59,3 @@ return remoteClass; } - -void RemoteObjectMapper::clear() { - qDeleteAll(mRemoteObjects); - mRemoteObjects.clear(); - - qDeleteAll(mRemoteClasses); - mRemoteClasses.clear(); -} Modified: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.h 2012-08-10 11:19:33 UTC (rev 361) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.h 2012-08-10 11:28:21 UTC (rev 362) @@ -32,7 +32,7 @@ * depending on the case. The same behavior is shown for RemoteClasses. * * The RemoteObjectMapper also has ownership of the RemoteObjects and - * RemoteClasses, so they are deleted when the mapper is cleared or destroyed. + * RemoteClasses, so they are deleted when the mapper is destroyed. */ class RemoteObjectMapper { public: @@ -52,8 +52,8 @@ /** * Returns the RemoteObject associated with the given object id. - * The RemoteObject is destroyed when this RemoteObjectMapper is cleared or - * destroyed, so consider using QPointer to store it. + * The RemoteObject is destroyed when this RemoteObjectMapper is destroyed, + * so consider using QPointer to store it. * The object id 0 is a special case, and it is associated to the null * object; a null pointer is returned in this case. * @@ -64,19 +64,14 @@ /** * Returns the RemoteClass associated with the given class name. - * The RemoteClass is destroyed when this RemoteObjectMapper is cleared or - * destroyed, so consider using QPointer to store it. + * The RemoteClass is destroyed when this RemoteObjectMapper is destroyed, + * so consider using QPointer to store it. * * @param className The name of the remote class. * @return The RemoteClass. */ RemoteClass* remoteClass(const QString& className); - /** - * Destroys all the mapped RemoteObjects and RemoteClasses. - */ - void clear(); - private: /** @@ -85,14 +80,12 @@ QString mService; /** - * All the RemoteObjects already requested since the last time this - * RemoteObjectMapper was cleared. + * All the RemoteObjects already requested. */ QHash<int, RemoteObject*> mRemoteObjects; /** - * All the RemoteClasses already requested since the last time this - * RemoteObjectMapper was cleared. + * All the RemoteClasses already requested. */ QHash<QString, RemoteClass*> mRemoteClasses; Modified: trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteObjectMapperTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteObjectMapperTest.cpp 2012-08-10 11:19:33 UTC (rev 361) +++ trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteObjectMapperTest.cpp 2012-08-10 11:28:21 UTC (rev 362) @@ -43,7 +43,6 @@ void testRemoteClassSeveralIds(); void testRemoteClassTwice(); - void testClear(); private: StubObjectRegister* mObjectRegister; @@ -149,23 +148,6 @@ QCOMPARE(remoteClass1->className(), QString("Class")); } -void RemoteObjectMapperTest::testClear() { - RemoteObjectMapper mapper(QDBusConnection::sessionBus().baseService()); - - QPointer<RemoteObject> remoteObject1 = mapper.remoteObject(42); - QPointer<RemoteClass> remoteClass1 = mapper.remoteClass("Class"); - - mapper.clear(); - - RemoteObject* remoteObject2 = mapper.remoteObject(42); - RemoteClass* remoteClass2 = mapper.remoteClass("Class"); - - QVERIFY(!remoteObject1); - QVERIFY(remoteObject2); - QVERIFY(!remoteClass1); - QVERIFY(remoteClass2); -} - QTEST_MAIN(RemoteObjectMapperTest) #include "RemoteObjectMapperTest.moc" This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2012-08-10 11:19:41
|
Revision: 361 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=361&view=rev Author: danxuliu Date: 2012-08-10 11:19:33 +0000 (Fri, 10 Aug 2012) Log Message: ----------- Fix invalid documentation. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.h Modified: trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.h 2012-08-10 10:57:33 UTC (rev 360) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.h 2012-08-10 11:19:33 UTC (rev 361) @@ -57,9 +57,7 @@ * The event spy filters all the events received by the main window and, * recursively, all its children objects. It is used in the remote KTutorial * editor to know, for example, that some widget got the focus, or that the - * mouse entered in some widget. Using the information provided by the EventSpy, - * the KTutorial editor can provide a way to the tutorial author to select - * widgets directly from a running application just with the mouse. + * mouse entered in some widget. * * The main object is registered at "/ktutorial" path, and provides the * "org.kde.ktutorial.EditorSupport" interface. The object register is This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2012-08-10 10:57:43
|
Revision: 360 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=360&view=rev Author: danxuliu Date: 2012-08-10 10:57:33 +0000 (Fri, 10 Aug 2012) Log Message: ----------- Improve the unique name generation to take into account ambiguity solving rules. Now only objects with a unique name (or a unique name after applying the ambiguity solving rules) can be chosen by the user; if he tries to chose an object that has no unique name a warning is shown. Also, the concept of "best name" is introduced, so the objects that have no unique name are still shown in the completion box with their full path to help the user identify them and fix the target application. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.cpp trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.h trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.cpp trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.h trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.cpp trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.h trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteClassStubs.h trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteEditorSupportTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteObjectTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameRegisterTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp trunk/ktutorial/ktutorial-library/src/KTutorial.cpp trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.cpp trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.h trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupportAdaptor.cpp trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupportAdaptor.h trunk/ktutorial/ktutorial-library/tests/editorsupport/EditorSupportAdaptorTest.cpp trunk/ktutorial/ktutorial-library/tests/editorsupport/EditorSupportTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.cpp 2012-08-09 15:59:47 UTC (rev 359) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.cpp 2012-08-10 10:57:33 UTC (rev 360) @@ -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 * @@ -49,6 +49,16 @@ return mMapper->remoteObject(reply.value()); } +RemoteObject* RemoteEditorSupport::findObject(const QString& name) + throw (DBusException) { + QDBusReply<int> reply = call("findObject", name); + if (!reply.isValid()) { + throw DBusException(reply.error().message()); + } + + return mMapper->remoteObject(reply.value()); +} + void RemoteEditorSupport::highlight(RemoteObject* remoteWidget) throw (DBusException) { QDBusReply<void> reply = call("highlight", remoteWidget->objectId()); Modified: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.h 2012-08-09 15:59:47 UTC (rev 359) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.h 2012-08-10 10:57:33 UTC (rev 360) @@ -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 * @@ -70,6 +70,15 @@ RemoteObject* mainWindow() throw (DBusException); /** + * Returns the RemoteObject with the given name. + * The name can include ancestor names. + * + * @param name The name of the RemoteObject to find. + * @return The RemoteObject with the given name, or 0 if there is none. + */ + RemoteObject* findObject(const QString& name) throw (DBusException); + + /** * Highlights the widget represented by the given remote object. * If the object does not represent a widget nothing is highlighted. * Modified: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.cpp 2012-08-09 15:59:47 UTC (rev 359) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.cpp 2012-08-10 10:57:33 UTC (rev 360) @@ -52,60 +52,58 @@ QString RemoteObjectNameRegister::uniqueName(RemoteObject* remoteObject) const throw (DBusException) { - QString name = remoteObject->name(); - if (name.isEmpty()) { - return ""; - } + QStringList reversedPath = bestNameAsReversedPath(remoteObject); + QString name = reversedPathAsName(reversedPath); QList<QStringList> reversedPathsToHomonyms = reversedPathsToHomonymsOf(remoteObject); - if (reversedPathsToHomonyms.count() == 0) { + if (isUniquePath(reversedPath, reversedPathsToHomonyms)) { return name; } - QStringList reversedFullPath = reversedPathTo(remoteObject); - - for (int i=1; i<reversedFullPath.size(); ++i) { - if (ancestorNotInPaths(reversedFullPath[i], reversedPathsToHomonyms)) { - return reversedFullPath[i] + '/' + reversedFullPath[0]; - } + //Although the name is ambiguous, it identifies the given remote object + if (findRemoteObject(name) == remoteObject) { + return name; } - QStringList reversedUniquePath; - reversedUniquePath.append(reversedFullPath[0]); - - for (int i=1; i<reversedFullPath.size(); ++i) { - reversedUniquePath.append(reversedFullPath[i]); - - if (isUniquePath(reversedUniquePath, reversedPathsToHomonyms)) { - return reversedPathAsName(reversedUniquePath); - } - } - - return reversedPathAsName(reversedUniquePath); + return ""; } -QStringList RemoteObjectNameRegister::uniqueNames(const QString& name) const +QStringList RemoteObjectNameRegister::bestNames(const QString& name) const throw (DBusException) { Q_ASSERT(mRemoteObjectForName.values(name).count() > 0); - QStringList uniqueNames; + QStringList bestNames; if (mRemoteObjectForName.values(name).count() <= 1) { - uniqueNames.append(name); - return uniqueNames; + bestNames.append(name); + return bestNames; } foreach (RemoteObject* remoteObject, mRemoteObjectForName.values(name)) { - uniqueNames.append(uniqueName(remoteObject)); + bestNames.append(bestName(remoteObject)); } - return uniqueNames; + return bestNames; } + +//As KTutorial applies several rules to resolve ambiguous names (a name that +//represents two or more objects), and querying the editor support is relatively +//quick (around 5-6 miliseconds on average for Showfoto on a computer from the +//year 2002), the target application itself is queried instead of reimplementing +//those rules in the editor, as they would be very cumbersome to be tested +//properly, and prevents having to duplicate the changes when some rule is +//modified in the library. RemoteObject* RemoteObjectNameRegister::findRemoteObject(const QString& name) - const { - return findRemoteObject(name, 0); + const +throw (DBusException) { + if (TargetApplication::self()->remoteEditorSupport()) { + return TargetApplication::self()->remoteEditorSupport()-> + findObject(name); + } + + return 0; } bool RemoteObjectNameRegister::isBeingUpdated() const { @@ -149,13 +147,78 @@ } } +QString RemoteObjectNameRegister::bestName(RemoteObject* remoteObject) const +throw (DBusException) { + return reversedPathAsName(bestNameAsReversedPath(remoteObject)); +} + +QStringList RemoteObjectNameRegister::bestNameAsReversedPath( + RemoteObject* remoteObject) const +throw (DBusException) { + QStringList reversedUniquePath; + + QString name = remoteObject->name(); + if (name.isEmpty()) { + return reversedUniquePath; + } + + reversedUniquePath.append(name); + + QList<QStringList> reversedPathsToHomonyms = + reversedPathsToHomonymsOf(remoteObject); + if (reversedPathsToHomonyms.count() == 0) { + return reversedUniquePath; + } + + QStringList reversedFullPath = reversedPathTo(remoteObject); + + //Get deepest ancestor with unique name + int uniqueAncestorIndex = -1; + for (int i=1; i<reversedFullPath.size() && uniqueAncestorIndex == -1; ++i) { + if (mRemoteObjectForName.values(reversedFullPath[i]).count() == 1) { + uniqueAncestorIndex = i; + } + } + + //If there is an ancestor with a unique name and it has only one descendant + //with the name of the remote object (that is, the remote object itself), + //return "ancestor/object". + if (uniqueAncestorIndex != -1 && + ancestorNotInPaths(reversedFullPath[uniqueAncestorIndex], + reversedPathsToHomonyms)) { + reversedUniquePath.append(reversedFullPath[uniqueAncestorIndex]); + return reversedUniquePath; + } + + //If there is more than one descendant with that name, mark the ancestor + //with the unique name as the limit to descend when looking for a unique + //path + int limit = uniqueAncestorIndex; + if (limit == -1) { + //If no ancestor has a unique name, mark the shallowest ancestor as the + //limit to descend + limit = reversedFullPath.size() - 1; + } + + //Add ancestors to the path until limit or until a unique path is found + for (int i=1; i <= limit; ++i) { + reversedUniquePath.append(reversedFullPath[i]); + + if (isUniquePath(reversedUniquePath, reversedPathsToHomonyms)) { + return reversedUniquePath; + } + } + + return reversedUniquePath; +} + QStringList RemoteObjectNameRegister::reversedPathTo( RemoteObject* remoteObject) const throw (DBusException) { QStringList reversedPath; RemoteObject* parent = remoteObject; - while (parent) { + while (parent && mRemoteObjectForParent.key(parent)) { QString name = parent->name(); if (!name.isEmpty() && !name.startsWith(QLatin1String("qt_"))) { reversedPath.append(name); @@ -222,6 +285,10 @@ QString RemoteObjectNameRegister::reversedPathAsName( const QStringList& reversedPath) const { + if (reversedPath.isEmpty()) { + return ""; + } + QString name = reversedPath[0]; for (int i=1; i<reversedPath.size(); ++i) { name = reversedPath[i] + '/' + name; @@ -230,56 +297,6 @@ return name; } -RemoteObject* RemoteObjectNameRegister::findRemoteObject(const QString& name, - RemoteObject* ancestor) const { - if (name.indexOf('/') == -1) { - QList<RemoteObject*> remoteObjects = mRemoteObjectForName.values(name); - foreach (RemoteObject* remoteObject, remoteObjects) { - if (isDescendantOf(remoteObject, ancestor)) { - return remoteObject; - } - } - - return 0; - } - - QRegExp slashPattern("/+"); - QString ancestorName = name.left(name.indexOf(slashPattern)); - QString descendantName = name.mid(ancestorName.length() + - slashPattern.matchedLength()); - - QList<RemoteObject*> namedAncestors = - mRemoteObjectForName.values(ancestorName); - - foreach (RemoteObject* namedAncestor, namedAncestors) { - if (isDescendantOf(namedAncestor, ancestor)) { - RemoteObject* remoteObject = findRemoteObject(descendantName, - namedAncestor); - if (remoteObject) { - return remoteObject; - } - } - } - - return 0; -} - -bool RemoteObjectNameRegister::isDescendantOf(RemoteObject* remoteObject, - RemoteObject* ancestor) const { - if (!ancestor) { - return true; - } - - QList<RemoteObject*> children = mRemoteObjectForParent.values(ancestor); - foreach (RemoteObject* child, children) { - if (child == remoteObject || isDescendantOf(remoteObject, child)) { - return true; - } - } - - return false; -} - void RemoteObjectNameRegister::startNameUpdate() { mIsBeingUpdated = true; emit nameUpdateStarted(); Modified: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.h 2012-08-09 15:59:47 UTC (rev 359) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.h 2012-08-10 10:57:33 UTC (rev 360) @@ -41,9 +41,31 @@ * object can be got using uniqueName(RemoteObject*). Also, given a name, the * remote object that it represents can be got using findObject(QString). An * unqualified name (without any ancestor names, just the name of the remote - * object itself) can be used to get the unique names of all the remote objects + * object itself) can be used to get the best names of all the remote objects * represented by that unqualified name. * + * The best name and the unique name of a remote object are tightly related. + * When the name of a remote object is unique by itself, the best name of the + * remote object is the name itself. + * When there are other remote objects with the same name, but the remote object + * has an ancestor with a unique name, and the remote object is the only + * descendant of that ancestor with that name, its best name (which is also + * unique) is the name of the ancestor and the name of the remote object. The + * ancestor with a unique name used is the nearest one to the remote object + * (parent preferred to grandparent, grandparent preferred to great-grandparent + * and so on). + * If there are more descendants of the ancestor with that name, or if there + * is no ancestor with a unique name, the best name is composed by the name of + * the remote object itself and the name of as many ancestors as needed to + * obtain a unique name. + * If there is no unique name for the remote object, the best name is composed + * by the name of the remote object itself and the name of all of its ancestors + * from the remote object to the deepest ancestor with a unique name, or all its + * ancestors if none has a unique name. + * A best name that is ambiguous, that is, identifies more than one remote + * object, may become unique if the ambiguity solving rules from KTutorial are + * taken into account. + * * Note that a name and the remote object it represents depends on the state of * the target application. For example, if there is a dialog named * "Configuration dialog" with a button named "Ok button", its unique name could @@ -97,51 +119,40 @@ /** * Returns a unique name for the given remote object. - * When the remote object name is unique by itself, its name is returned. - * When there are other remote objects with the same name as the given one, - * but it has an ancestor with a unique name, the returned name includes - * both names. The ancestor with a unique name used is the nearest one to - * the remote object (parent preferred to grandparent, grandparent preferred - * to great-grandparent and so on). - * When there are other remote objects with the same name as the given one, - * and no ancestor has a unique name, the returned name includes as many - * ancestor names as necessary to create a name unique to the given remote - * object. - * + * If the best name of the given remote object is unique, or if it is not + * unique, but the object found when applying the ambiguity solving rules is + * the given remote object, the best name is returned. + * Otherwise, there is no unique name for the given remote object, and an + * empty string is returned instead. + * * @param remoteObject The remote object to get its unique name. - * @return A unique name for the given remote object. + * @return A unique name for the given remote object, if any. * @throws DBusException If a DBus error happens. */ QString uniqueName(RemoteObject* remoteObject) const throw (DBusException); /** - * Returns the unique names of the remote objects that have the given name. + * Returns the best names of the remote objects that have the given name. * The given name can not contain any ancestor names, and it must be a * registered name. * - * @param name The name of the remote objects to get their unique names. - * @return The unique names of the remote objects that have the given name. + * @param name The name of the remote objects to get their best names. + * @return The best names of the remote objects that have the given name. * @throws DBusException If a DBus error happens. */ - QStringList uniqueNames(const QString& name) const throw (DBusException); + QStringList bestNames(const QString& name) const throw (DBusException); /** * Returns the remote object with the given name, if any. - * When the name of the desired remote object is not unique the name of some - * ancestor must be included. Ancestor names are separated using a "/". That - * is, "Ancestor name/The remote object with a repeated name". As many - * ancestor names as desired can be included, not just one. - * The name of the ancestors does not need to be unique either; what has to - * be unique is the full path to the remote object to find. For example, - * "Ancestor name not unique/The remote object to find" is valid if, among - * all the remote objects called "Ancestor name not unique", there is only - * one that has a descendant called "The remote object to find". + * The editor support module in the target application is queried to find + * the object with the given name. * * @param name The name of the remote object to find. * @return The remote object with the specified name, or a null pointer if * there is none. */ - RemoteObject* findRemoteObject(const QString& name) const; + RemoteObject* findRemoteObject(const QString& name) const + throw (DBusException); /** * Returns whether the names are being registered or not. @@ -229,12 +240,32 @@ throw (DBusException); /** + * Returns the best name for the given remote object. + * + * @param remoteObject The remote object to get its best name. + * @return The best name of the given remote object. + * @throws DBusException If a DBus error happens. + */ + QString bestName(RemoteObject* remoteObject) const + throw (DBusException); + + /** + * Returns the best name for the given remote object as a reversed path. + * + * @param remoteObject The remote object to get its best name. + * @return The reversed path for the best name of the given remote object. + * @throws DBusException If a DBus error happens. + */ + QStringList bestNameAsReversedPath(RemoteObject* remoteObject) const + throw (DBusException); + + /** * Returns the reversed path to the given remote object. * The path contains the name of the remote object itself and all its named - * ancestors. The order of the items is reversed from the natural path, that - * is, the first item in the returned path is the remote object name, the - * second item is the name of its parent, the third is the name of its - * grandparent... + * ancestors (except for the root ancestor). The order of the items is + * reversed from the natural path, that is, the first item in the returned + * path is the remote object name, the second item is the name of its + * parent, the third is the name of its grandparent... * Empty names and Qt default names (like "qt_scrollarea_viewport") are not * included in the path. * @@ -311,6 +342,7 @@ * The name contains all the items in the path separated by "/". The order * of the items in the name is their natural order, that is, the last item * is the first one, the penultimate is the second one... + * If the given path is empty an empty string is returned. * * @param path The reversed path to get its equivalent name. * @return The name equivalent to the given reversed path. @@ -318,31 +350,6 @@ QString reversedPathAsName(const QStringList& reversedPath) const; /** - * Returns the remote object with the given name, if any. - * The name can contain also the ancestor names, separated by "/". The - * first object that matches the whole path is returned (note that ancestor - * are not necessarily direct parents). - * - * @param name The name of the remote object to find. - * @param ancestor The ancestor to look the remote object in. - * @return The remote object with the specified name, or a null pointer if - * there is none. - */ - RemoteObject* findRemoteObject(const QString& name, RemoteObject* ancestor) - const; - - /** - * Returns true if remoteObject is descendant of ancestor, false otherwise. - * When ancestor is null, it always returns true. - * - * @param remoteObject The remote object to check. - * @param ancestor The ancestor to check. - * @return True if remoteObject is descendant of ancestor, false otherwise. - */ - bool isDescendantOf(RemoteObject* remoteObject, - RemoteObject* ancestor) const; - - /** * Sets this RemoteObjectNameRegister as being updated and emits the * nameUpdateStarted() signal. */ Modified: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.cpp 2012-08-09 15:59:47 UTC (rev 359) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.cpp 2012-08-10 10:57:33 UTC (rev 360) @@ -32,7 +32,10 @@ * The items added for completion should be the unqualified remote object names, * that is, just the name of the remote objects, without any ancestor. When a * match is found, and it represents more than one remote object, the match is - * replaced by the unique names of its remote objects. + * replaced by the best names of its remote objects. Note that an object without + * a unique name will have the same best name as another object. Showing two or + * more objects with the same best name in the completion list will hopefully + * help the user to identify problems with the names in the target application. */ class RemoteObjectNameCompletion: public KCompletion { public: @@ -64,30 +67,30 @@ /** * Processes the matches to provide unique names. * When a match is a name that represents several remote objects that match - * is replaced by all the unique names of the remote objects it represents. + * is replaced by the best names of the remote objects it represents. * * @param matches The matches to process. */ virtual void postProcessMatches(QStringList* matches) const { QMutableListIterator<QString> it(*matches); while (it.hasNext()) { - QStringList uniqueNames; + QStringList bestNames; try { - uniqueNames = mNameRegister->uniqueNames(it.next()); + bestNames = mNameRegister->bestNames(it.next()); } catch (DBusException e) { kWarning() << "The unique names for the remote objects named " << it.value() << "could not be added to the " << "completion matches (" << e.message() << ")"; } - if (uniqueNames.count() > 1) { + if (bestNames.count() > 1) { it.remove(); - qSort(uniqueNames); + qSort(bestNames); - foreach (const QString& uniqueName, uniqueNames) { - it.insert(uniqueName); + foreach (const QString& bestName, bestNames) { + it.insert(bestName); } } } @@ -193,16 +196,29 @@ return; } + QString uniqueName; try { - ui->objectNameLineEdit->setText( - mRemoteObjectNameRegister->uniqueName(remoteObject)); + uniqueName = mRemoteObjectNameRegister->uniqueName(remoteObject); } catch (DBusException e) { QString text = i18nc("@label", "The object name can not be set, there " "was a problem getting the name from the target application: %1", e.message()); QString title = i18nc("@title", "Can't communicate with the target " "application"); KMessageBox::sorry(this, text, title); + return; } + + if (uniqueName.isEmpty()) { + QString text = i18nc("@label", "There is no unique name for the chosen " +"object (at least, in the current state of the target application), so it is " +"not possible to refer to that object in a tutorial. The target application " +"must be modified to provide a unique name for that object."); + QString title = i18nc("@title", "The chosen object has no unique name"); + KMessageBox::sorry(this, text, title); + return; + } + + ui->objectNameLineEdit->setText(uniqueName); } void RemoteObjectNameWidget::handleNameChanged(const QString& name) { @@ -211,7 +227,16 @@ return; } - emit remoteObjectChosen(mRemoteObjectNameRegister->findRemoteObject(name)); + RemoteObject* remoteObject; + try { + remoteObject = mRemoteObjectNameRegister->findRemoteObject(name); + } catch (DBusException e) { + kWarning() << "There was a problem finding the remote object (" + << e.message() << ")."; + return; + } + + emit remoteObjectChosen(remoteObject); } void RemoteObjectNameWidget::handleCompletion() { Modified: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.h 2012-08-09 15:59:47 UTC (rev 359) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.h 2012-08-10 10:57:33 UTC (rev 360) @@ -39,7 +39,7 @@ * in the target application. If there is no target application running, no * completion is provided. As soon as it starts, the completion is enabled. * When the completion matches contain a name that represents several remote - * objects that match is replaced by the unique name of each remote object. + * objects that match is replaced by the best name of each remote object. * * When a name is set in the line edit the signal * remoteObjectChosen(RemoteObject*) is emitted. @@ -159,14 +159,17 @@ * If the name register is being updated, nothing will be done, but marking * setting the chosen remote object as a pending operation. * If the given remote object is null no name is set. + * If the given remote object has no unique name, no name is set and a + * message box is shown to inform the user that the target application must + * be modified in order to be able to use that object in a tutorial. * * @param remoteObject The chosen RemoteObject. */ void setChosenRemoteObject(RemoteObject* remoteObject); /** - * Emits remoteObjectChosen(RemoteObject*) with the remote object with the - * given name. + * Emits remoteObjectChosen(RemoteObject*) with the remote object found with + * the given name (which can include ancestor names). * If the name register is being updated, nothing will be done, but marking * handling the name change as a pending operation. * Modified: trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteClassStubs.h =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteClassStubs.h 2012-08-09 15:59:47 UTC (rev 359) +++ trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteClassStubs.h 2012-08-10 10:57:33 UTC (rev 360) @@ -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 * @@ -101,21 +101,36 @@ * | | |... * | |... * |-8: "The object name 8" - * |-80: "" (The class name 80) - * | |-800: "Duplicated object" - * | |-801: "The object name 801" - * | |-802: "" - * | |-803: "The object name 803" - * |-81: "" (QWidgetChildChild) - * | |-810: "" - * | |-811: "" - * | |-812: "" - * | |-813: "" - * |-82: "The object name 82" (ChildChildQWidget) - * | |-820: "The object name 820" + * | |-80: "" (The class name 80) + * | | |-800: "Duplicated object" + * | | |-801: "The object name 801" + * | | |-802: "" + * | | |-803: "The object name 803" + * | |-81: "" (QWidgetChildChild) + * | | |-810: "" + * | | |-811: "" + * | | |-812: "" + * | | |-813: "" + * | |-82: "The object name 82" (ChildChildQWidget) + * | | |-820: "The object name 820" + * | | |... + * | |-83: "The object name 83" (The class name 83) + * | |-830: "The object name 830" (ChildQWidget) + * | |... + * |-9: "Duplicated grandparent" + * |-90: "The object name 90" + * | |-900: "Duplicated object" + * | |-901: "Duplicated object" * | |... - * |-83: "The object name 83" (The class name 83) - * |-830: "The object name 830" (ChildQWidget) + * |-91: "Another duplicated parent" + * | |-910: "Duplicated object" + * | |... + * |-92: "Another duplicated parent" + * | |-920: "Duplicated object" + * | |... + * |-93: "" + * |-930: "Duplicated object" + * |-931: "Duplicated object" * |... */ class StubObjectRegisterAdaptor: public QDBusAbstractAdaptor { @@ -130,7 +145,9 @@ QString objectName(int objectId) { if (objectId == 500 || objectId == 600 || objectId == 700 || - objectId == 800) { + objectId == 800 || objectId == 900 || objectId == 901 || + objectId == 910 || objectId == 920 || objectId == 930 || + objectId == 931) { return "Duplicated object"; } @@ -138,13 +155,17 @@ return "Duplicated parent"; } - if (objectId == 5 || objectId == 6) { + if (objectId == 91 || objectId == 92) { + return "Another duplicated parent"; + } + + if (objectId == 5 || objectId == 6 || objectId == 9) { return "Duplicated grandparent"; } if (objectId == 80 || objectId == 81 || objectId == 802 || objectId == 810 || objectId == 811 || objectId == 812 || - objectId == 813) { + objectId == 813 || objectId == 93) { return ""; } @@ -186,6 +207,7 @@ ids.append(6); ids.append(7); ids.append(8); + ids.append(9); } return ids; @@ -230,6 +252,51 @@ return 42; } + int findObject(const QString& name) { + //Used in RemoteEditorSupportTest + if (name == "The object name 423") { + return 423; + } + + //Used in WaitForPropertyWidgetTest + if (name == "The object name 830") { + return 830; + } + + //Used in RemoteObjectNameRegisterTest/RemoteObjectNameWidgetTest + if (name == "The object name 50/Duplicated object") { + return 500; + } + + if (name == "The object name 501") { + return 501; + } + + if (name == "Duplicated grandparent/Duplicated parent/" + "Duplicated object") { + return 600; + } + + if (name == "The object name 7/Duplicated object") { + return 700; + } + + if (name == "The object name 90/Duplicated object") { + return 900; + } + + if (name == "Duplicated grandparent/Another duplicated parent/" + "Duplicated object") { + return 910; + } + + if (name == "Duplicated grandparent/Duplicated object") { + return 930; + } + + return 0; + } + void highlight(int objectId) { mHighlightRemoteWidgetIds.append(objectId); } Modified: trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteEditorSupportTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteEditorSupportTest.cpp 2012-08-09 15:59:47 UTC (rev 359) +++ trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteEditorSupportTest.cpp 2012-08-10 10:57:33 UTC (rev 360) @@ -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 * @@ -50,6 +50,9 @@ void testMainWindowTwice(); void testMainWindowWhenRemoteEditorSupportIsNotAvailable(); + void testFindObject(); + void testFindObjectWhenRemoteEditorSupportIsNotAvailable(); + void testHighlight(); void testHighlightWhenRemoteEditorSupportIsNotAvailable(); @@ -132,6 +135,28 @@ EXPECT_EXCEPTION(remoteEditorSupport.mainWindow(), DBusException); } +void RemoteEditorSupportTest::testFindObject() { + RemoteObjectMapper mapper(QDBusConnection::sessionBus().baseService()); + RemoteEditorSupport remoteEditorSupport( + QDBusConnection::sessionBus().baseService(), &mapper); + + RemoteObject* object = remoteEditorSupport.mainWindow()->children()[3]; + + QCOMPARE(remoteEditorSupport.findObject("The object name 423"), object); +} + +void RemoteEditorSupportTest:: +testFindObjectWhenRemoteEditorSupportIsNotAvailable() { + RemoteObjectMapper mapper(QDBusConnection::sessionBus().baseService()); + RemoteEditorSupport remoteEditorSupport( + QDBusConnection::sessionBus().baseService(), &mapper); + + QDBusConnection::sessionBus().unregisterObject("/ktutorial"); + + EXPECT_EXCEPTION(remoteEditorSupport.findObject("The object name 423"), + DBusException); +} + void RemoteEditorSupportTest::testHighlight() { RemoteObjectMapper mapper(QDBusConnection::sessionBus().baseService()); RemoteEditorSupport remoteEditorSupport( Modified: trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteObjectTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteObjectTest.cpp 2012-08-09 15:59:47 UTC (rev 359) +++ trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteObjectTest.cpp 2012-08-10 10:57:33 UTC (rev 360) @@ -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 * @@ -122,7 +122,7 @@ RemoteObject remoteObject(mService, mMapper, 42); QList<RemoteObject*> children = remoteObject.children(); - QCOMPARE(children.count(), 8); + QCOMPARE(children.count(), 9); QCOMPARE(children[0]->objectId(), 420); QCOMPARE(children[1]->objectId(), 421); QCOMPARE(children[2]->objectId(), 422); @@ -131,6 +131,7 @@ QCOMPARE(children[5]->objectId(), 6); QCOMPARE(children[6]->objectId(), 7); QCOMPARE(children[7]->objectId(), 8); + QCOMPARE(children[8]->objectId(), 9); } void RemoteObjectTest::testChildrenWhenRemoteObjectIsNotAvailable() { Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameRegisterTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameRegisterTest.cpp 2012-08-09 15:59:47 UTC (rev 359) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameRegisterTest.cpp 2012-08-10 10:57:33 UTC (rev 360) @@ -50,6 +50,9 @@ void testFindRemoteObjectWithParentName(); void testFindRemoteObjectWithGrandparentName(); void testFindRemoteObjectWithParentAndGrandparentNames(); + void testFindRemoteObjectWithAmbiguousNameAndUniqueParent(); + void testFindRemoteObjectWithAmbiguousNameAndDuplicatedParent(); + void testFindRemoteObjectWithAmbiguousNameAndEmptyParent(); void testFindRemoteObjectWithUnknownName(); void testUniqueName(); @@ -57,10 +60,14 @@ void testUniqueNameWhenUniqueGrandparentAndEmptyParent(); void testUniqueNameWhenUniqueGrandparentAndDuplicatedParent(); void testUniqueNameWhenUniqueUnionOfGrandparentAndParent(); + void testUniqueNameWhenAmbiguousNameWithUniqueParent(); + void testUniqueNameWhenAmbiguousNameWithDuplicatedParent(); + void testUniqueNameWhenAmbiguousNameWithEmptyParent(); void testUniqueNameWhenEmptyName(); + void testUniqueNameWhenNameNotUnique(); - void testUniqueNames(); - void testUniqueNamesWithSingleRemoteObject(); + void testBestNames(); + void testBestNamesWithSingleRemoteObject(); private: @@ -133,7 +140,7 @@ QCOMPARE(nameUpdateFinishedSpy.count(), 1); assertNames(remoteObjectNameRegister.names()); - QCOMPARE(nameAddedSpy.count(), 82); + QCOMPARE(nameAddedSpy.count(), 102); QCOMPARE(nameAddedSpy.at(0).at(0).toString(), QString("The object name 42")); QCOMPARE(nameAddedSpy.at(1).at(0).toString(), @@ -305,6 +312,63 @@ mainWindow->children()[5]->children()[0]->children()[0]); } +void RemoteObjectNameRegisterTest:: + testFindRemoteObjectWithAmbiguousNameAndUniqueParent() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + QVERIFY(waitForTargetApplicationToStart(10000)); + + RemoteObjectNameRegister remoteObjectNameRegister; + + QVERIFY(waitForNamesToBeRegistered(remoteObjectNameRegister, 10000)); + + RemoteObject* mainWindow = + TargetApplication::self()->remoteEditorSupport()->mainWindow(); + + QCOMPARE(remoteObjectNameRegister.findRemoteObject( + "The object name 90/Duplicated object"), + mainWindow->children()[8]->children()[0]->children()[0]); +} + +void RemoteObjectNameRegisterTest:: + testFindRemoteObjectWithAmbiguousNameAndDuplicatedParent() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + QVERIFY(waitForTargetApplicationToStart(10000)); + + RemoteObjectNameRegister remoteObjectNameRegister; + + QVERIFY(waitForNamesToBeRegistered(remoteObjectNameRegister, 10000)); + + RemoteObject* mainWindow = + TargetApplication::self()->remoteEditorSupport()->mainWindow(); + + QCOMPARE(remoteObjectNameRegister.findRemoteObject( + "Duplicated grandparent/Another duplicated parent/Duplicated object"), + mainWindow->children()[8]->children()[1]->children()[0]); +} + +void RemoteObjectNameRegisterTest:: + testFindRemoteObjectWithAmbiguousNameAndEmptyParent() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + QVERIFY(waitForTargetApplicationToStart(10000)); + + RemoteObjectNameRegister remoteObjectNameRegister; + + QVERIFY(waitForNamesToBeRegistered(remoteObjectNameRegister, 10000)); + + RemoteObject* mainWindow = + TargetApplication::self()->remoteEditorSupport()->mainWindow(); + + QCOMPARE(remoteObjectNameRegister.findRemoteObject( + "Duplicated grandparent/Duplicated object"), + mainWindow->children()[8]->children()[3]->children()[0]); +} + void RemoteObjectNameRegisterTest::testFindRemoteObjectWithUnknownName() { TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); @@ -418,6 +482,67 @@ "Duplicated object")); } +void RemoteObjectNameRegisterTest:: + testUniqueNameWhenAmbiguousNameWithUniqueParent() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + QVERIFY(waitForTargetApplicationToStart(10000)); + + RemoteObjectNameRegister remoteObjectNameRegister; + + QVERIFY(waitForNamesToBeRegistered(remoteObjectNameRegister, 10000)); + + RemoteObject* mainWindow = + TargetApplication::self()->remoteEditorSupport()->mainWindow(); + RemoteObject* remoteObject = + mainWindow->children()[8]->children()[0]->children()[0]; + + QCOMPARE(remoteObjectNameRegister.uniqueName(remoteObject), + QString("The object name 90/Duplicated object")); +} + +void RemoteObjectNameRegisterTest:: + testUniqueNameWhenAmbiguousNameWithDuplicatedParent() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + QVERIFY(waitForTargetApplicationToStart(10000)); + + RemoteObjectNameRegister remoteObjectNameRegister; + + QVERIFY(waitForNamesToBeRegistered(remoteObjectNameRegister, 10000)); + + RemoteObject* mainWindow = + TargetApplication::self()->remoteEditorSupport()->mainWindow(); + RemoteObject* remoteObject = + mainWindow->children()[8]->children()[1]->children()[0]; + + QCOMPARE(remoteObjectNameRegister.uniqueName(remoteObject), + QString("Duplicated grandparent/Another duplicated parent/" + "Duplicated object")); +} + +void RemoteObjectNameRegisterTest:: + testUniqueNameWhenAmbiguousNameWithEmptyParent() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + QVERIFY(waitForTargetApplicationToStart(10000)); + + RemoteObjectNameRegister remoteObjectNameRegister; + + QVERIFY(waitForNamesToBeRegistered(remoteObjectNameRegister, 10000)); + + RemoteObject* mainWindow = + TargetApplication::self()->remoteEditorSupport()->mainWindow(); + RemoteObject* remoteObject = + mainWindow->children()[8]->children()[3]->children()[0]; + + QCOMPARE(remoteObjectNameRegister.uniqueName(remoteObject), + QString("Duplicated grandparent/Duplicated object")); +} + void RemoteObjectNameRegisterTest::testUniqueNameWhenEmptyName() { TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); @@ -435,7 +560,7 @@ QCOMPARE(remoteObjectNameRegister.uniqueName(remoteObject), QString("")); } -void RemoteObjectNameRegisterTest::testUniqueNames() { +void RemoteObjectNameRegisterTest::testUniqueNameWhenNameNotUnique() { TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); @@ -445,18 +570,52 @@ QVERIFY(waitForNamesToBeRegistered(remoteObjectNameRegister, 10000)); - QStringList uniqueNames = - remoteObjectNameRegister.uniqueNames("Duplicated object"); + RemoteObject* mainWindow = + TargetApplication::self()->remoteEditorSupport()->mainWindow(); + RemoteObject* remoteObject = + mainWindow->children()[8]->children()[0]->children()[1]; - QCOMPARE(uniqueNames.count(), 4); - QVERIFY(uniqueNames.contains("The object name 50/Duplicated object")); - QVERIFY(uniqueNames.contains("Duplicated grandparent/Duplicated parent/" + QCOMPARE(remoteObjectNameRegister.uniqueName(remoteObject), QString("")); + + remoteObject = mainWindow->children()[8]->children()[2]->children()[0]; + + QCOMPARE(remoteObjectNameRegister.uniqueName(remoteObject), QString("")); + + remoteObject = mainWindow->children()[8]->children()[3]->children()[1]; + + QCOMPARE(remoteObjectNameRegister.uniqueName(remoteObject), QString("")); +} + +void RemoteObjectNameRegisterTest::testBestNames() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + QVERIFY(waitForTargetApplicationToStart(10000)); + + RemoteObjectNameRegister remoteObjectNameRegister; + + QVERIFY(waitForNamesToBeRegistered(remoteObjectNameRegister, 10000)); + + QStringList bestNames = + remoteObjectNameRegister.bestNames("Duplicated object"); + + QCOMPARE(bestNames.count(), 10); + QVERIFY(bestNames.contains("The object name 50/Duplicated object")); + QVERIFY(bestNames.contains("Duplicated grandparent/Duplicated parent/" "Duplicated object")); - QVERIFY(uniqueNames.contains("The object name 7/Duplicated object")); - QVERIFY(uniqueNames.contains("The object name 8/Duplicated object")); + QVERIFY(bestNames.contains("The object name 7/Duplicated object")); + QVERIFY(bestNames.contains("The object name 8/Duplicated object")); + QVERIFY(bestNames.contains("The object name 90/Duplicated object")); + QVERIFY(bestNames.contains("The object name 90/Duplicated object")); + QVERIFY(bestNames.contains("Duplicated grandparent/Another duplicated " + "parent/Duplicated object")); + QVERIFY(bestNames.contains("Duplicated grandparent/Another duplicated " + "parent/Duplicated object")); + QVERIFY(bestNames.contains("Duplicated grandparent/Duplicated object")); + QVERIFY(bestNames.contains("Duplicated grandparent/Duplicated object")); } -void RemoteObjectNameRegisterTest::testUniqueNamesWithSingleRemoteObject() { +void RemoteObjectNameRegisterTest::testBestNamesWithSingleRemoteObject() { TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); @@ -466,11 +625,11 @@ QVERIFY(waitForNamesToBeRegistered(remoteObjectNameRegister, 10000)); - QStringList uniqueNames = - remoteObjectNameRegister.uniqueNames("The object name 423"); + QStringList bestNames = + remoteObjectNameRegister.bestNames("The object name 423"); - QCOMPARE(uniqueNames.count(), 1); - QVERIFY(uniqueNames.contains("The object name 423")); + QCOMPARE(bestNames.count(), 1); + QVERIFY(bestNames.contains("The object name 423")); } /////////////////////////////////// Helpers //////////////////////////////////// @@ -541,7 +700,7 @@ } void RemoteObjectNameRegisterTest::assertNames(const QStringList& names) const { - QCOMPARE(names.count(), 82); + QCOMPARE(names.count(), 102); QVERIFY(names.contains("The object name 42")); QVERIFY(names.contains("The object name 420")); QVERIFY(names.contains("The object name 421")); Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp 2012-08-09 15:59:47 UTC (rev 359) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp 2012-08-10 10:57:33 UTC (rev 360) @@ -29,6 +29,7 @@ #include <QtDBus/QtDBus> #include <KCompletionBox> +#include <KDialog> #include <KLineEdit> #include <KProcess> @@ -59,6 +60,7 @@ void testSetNameDuringNameRegisterUpdate(); void testSetChosenRemoteObject(); + void testSetChosenRemoteObjectWithUniqueComplexName(); void testSetChosenRemoteObjectWithNameNotUnique(); void testSetChosenRemoteObjectDuringNameRegisterUpdate(); void testSetChosenRemoteObjectDuringNameRegisterUpdateKillingApplication(); @@ -94,6 +96,8 @@ bool waitForNamesToBeRegistered(const RemoteObjectNameWidget* widget, int timeout) const; + void closeSorryMessageBox(QWidget* widget, int timeToWait); + void assertRemoteObjectSignal(const QSignalSpy& spy, int index, const RemoteObject* remoteObject) const; @@ -261,7 +265,8 @@ assertRemoteObjectSignal(remoteObjectChosenSpy, 0, remoteObject); } -void RemoteObjectNameWidgetTest::testSetChosenRemoteObjectWithNameNotUnique() { +void RemoteObjectNameWidgetTest:: + testSetChosenRemoteObjectWithUniqueComplexName() { TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); @@ -300,6 +305,38 @@ assertRemoteObjectSignal(remoteObjectChosenSpy, 2, remoteObject); } +void RemoteObjectNameWidgetTest::testSetChosenRemoteObjectWithNameNotUnique() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + QVERIFY(waitForTargetApplicationToStart(10000)); + + RemoteObjectNameWidget widget; + + QVERIFY(waitForNamesToBeRegistered(&widget, 10000)); + + widget.setName("An object name"); + + QSignalSpy remoteObjectChosenSpy(&widget, + SIGNAL(remoteObjectChosen(RemoteObject*))); + + RemoteObject* mainWindow = + TargetApplication::self()->remoteEditorSupport()->mainWindow(); + RemoteObject* remoteObject = + mainWindow->children()[8]->children()[0]->children()[1]; + + //The widget must be shown for a proper parenting of the sorry message box + widget.show(); + + //Queue closing the sorry message box + closeSorryMessageBox(&widget, 10000); + + widget.setChosenRemoteObject(remoteObject); + + QCOMPARE(widget.name(), QString("An object name")); + QCOMPARE(remoteObjectChosenSpy.count(), 0); +} + void RemoteObjectNameWidgetTest:: testSetChosenRemoteObjectDuringNameRegisterUpdate() { TargetApplication::self()->setTargetApplicationFilePath(mPath); @@ -429,16 +466,30 @@ QVERIFY(completionBox->isVisible()); - QCOMPARE(completionItems.count(), 4); + QCOMPARE(completionItems.count(), 10); QCOMPARE(completionItems[0], + QString("Duplicated grandparent/Another duplicated parent/" + "Duplicated object")); + QCOMPARE(completionItems[1], + QString("Duplicated grandparent/Another duplicated parent/" + "Duplicated object")); + QCOMPARE(completionItems[2], + QString("Duplicated grandparent/Duplicated object")); + QCOMPARE(completionItems[3], + QString("Duplicated grandparent/Duplicated object")); + QCOMPARE(completionItems[4], QString("Duplicated grandparent/Duplicated parent/" "Duplicated object")); - QCOMPARE(completionItems[1], + QCOMPARE(completionItems[5], QString("The object name 50/Duplicated object")); - QCOMPARE(completionItems[2], + QCOMPARE(completionItems[6], QString("The object name 7/Duplicated object")); - QCOMPARE(completionItems[3], + QCOMPARE(completionItems[7], QString("The object name 8/Duplicated object")); + QCOMPARE(completionItems[8], + QString("The object name 90/Duplicated object")); + QCOMPARE(completionItems[9], + QString("The object name 90/Duplicated object")); } void RemoteObjectNameWidgetTest::testNameCompletionDuringNameRegisterUpdate() { @@ -470,16 +521,30 @@ QVERIFY(completionBox->isVisible()); - QCOMPARE(completionItems.count(), 4); + QCOMPARE(completionItems.count(), 10); QCOMPARE(completionItems[0], + QString("Duplicated grandparent/Another duplicated parent/" + "Duplicated object")); + QCOMPARE(completionItems[1], + QString("Duplicated grandparent/Another duplicated parent/" + "Duplicated object")); + QCOMPARE(completionItems[2], + QString("Duplicated grandparent/Duplicated object")); + QCOMPARE(completionItems[3], + QString("Duplicated grandparent/Duplicated object")); + QCOMPARE(completionItems[4], QString("Duplicated grandparent/Duplicated parent/" "Duplicated object")); - QCOMPARE(completionItems[1], + QCOMPARE(completionItems[5], QString("The object name 50/Duplicated object")); - QCOMPARE(completionItems[2], + QCOMPARE(completionItems[6], QString("The object name 7/Duplicated object")); - QCOMPARE(completionItems[3], + QCOMPARE(completionItems[7], QString("The object name 8/Duplicated object")); + QCOMPARE(completionItems[8], + QString("The object name 90/Duplicated object")); + QCOMPARE(completionItems[9], + QString("The object name 90/Duplicated object")); } void RemoteObjectNameWidgetTest:: @@ -515,7 +580,7 @@ QVERIFY(completionBox->isVisible()); - QCOMPARE(completionItems.count(), 82); + QCOMPARE(completionItems.count(), 102); QVERIFY(completionItems.contains("The object name 42")); QVERIFY(completionItems.contains("The object name 420")); QVERIFY(completionItems.contains("The object name 421")); @@ -723,6 +788,101 @@ timeout); } +//The message boxes are modal, so they won't return to the test code until they +//are closed. Thus, the actions to be performed while the message boxes are +//being shown (like closing the message boxes themselves) must be "queued". +//Instead of queueing them to be performed after some fixed amount of time, +//it is checked if the action can be performed and, if not, the action is +//retried again after a little amount of time. The retries continue until the +//timeout expires. +class QueuedAction: public QObject { +Q_OBJECT +public: + + QueuedAction(int timeout, QObject* parent): QObject(parent), + mTimeout(timeout), + mFinished(false), + mActionDone(false) { + } + + void run() { + mElapsedTime.start(); + mFinished = false; + + checkAction(); + } + + bool isFinished() const { + return mFinished; + } + + bool isActionDone() const { + return mActionDone; + } + +public slots: + + void checkAction() { + mActionDone = action(); + if (!mActionDone && mElapsedTime.elapsed() < mTimeout) { + QTimer::singleShot(100, this, SLOT(checkAction())); + } else { + mFinished = true; + } + } + +protected: + + virtual bool action() = 0; + +private: + + int mTimeout; + QTime mElapsedTime; + bool mFinished; + bool mActionDone; + +}; + +class CloseSorryMessageBoxHelper: public QueuedAction { +public: + + CloseSorryMessageBoxHelper(int timeout, QObject* parent): + QueuedAction(timeout, parent), + mWidget(0) { + } + + void setWidget(QWidget* widget) { + mWidget = widget; + } + +protected: + + virtual bool action() { + KDialog* messageBox = mWidget->findChild<KDialog*>("sorry"); + if (!messageBox) { + return false; + } + + delete messageBox; + + return true; + } + +private: + + QWidget* mWidget; + +}; + +void RemoteObjectNameWidgetTest::closeSorryMessageBox(QWidget* widget, + int timeToWait) { + CloseSorryMessageBoxHelper* helper = new CloseSorryMessageBoxHelper( + timeToWait, this); + helper->setWidget(widget); + helper->run(); +} + //RemoteObject* must be declared as a metatype to be used in qvariant_cast Q_DECLARE_METATYPE(RemoteObject*); @@ -735,7 +895,7 @@ void RemoteObjectNameWidgetTest::assertCompletionItems( const QStringList& items) const { - QCOMPARE(items.count(), 77); + QCOMPARE(items.count(), 89); QVERIFY(items.contains("The object name 42")); QVERIFY(items.contains("The object name 420")); QVERIFY(items.contains("The object name 421")); Modified: trunk/ktutorial/ktutorial-library/src/KTutorial.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/KTutorial.cpp 2012-08-09 15:59:47 UTC (rev 359) +++ trunk/ktutorial/ktutorial-library/src/KTutorial.cpp 2012-08-10 10:57:33 UTC (rev 360) @@ -86,6 +86,7 @@ #ifdef QT_QTDBUS_FOUND editorsupport::EditorSupport* editorSupport = new editorsupport::EditorSupport(this); + editorSupport->setObjectFinder(mObjectFinder); editorSupport->setup(window); connect(editorSupport, SIGNAL(started(Tutorial*)), this, SLOT(showStepWidget(Tutorial*))); Modified: trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.cpp 2012-08-09 15:59:47 UTC (rev 359) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.cpp 2012-08-10 10:57:33 UTC (rev 360) @@ -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 * @@ -28,6 +28,7 @@ #include "EventSpyAdaptor.h" #include "ObjectRegister.h" #include "ObjectRegisterAdaptor.h" +#include "../ObjectFinder.h" #include ".... [truncated message content] |
From: <dan...@us...> - 2012-08-09 15:59:53
|
Revision: 359 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=359&view=rev Author: danxuliu Date: 2012-08-09 15:59:47 +0000 (Thu, 09 Aug 2012) Log Message: ----------- Fix parameter order in QCOMPARE. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/tests/editorsupport/EditorSupportTest.cpp Modified: trunk/ktutorial/ktutorial-library/tests/editorsupport/EditorSupportTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/editorsupport/EditorSupportTest.cpp 2012-08-09 15:52:01 UTC (rev 358) +++ trunk/ktutorial/ktutorial-library/tests/editorsupport/EditorSupportTest.cpp 2012-08-09 15:59:47 UTC (rev 359) @@ -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 * @@ -142,8 +142,8 @@ int mainWindowObjectId = editorSupport.mainWindowObjectId(); - QCOMPARE(editorSupport.mObjectRegister->idForObject(&window), - mainWindowObjectId); + QCOMPARE(mainWindowObjectId, + editorSupport.mObjectRegister->idForObject(&window)); } void EditorSupportTest::testHighlight() { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2012-08-09 15:52:12
|
Revision: 358 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=358&view=rev Author: danxuliu Date: 2012-08-09 15:52:01 +0000 (Thu, 09 Aug 2012) Log Message: ----------- Add support for null objects in the editor support module. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.cpp trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.h trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteObjectMapperTest.cpp trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegister.cpp trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegister.h trunk/ktutorial/ktutorial-library/tests/editorsupport/ObjectRegisterTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.cpp 2012-08-09 09:40:28 UTC (rev 357) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.cpp 2012-08-09 15:52:01 UTC (rev 358) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 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 * @@ -35,6 +35,10 @@ } RemoteObject* RemoteObjectMapper::remoteObject(int objectId) { + if (objectId == 0) { + return 0; + } + if (mRemoteObjects.contains(objectId)) { return mRemoteObjects.value(objectId); } Modified: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.h 2012-08-09 09:40:28 UTC (rev 357) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.h 2012-08-09 15:52:01 UTC (rev 358) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 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 * @@ -54,6 +54,8 @@ * Returns the RemoteObject associated with the given object id. * The RemoteObject is destroyed when this RemoteObjectMapper is cleared or * destroyed, so consider using QPointer to store it. + * The object id 0 is a special case, and it is associated to the null + * object; a null pointer is returned in this case. * * @param objectId The id of the remote object. * @return The RemoteObject. Modified: trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteObjectMapperTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteObjectMapperTest.cpp 2012-08-09 09:40:28 UTC (rev 357) +++ trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteObjectMapperTest.cpp 2012-08-09 15:52:01 UTC (rev 358) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 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 * @@ -37,6 +37,7 @@ void testRemoteObject(); void testRemoteObjectSeveralIds(); void testRemoteObjectTwice(); + void testRemoteNullObject(); void testRemoteClass(); void testRemoteClassSeveralIds(); @@ -101,6 +102,14 @@ QCOMPARE(remoteObject1->name(), QString("The object name 42")); } +void RemoteObjectMapperTest::testRemoteNullObject() { + RemoteObjectMapper mapper(QDBusConnection::sessionBus().baseService()); + + RemoteObject* remoteObject = mapper.remoteObject(0); + + QVERIFY(!remoteObject); +} + void RemoteObjectMapperTest::testRemoteClass() { RemoteObjectMapper mapper(QDBusConnection::sessionBus().baseService()); Modified: trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegister.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegister.cpp 2012-08-09 09:40:28 UTC (rev 357) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegister.cpp 2012-08-09 15:52:01 UTC (rev 358) @@ -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 * @@ -27,6 +27,10 @@ } int ObjectRegister::idForObject(QObject* object) { + if (!object) { + return 0; + } + int id = mRegisteredIds.value(object); if (!id) { Modified: trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegister.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegister.h 2012-08-09 09:40:28 UTC (rev 357) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegister.h 2012-08-09 15:52:01 UTC (rev 358) @@ -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 * @@ -29,7 +29,7 @@ * The id for an object is assigned after the first time the id is requested * for that object, and are valid until the register is cleared or the object is * destroyed (if an object is destroyed it is automatically removed from the - * register). + * register). The null object is a special case, and its id is always 0. * * When an object is registered, its QMetaObject and the QMetaObject of all its * super classes are also registered. The QMetaObject can be got using the class Modified: trunk/ktutorial/ktutorial-library/tests/editorsupport/ObjectRegisterTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/editorsupport/ObjectRegisterTest.cpp 2012-08-09 09:40:28 UTC (rev 357) +++ trunk/ktutorial/ktutorial-library/tests/editorsupport/ObjectRegisterTest.cpp 2012-08-09 15:52:01 UTC (rev 358) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 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 * @@ -44,6 +44,7 @@ void testRegisterObject(); void testRegisterObjectSeveralObjects(); void testRegisterObjectTwice(); + void testRegisterNullObject(); void testObjectForIdWithDestroyedObject(); @@ -112,6 +113,14 @@ &QObject::staticMetaObject); } +void ObjectRegisterTest::testRegisterNullObject() { + ObjectRegister objectRegister; + + int id = objectRegister.idForObject(0); + + QCOMPARE(objectRegister.objectForId(id), (QObject*)0); +} + void ObjectRegisterTest::testObjectForIdWithDestroyedObject() { ObjectRegister objectRegister; QObject* object = new QObject(); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2012-08-09 09:40:37
|
Revision: 357 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=357&view=rev Author: danxuliu Date: 2012-08-09 09:40:28 +0000 (Thu, 09 Aug 2012) Log Message: ----------- Add missing default null parent to the constructor. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/ObjectFinder.h Modified: trunk/ktutorial/ktutorial-library/src/ObjectFinder.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/ObjectFinder.h 2012-08-04 15:54:59 UTC (rev 356) +++ trunk/ktutorial/ktutorial-library/src/ObjectFinder.h 2012-08-09 09:40:28 UTC (rev 357) @@ -38,7 +38,7 @@ * * @param parent The parent object. */ - ObjectFinder(QObject* parent): QObject(parent) { + ObjectFinder(QObject* parent = 0): QObject(parent) { } /** This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2012-08-04 15:55:07
|
Revision: 356 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=356&view=rev Author: danxuliu Date: 2012-08-04 15:54:59 +0000 (Sat, 04 Aug 2012) Log Message: ----------- Fix bug where the RemoteObjectNameRegister/Widget "missed" the name of some remote objects. Before, the name of the remote objects was registered when the object was added to its parent in the target application. This could lead to the object being registered before its name was set, so it was registered with an empty name. Now, when the object is added to its parent in the target application it is queued for its name to be registered, and after a little delay (500ms), the register of the name takes place. During that delay the register should not be queried. If during the name update in the register something is done in the RemoteObjectNameWidget which has to query the register, the operation is also queued and executed once the name update has finished. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.cpp trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.h trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.cpp trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.h 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 Modified: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.cpp 2012-07-11 14:29:47 UTC (rev 355) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.cpp 2012-08-04 15:54:59 UTC (rev 356) @@ -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 * @@ -28,7 +28,8 @@ //public: RemoteObjectNameRegister::RemoteObjectNameRegister(QObject* parent /*= 0*/): - QObject(parent) { + QObject(parent), + mIsBeingUpdated(false) { if (TargetApplication::self()->remoteEditorSupport()) { registerRemoteObjects(); } @@ -107,6 +108,10 @@ return findRemoteObject(name, 0); } +bool RemoteObjectNameRegister::isBeingUpdated() const { + return mIsBeingUpdated; +} + //private: void RemoteObjectNameRegister::registerRemoteObject(RemoteObject* remoteObject, @@ -116,13 +121,10 @@ mRemoteObjectForParent.insert(parent, remoteObject); - QString name = remoteObject->name(); - if (!name.isEmpty()) { - emit nameAdded(name); + mRemoteObjectsPendingNameRegister.append( + QPointer<RemoteObject>(remoteObject)); + QTimer::singleShot(500, this, SLOT(deferredRegisterRemoteObjectName())); - mRemoteObjectForName.insert(name, remoteObject); - } - foreach (RemoteObject* child, remoteObject->children()) { registerRemoteObject(child, remoteObject); } @@ -278,9 +280,21 @@ return false; } +void RemoteObjectNameRegister::startNameUpdate() { + mIsBeingUpdated = true; + emit nameUpdateStarted(); +} + +void RemoteObjectNameRegister::finishNameUpdate() { + mIsBeingUpdated = false; + emit nameUpdateFinished(); +} + //private slots: void RemoteObjectNameRegister::registerRemoteObjects() { + startNameUpdate(); + try { registerRemoteObject(TargetApplication::self()-> remoteEditorSupport()->mainWindow(), 0); @@ -288,7 +302,11 @@ kWarning() << "The remote objects could not be registered to provide " << "name completion (" << e.message() << ")."; } - + + if (mRemoteObjectsPendingNameRegister.isEmpty()) { + finishNameUpdate(); + } + try { RemoteEventSpy* remoteEventSpy = TargetApplication::self()->remoteEditorSupport()->enableEventSpy(); @@ -311,6 +329,10 @@ return; } + if (eventType == "ChildAdded") { + startNameUpdate(); + } + try { QList<RemoteObject*> children = remoteObject->children(); @@ -337,4 +359,39 @@ << "name completion could not be updated (" << e.message() << ")."; } + + if (eventType == "ChildAdded" && + mRemoteObjectsPendingNameRegister.isEmpty()) { + finishNameUpdate(); + } } + +void RemoteObjectNameRegister::deferredRegisterRemoteObjectName() { + QPointer<RemoteObject> remoteObject = + mRemoteObjectsPendingNameRegister.takeFirst(); + if (!remoteObject) { + if (mRemoteObjectsPendingNameRegister.isEmpty()) { + finishNameUpdate(); + } + + return; + } + + QString name; + try { + name = remoteObject->name(); + } catch (DBusException e) { + kWarning() << "There was a problem getting the name of the remote" + << "object (" << e.message() << ")."; + } + + if (!name.isEmpty()) { + emit nameAdded(name); + + mRemoteObjectForName.insert(name, remoteObject); + } + + if (mRemoteObjectsPendingNameRegister.isEmpty()) { + finishNameUpdate(); + } +} Modified: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.h 2012-07-11 14:29:47 UTC (rev 355) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.h 2012-08-04 15:54:59 UTC (rev 356) @@ -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 * @@ -21,6 +21,7 @@ #include <QMultiHash> #include <QObject> +#include <QPointer> #include "../targetapplication/DBusException.h" @@ -51,6 +52,17 @@ * "File information dialog" had a also button called "Ok button", now the * unique name of the first button would be "Configuration dialog/Ok button". In * this situation, "Ok button" would not represent a specific remote object. + * + * Also note that names are not registered when the objects are created in the + * target application, but a slight delay is introduced instead. This prevents + * registering objects too soon, before their name was even set in the target + * application. While the update in the registered names is taking place the + * methods described above should not be used. When a name update is started, + * the signal nameUpdateStarted() is emitted; when the update is finished, the + * signal nameUpdateFinished() is emitted. It can be checked if the register is + * being updated or not using isBeingUpdated(). The "updating" state is only + * enabled and the signals emitted when new objects are created; if an object is + * deleted there is no delay in the update of the names. */ class RemoteObjectNameRegister: public QObject { Q_OBJECT @@ -58,6 +70,12 @@ /** * Creates a new RemoteObjectNameRegister. + * Note that if the target application is already started when the + * RemoteObjectNameRegister is created the RemoteObjects will be registered + * and the nameUpdateStarted() signal will be emitted, even before being + * able to connect to it. Thus, you may want to check whether the register + * is being updated or not after the constructor to call the slot that will + * be connected with nameUpdateStarted() signal. * * @param parent The parent QObject. */ @@ -125,6 +143,13 @@ */ RemoteObject* findRemoteObject(const QString& name) const; + /** + * Returns whether the names are being registered or not. + * + * @return True if the names are being registered, false otherwise. + */ + bool isBeingUpdated() const; + Q_SIGNALS: /** @@ -141,6 +166,21 @@ */ void nameRemoved(const QString& name); + /** + * Emitted when one or more remote objects are created in the target + * application, and thus queued for their names to be registered. + * Note that this signal can be emitted several times before + * nameUpdateFinished() signal is emitted. + */ + void nameUpdateStarted(); + + /** + * Emitted when there are no more remote objects pending to be registered. + * Note that this signal may be emitted just once after several + * nameUpdateStarted() signals are emitted. + */ + void nameUpdateFinished(); + private: /** @@ -154,8 +194,21 @@ QMultiHash<RemoteObject*, RemoteObject*> mRemoteObjectForParent; /** + * Whether the names are being registered or not. + */ + bool mIsBeingUpdated; + + /** + * The list of RemoteObjects queued to register their name. + * A guarded pointer is needed, as the RemoteObjects may be deleted if the + * target application is closed before finishing the update of the names. + */ + QList< QPointer<RemoteObject> > mRemoteObjectsPendingNameRegister; + + /** * Registers the given RemoteObject and all its children. - * The objects are only registered if they have a name. + * The name itself is not registered yet, but queued to be registered after + * a little time. * * @param remoteObject The RemoteObject to register. * @param parent The parent of the RemoteObject to register. @@ -289,10 +342,24 @@ bool isDescendantOf(RemoteObject* remoteObject, RemoteObject* ancestor) const; + /** + * Sets this RemoteObjectNameRegister as being updated and emits the + * nameUpdateStarted() signal. + */ + void startNameUpdate(); + + /** + * Sets this RemoteObjectNameRegister as not being updated and emits the + * nameUpdateFinished() signal. + */ + void finishNameUpdate(); + private Q_SLOTS: /** * Registers all the remote objects. + * The name update is started (and finished if no names are queued to be + * registered). */ void registerRemoteObjects(); @@ -304,6 +371,8 @@ /** * Updates the registered remote objects if they received a ChildAdded or * ChildRemoved event. + * If the ChildAdded event is received, the name update is started (and + * finished if no names are queued to be registered). * * @param remoteObject The RemoteObject that received the event. * @param eventType The type of the event received. @@ -311,6 +380,14 @@ void updateRemoteObjects(RemoteObject* remoteObject, const QString& eventType); + /** + * Registers, if it is still available, the name of the remote object + * pending since the longest time ago. + * If there are no more names pending to be registered, the name update is + * finished. + */ + void deferredRegisterRemoteObjectName(); + }; #endif Modified: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.cpp 2012-07-11 14:29:47 UTC (rev 355) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.cpp 2012-08-04 15:54:59 UTC (rev 356) @@ -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 * @@ -24,6 +24,7 @@ #include "RemoteObjectChooser.h" #include "RemoteObjectNameRegister.h" +#include "../targetapplication/RemoteObject.h" #include "../targetapplication/TargetApplication.h" /** @@ -104,10 +105,19 @@ //public: RemoteObjectNameWidget::RemoteObjectNameWidget(QWidget* parent): - QWidget(parent) { + QWidget(parent), + mIsRemoteObjectChosenPending(false), + mIsNameChangedPending(false), + mIsCompletionPending(false), + mIsSubstringCompletionPending(false) { mRemoteObjectNameRegister = new RemoteObjectNameRegister(this); + connect(mRemoteObjectNameRegister, SIGNAL(nameUpdateStarted()), + this, SLOT(startNameUpdate())); + connect(mRemoteObjectNameRegister, SIGNAL(nameUpdateFinished()), + this, SLOT(finishNameUpdate())); + ui = new Ui::RemoteObjectNameWidget(); ui->setupUi(this); @@ -120,6 +130,9 @@ connect(ui->objectNamePushButton, SIGNAL(clicked(bool)), this, SLOT(showRemoteObjectChooser())); + //It will probably be IBeamCursor, but just in case + mObjectNameLineEditCursorShape = ui->objectNameLineEdit->cursor().shape(); + KCompletion* completion = new RemoteObjectNameCompletion(mRemoteObjectNameRegister); completion->setOrder(KCompletion::Sorted); @@ -127,9 +140,10 @@ ui->objectNameLineEdit->setCompletionObject(completion); ui->objectNameLineEdit->setAutoDeleteCompletionObject(true); - foreach (const QString& name, mRemoteObjectNameRegister->names()) { - completion->addItem(name); - } + connect(ui->objectNameLineEdit, SIGNAL(completion(QString)), + this, SLOT(handleCompletion())); + connect(ui->objectNameLineEdit, SIGNAL(substringCompletion(QString)), + this, SLOT(handleSubstringCompletion())); connect(mRemoteObjectNameRegister, SIGNAL(nameAdded(QString)), completion, SLOT(addItem(QString))); @@ -137,6 +151,13 @@ completion, SLOT(removeItem(QString))); connect(TargetApplication::self(), SIGNAL(finished()), completion, SLOT(clear())); + + //If the target application already exists when the register is + //created the nameUpdateStarted is emitted before being able to connect to + //it. + if (mRemoteObjectNameRegister->isBeingUpdated()) { + startNameUpdate(); + } } RemoteObjectNameWidget::~RemoteObjectNameWidget() { @@ -162,6 +183,16 @@ } void RemoteObjectNameWidget::setChosenRemoteObject(RemoteObject* remoteObject) { + if (mRemoteObjectNameRegister->isBeingUpdated()) { + mIsRemoteObjectChosenPending = true; + mPendingRemoteObjectChosen = QPointer<RemoteObject>(remoteObject); + return; + } + + if (!remoteObject) { + return; + } + try { ui->objectNameLineEdit->setText( mRemoteObjectNameRegister->uniqueName(remoteObject)); @@ -175,5 +206,62 @@ } void RemoteObjectNameWidget::handleNameChanged(const QString& name) { + if (mRemoteObjectNameRegister->isBeingUpdated()) { + mIsNameChangedPending = true; + return; + } + emit remoteObjectChosen(mRemoteObjectNameRegister->findRemoteObject(name)); } + +void RemoteObjectNameWidget::handleCompletion() { + if (!mRemoteObjectNameRegister->isBeingUpdated()) { + return; + } + + mIsCompletionPending = true; +} + +void RemoteObjectNameWidget::handleSubstringCompletion() { + if (!mRemoteObjectNameRegister->isBeingUpdated()) { + return; + } + + mIsSubstringCompletionPending = true; +} + +//This method may be called several times before finishNameUpdate() is called, +//so it should not be assumed that the widget will be in a "not updating" state +void RemoteObjectNameWidget::startNameUpdate() { + ui->objectNameLineEdit->setCursor(Qt::BusyCursor); + + ui->objectNameLineEdit->setHandleSignals(false); +} + +void RemoteObjectNameWidget::finishNameUpdate() { + if (mIsRemoteObjectChosenPending) { + mIsRemoteObjectChosenPending = false; + setChosenRemoteObject(mPendingRemoteObjectChosen); + } + + if (mIsNameChangedPending) { + mIsNameChangedPending = false; + handleNameChanged(ui->objectNameLineEdit->text()); + } + + ui->objectNameLineEdit->setHandleSignals(true); + + if (mIsSubstringCompletionPending) { + mIsSubstringCompletionPending = false; + ui->objectNameLineEdit->setCompletedItems( + ui->objectNameLineEdit->completionObject()->substringCompletion( + ui->objectNameLineEdit->text())); + } + + if (mIsCompletionPending) { + mIsCompletionPending = false; + ui->objectNameLineEdit->doCompletion(ui->objectNameLineEdit->text()); + } + + ui->objectNameLineEdit->setCursor(mObjectNameLineEditCursorShape); +} Modified: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.h 2012-07-11 14:29:47 UTC (rev 355) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.h 2012-08-04 15:54:59 UTC (rev 356) @@ -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 * @@ -19,6 +19,7 @@ #ifndef REMOTEOBJECTNAMEWIDGET_H #define REMOTEOBJECTNAMEWIDGET_H +#include <QPointer> #include <QWidget> class RemoteObject; @@ -42,6 +43,16 @@ * * When a name is set in the line edit the signal * remoteObjectChosen(RemoteObject*) is emitted. + * + * To provide name completion, name substring completion, setting the name based + * on the RemoteObject chosen or emitting the signal + * remoteObjectChosen(RemoteObject*) based on the current name, the widget uses + * a register with the names of the remote objects. During the updates of this + * register it should not be queried (as the information provided will not be + * accurate), so if any of the aforementioned operations is requested during a + * register update, the operation is queued and executed once the update + * finishes.The cursor shape is changed to busy during register updates to + * reflect this fact. */ class RemoteObjectNameWidget: public QWidget { Q_OBJECT @@ -99,6 +110,43 @@ */ Ui::RemoteObjectNameWidget* ui; + /** + * Whether the remote object was chosen while the register was being updated + * or not. + */ + bool mIsRemoteObjectChosenPending; + + /** + * The last remote object chosen while the register was being updated, if + * any. + * A guarded pointer is needed, as the RemoteObject may be deleted if the + * target application is closed before finishing the update of the names. + */ + QPointer<RemoteObject> mPendingRemoteObjectChosen; + + /** + * Whether the name was set (programatically or by the user) while the + * register was being updated or not. + */ + bool mIsNameChangedPending; + + /** + * Whether the name completion was triggered while the register was being + * updated or not. + */ + bool mIsCompletionPending; + + /** + * Whether the name substring completion was triggered while the register + * was being updated or not. + */ + bool mIsSubstringCompletionPending; + + /** + * The cursor shape used by default by the object name line edit. + */ + Qt::CursorShape mObjectNameLineEditCursorShape; + private Q_SLOTS: /** @@ -108,6 +156,9 @@ /** * Sets the object name based in the chosen remote object. + * If the name register is being updated, nothing will be done, but marking + * setting the chosen remote object as a pending operation. + * If the given remote object is null no name is set. * * @param remoteObject The chosen RemoteObject. */ @@ -116,11 +167,38 @@ /** * Emits remoteObjectChosen(RemoteObject*) with the remote object with the * given name. + * If the name register is being updated, nothing will be done, but marking + * handling the name change as a pending operation. * * @param name The name set. */ void handleNameChanged(const QString& name); + /** + * If the name register is being updated, marks the name completion as a + * pending operation. + */ + void handleCompletion(); + + /** + * If the name register is being updated, marks the name substring + * completion as a pending operation. + */ + void handleSubstringCompletion(); + + /** + * Enters the name register update state. + * Changes the cursor to busy and disables the completion. + */ + void startNameUpdate(); + + /** + * Exists the name register update state. + * Executes all the pending operations, enables again the completion and + * restores the cursor. + */ + void finishNameUpdate(); + }; #endif Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameRegisterTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameRegisterTest.cpp 2012-07-11 14:29:47 UTC (rev 355) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameRegisterTest.cpp 2012-08-04 15:54:59 UTC (rev 356) @@ -42,6 +42,7 @@ void testTargetApplicationStartedAfterRegister(); void testTargetApplicationStopped(); + void testTargetApplicationStoppedBeforeFinishingNameUpdate(); void testAddingOrRemovingRemoteObjects(); @@ -68,6 +69,10 @@ bool waitForTargetApplicationToStart(int timeout) const; bool waitForTargetApplicationToStop(int timeout) const; + bool waitForNamesToBeRegistered( + const RemoteObjectNameRegister& remoteObjectNameRegister, + int timeout) const; + void assertNames(const QStringList& names) const; }; @@ -95,6 +100,9 @@ RemoteObjectNameRegister remoteObjectNameRegister; + QVERIFY(remoteObjectNameRegister.isBeingUpdated()); + QVERIFY(waitForNamesToBeRegistered(remoteObjectNameRegister, 10000)); + assertNames(remoteObjectNameRegister.names()); } @@ -104,12 +112,26 @@ SIGNAL(nameAdded(QString))); QSignalSpy nameRemovedSpy(&remoteObjectNameRegister, SIGNAL(nameRemoved(QString))); + QSignalSpy nameUpdateStartedSpy(&remoteObjectNameRegister, + SIGNAL(nameUpdateStarted())); + QSignalSpy nameUpdateFinishedSpy(&remoteObjectNameRegister, + SIGNAL(nameUpdateFinished())); TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); QVERIFY(waitForTargetApplicationToStart(10000)); + QVERIFY(remoteObjectNameRegister.isBeingUpdated()); + + QCOMPARE(nameUpdateStartedSpy.count(), 1); + QCOMPARE(nameUpdateFinishedSpy.count(), 0); + + QVERIFY(waitForNamesToBeRegistered(remoteObjectNameRegister, 10000)); + + QCOMPARE(nameUpdateStartedSpy.count(), 1); + QCOMPARE(nameUpdateFinishedSpy.count(), 1); + assertNames(remoteObjectNameRegister.names()); QCOMPARE(nameAddedSpy.count(), 82); QCOMPARE(nameAddedSpy.at(0).at(0).toString(), @@ -139,6 +161,8 @@ QCOMPARE(nameAddedSpy.at(71).at(0).toString(), QString("The object name 803")); QCOMPARE(nameRemovedSpy.count(), 0); + QCOMPARE(nameUpdateStartedSpy.count(), 1); + QCOMPARE(nameUpdateFinishedSpy.count(), 1); } void RemoteObjectNameRegisterTest::testTargetApplicationStopped() { @@ -149,10 +173,16 @@ RemoteObjectNameRegister remoteObjectNameRegister; + QVERIFY(waitForNamesToBeRegistered(remoteObjectNameRegister, 10000)); + QSignalSpy nameAddedSpy(&remoteObjectNameRegister, SIGNAL(nameAdded(QString))); QSignalSpy nameRemovedSpy(&remoteObjectNameRegister, SIGNAL(nameAdded(QString))); + QSignalSpy nameUpdateStartedSpy(&remoteObjectNameRegister, + SIGNAL(nameUpdateStarted())); + QSignalSpy nameUpdateFinishedSpy(&remoteObjectNameRegister, + SIGNAL(nameUpdateFinished())); TargetApplication::self()->mProcess->kill(); @@ -161,8 +191,42 @@ QVERIFY(remoteObjectNameRegister.names().isEmpty()); QCOMPARE(nameAddedSpy.count(), 0); QCOMPARE(nameRemovedSpy.count(), 0); + QCOMPARE(nameUpdateStartedSpy.count(), 0); + QCOMPARE(nameUpdateFinishedSpy.count(), 0); } +void RemoteObjectNameRegisterTest:: + testTargetApplicationStoppedBeforeFinishingNameUpdate() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + QVERIFY(waitForTargetApplicationToStart(10000)); + + RemoteObjectNameRegister remoteObjectNameRegister; + + QSignalSpy nameUpdateFinishedSpy(&remoteObjectNameRegister, + SIGNAL(nameUpdateFinished())); + + TargetApplication::self()->mProcess->kill(); + + //Process deleteLater events for the RemoteObjects + QApplication::processEvents(QEventLoop::DeferredDeletion); + + //Ensure that there are still names pending to be updated + QVERIFY(remoteObjectNameRegister.isBeingUpdated()); + + QVERIFY(waitForTargetApplicationToStop(10000)); + + //If the pending RemoteObjects are not stored in the + //RemoteObjectNameRegister using a guarded pointer, getting the names to + //register them after the target application was killed would lead to a + //crash + QVERIFY(waitForNamesToBeRegistered(remoteObjectNameRegister, 10000)); + + QVERIFY(remoteObjectNameRegister.names().isEmpty()); + QCOMPARE(nameUpdateFinishedSpy.count(), 1); +} + void RemoteObjectNameRegisterTest::testAddingOrRemovingRemoteObjects() { QSKIP("Unfortunately, testing if the signals are emitted when an object is " "added or removed in the target application is too burdensome so the " @@ -177,6 +241,8 @@ RemoteObjectNameRegister remoteObjectNameRegister; + QVERIFY(waitForNamesToBeRegistered(remoteObjectNameRegister, 10000)); + RemoteObject* mainWindow = TargetApplication::self()->remoteEditorSupport()->mainWindow(); @@ -192,6 +258,8 @@ RemoteObjectNameRegister remoteObjectNameRegister; + QVERIFY(waitForNamesToBeRegistered(remoteObjectNameRegister, 10000)); + RemoteObject* mainWindow = TargetApplication::self()->remoteEditorSupport()->mainWindow(); @@ -208,6 +276,8 @@ RemoteObjectNameRegister remoteObjectNameRegister; + QVERIFY(waitForNamesToBeRegistered(remoteObjectNameRegister, 10000)); + RemoteObject* mainWindow = TargetApplication::self()->remoteEditorSupport()->mainWindow(); @@ -225,6 +295,8 @@ RemoteObjectNameRegister remoteObjectNameRegister; + QVERIFY(waitForNamesToBeRegistered(remoteObjectNameRegister, 10000)); + RemoteObject* mainWindow = TargetApplication::self()->remoteEditorSupport()->mainWindow(); @@ -241,6 +313,8 @@ RemoteObjectNameRegister remoteObjectNameRegister; + QVERIFY(waitForNamesToBeRegistered(remoteObjectNameRegister, 10000)); + QCOMPARE(remoteObjectNameRegister.findRemoteObject("The object name 108"), (RemoteObject*)0); } @@ -253,6 +327,8 @@ RemoteObjectNameRegister remoteObjectNameRegister; + QVERIFY(waitForNamesToBeRegistered(remoteObjectNameRegister, 10000)); + RemoteObject* mainWindow = TargetApplication::self()->remoteEditorSupport()->mainWindow(); RemoteObject* remoteObject = @@ -270,6 +346,8 @@ RemoteObjectNameRegister remoteObjectNameRegister; + QVERIFY(waitForNamesToBeRegistered(remoteObjectNameRegister, 10000)); + RemoteObject* mainWindow = TargetApplication::self()->remoteEditorSupport()->mainWindow(); RemoteObject* remoteObject = @@ -288,6 +366,8 @@ RemoteObjectNameRegister remoteObjectNameRegister; + QVERIFY(waitForNamesToBeRegistered(remoteObjectNameRegister, 10000)); + RemoteObject* mainWindow = TargetApplication::self()->remoteEditorSupport()->mainWindow(); RemoteObject* remoteObject = @@ -306,6 +386,8 @@ RemoteObjectNameRegister remoteObjectNameRegister; + QVERIFY(waitForNamesToBeRegistered(remoteObjectNameRegister, 10000)); + RemoteObject* mainWindow = TargetApplication::self()->remoteEditorSupport()->mainWindow(); RemoteObject* remoteObject = @@ -324,6 +406,8 @@ RemoteObjectNameRegister remoteObjectNameRegister; + QVERIFY(waitForNamesToBeRegistered(remoteObjectNameRegister, 10000)); + RemoteObject* mainWindow = TargetApplication::self()->remoteEditorSupport()->mainWindow(); RemoteObject* remoteObject = @@ -342,6 +426,8 @@ RemoteObjectNameRegister remoteObjectNameRegister; + QVERIFY(waitForNamesToBeRegistered(remoteObjectNameRegister, 10000)); + RemoteObject* mainWindow = TargetApplication::self()->remoteEditorSupport()->mainWindow(); RemoteObject* remoteObject = mainWindow->children()[7]->children()[0]; @@ -357,6 +443,8 @@ RemoteObjectNameRegister remoteObjectNameRegister; + QVERIFY(waitForNamesToBeRegistered(remoteObjectNameRegister, 10000)); + QStringList uniqueNames = remoteObjectNameRegister.uniqueNames("Duplicated object"); @@ -376,6 +464,8 @@ RemoteObjectNameRegister remoteObjectNameRegister; + QVERIFY(waitForNamesToBeRegistered(remoteObjectNameRegister, 10000)); + QStringList uniqueNames = remoteObjectNameRegister.uniqueNames("The object name 423"); @@ -417,6 +507,39 @@ return waitFor(&isTargetApplicationStopped, timeout); } +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 RegisterNotBeingUpdatedCondition { +public: + const RemoteObjectNameRegister* mRemoteObjectNameRegister; + + bool condition() const { + return !mRemoteObjectNameRegister->isBeingUpdated(); + } +}; + +bool RemoteObjectNameRegisterTest::waitForNamesToBeRegistered( + const RemoteObjectNameRegister& remoteObjectNameRegister, + int timeout) const { + RegisterNotBeingUpdatedCondition helper; + helper.mRemoteObjectNameRegister = &remoteObjectNameRegister; + return waitFor(helper, &RegisterNotBeingUpdatedCondition::condition, + 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-07-11 14:29:47 UTC (rev 355) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp 2012-08-04 15:54:59 UTC (rev 356) @@ -32,6 +32,7 @@ #include <KLineEdit> #include <KProcess> +#include "RemoteObjectNameRegister.h" #include "../targetapplication/RemoteClassStubs.h" #include "../targetapplication/RemoteEditorSupport.h" #include "../targetapplication/RemoteObject.h" @@ -55,12 +56,17 @@ void testSetName(); void testSetNameWithPath(); void testSetNameWithUnknownRemoteObjectName(); + void testSetNameDuringNameRegisterUpdate(); void testSetChosenRemoteObject(); void testSetChosenRemoteObjectWithNameNotUnique(); + void testSetChosenRemoteObjectDuringNameRegisterUpdate(); + void testSetChosenRemoteObjectDuringNameRegisterUpdateKillingApplication(); void testNameCompletion(); void testDuplicatedNameCompletion(); + void testNameCompletionDuringNameRegisterUpdate(); + void testNameSubcompletionDuringNameRegisterUpdate(); void testTargetApplicationStartedAfterWidget(); void testTargetApplicationStopped(); @@ -84,6 +90,9 @@ bool waitForTargetApplicationToStart(int timeout) const; bool waitForTargetApplicationToStop(int timeout) const; + + bool waitForNamesToBeRegistered(const RemoteObjectNameWidget* widget, + int timeout) const; void assertRemoteObjectSignal(const QSignalSpy& spy, int index, const RemoteObject* remoteObject) const; @@ -121,6 +130,12 @@ QWidget parent; RemoteObjectNameWidget* widget = new RemoteObjectNameWidget(&parent); + QCOMPARE(objectNameLineEdit(widget)->cursor().shape(), Qt::BusyCursor); + + QVERIFY(waitForNamesToBeRegistered(widget, 10000)); + + QCOMPARE(objectNameLineEdit(widget)->cursor().shape(), Qt::IBeamCursor); + QCOMPARE(widget->parentWidget(), &parent); QCOMPARE(widget->name(), QString("")); QStringList items = objectNameLineEdit(widget)->completionObject()->items(); @@ -137,6 +152,8 @@ QSignalSpy remoteObjectChosenSpy(&widget, SIGNAL(remoteObjectChosen(RemoteObject*))); + QVERIFY(waitForNamesToBeRegistered(&widget, 10000)); + widget.setName("The object name 423"); RemoteObject* mainWindow = @@ -158,6 +175,8 @@ QSignalSpy remoteObjectChosenSpy(&widget, SIGNAL(remoteObjectChosen(RemoteObject*))); + QVERIFY(waitForNamesToBeRegistered(&widget, 10000)); + widget.setName("The object name 7/Duplicated object"); RemoteObject* mainWindow = @@ -179,6 +198,8 @@ QSignalSpy remoteObjectChosenSpy(&widget, SIGNAL(remoteObjectChosen(RemoteObject*))); + QVERIFY(waitForNamesToBeRegistered(&widget, 10000)); + widget.setName("The object name 108"); QCOMPARE(widget.name(), QString("The object name 108")); @@ -186,6 +207,36 @@ assertRemoteObjectSignal(remoteObjectChosenSpy, 0, 0); } +void RemoteObjectNameWidgetTest::testSetNameDuringNameRegisterUpdate() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + QVERIFY(waitForTargetApplicationToStart(10000)); + + RemoteObjectNameWidget widget; + QSignalSpy remoteObjectChosenSpy(&widget, + SIGNAL(remoteObjectChosen(RemoteObject*))); + + widget.setName("The object name 423"); + + //Ensure that the register is still being updated + QVERIFY(widget.mRemoteObjectNameRegister->isBeingUpdated()); + + //Check that the name is set, but the signal was not emitted yet + QCOMPARE(widget.name(), QString("The object name 423")); + QCOMPARE(remoteObjectChosenSpy.count(), 0); + + QVERIFY(waitForNamesToBeRegistered(&widget, 10000)); + + RemoteObject* mainWindow = + TargetApplication::self()->remoteEditorSupport()->mainWindow(); + + QCOMPARE(widget.name(), QString("The object name 423")); + QCOMPARE(remoteObjectChosenSpy.count(), 1); + assertRemoteObjectSignal(remoteObjectChosenSpy, 0, + mainWindow->children()[3]); +} + void RemoteObjectNameWidgetTest::testSetChosenRemoteObject() { TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); @@ -196,6 +247,8 @@ QSignalSpy remoteObjectChosenSpy(&widget, SIGNAL(remoteObjectChosen(RemoteObject*))); + QVERIFY(waitForNamesToBeRegistered(&widget, 10000)); + RemoteObject* mainWindow = TargetApplication::self()->remoteEditorSupport()->mainWindow(); RemoteObject* remoteObject = @@ -218,6 +271,8 @@ QSignalSpy remoteObjectChosenSpy(&widget, SIGNAL(remoteObjectChosen(RemoteObject*))); + QVERIFY(waitForNamesToBeRegistered(&widget, 10000)); + RemoteObject* mainWindow = TargetApplication::self()->remoteEditorSupport()->mainWindow(); @@ -245,6 +300,85 @@ assertRemoteObjectSignal(remoteObjectChosenSpy, 2, remoteObject); } +void RemoteObjectNameWidgetTest:: + testSetChosenRemoteObjectDuringNameRegisterUpdate() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + QVERIFY(waitForTargetApplicationToStart(10000)); + + RemoteObjectNameWidget widget; + QSignalSpy remoteObjectChosenSpy(&widget, + SIGNAL(remoteObjectChosen(RemoteObject*))); + + RemoteObject* mainWindow = + TargetApplication::self()->remoteEditorSupport()->mainWindow(); + RemoteObject* remoteObject = + mainWindow->children()[4]->children()[0]->children()[1]; + + widget.setChosenRemoteObject(remoteObject); + + //Ensure that the register is still being updated + QVERIFY(widget.mRemoteObjectNameRegister->isBeingUpdated()); + + //Check that the name is not set and the signal was not emitted yet + QCOMPARE(widget.name(), QString("")); + QCOMPARE(remoteObjectChosenSpy.count(), 0); + + QVERIFY(waitForNamesToBeRegistered(&widget, 10000)); + + QCOMPARE(widget.name(), QString("The object name 501")); + QCOMPARE(remoteObjectChosenSpy.count(), 1); + assertRemoteObjectSignal(remoteObjectChosenSpy, 0, remoteObject); +} + +void RemoteObjectNameWidgetTest:: + testSetChosenRemoteObjectDuringNameRegisterUpdateKillingApplication() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + QVERIFY(waitForTargetApplicationToStart(10000)); + + RemoteObjectNameWidget widget; + widget.setName("An object name"); + + QSignalSpy remoteObjectChosenSpy(&widget, + SIGNAL(remoteObjectChosen(RemoteObject*))); + + RemoteObject* mainWindow = + TargetApplication::self()->remoteEditorSupport()->mainWindow(); + RemoteObject* remoteObject = + mainWindow->children()[4]->children()[0]->children()[1]; + + widget.setChosenRemoteObject(remoteObject); + + //Ensure that the register is still being updated + QVERIFY(widget.mRemoteObjectNameRegister->isBeingUpdated()); + + TargetApplication::self()->mProcess->kill(); + + QVERIFY(waitForTargetApplicationToStop(10000)); + + //Ensure that the register is still being updated + QVERIFY(widget.mRemoteObjectNameRegister->isBeingUpdated()); + + //Check that the name is not set and the signal was not emitted yet + QCOMPARE(widget.name(), QString("An object name")); + QCOMPARE(remoteObjectChosenSpy.count(), 0); + + //If the pending RemoteObject is not stored using a guarded pointer, getting + //its unique name after the target application was killed would lead to a + //crash + QVERIFY(waitForNamesToBeRegistered(&widget, 10000)); + + //If there is no RemoteObject to be set, nothing is done. However, the + //remoteObjectChosen signal was queued to be emitted when the name was first + //set (as the names were already being uptated), although it should not be + //emitted again when setting the null RemoteObject. + QCOMPARE(widget.name(), QString("An object name")); + QCOMPARE(remoteObjectChosenSpy.count(), 1); +} + void RemoteObjectNameWidgetTest::testNameCompletion() { TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); @@ -253,6 +387,8 @@ RemoteObjectNameWidget widget; + QVERIFY(waitForNamesToBeRegistered(&widget, 10000)); + KCompletion* completion = objectNameLineEdit(&widget)->completionObject(); QCOMPARE(completion->order(), KCompletion::Sorted); @@ -280,6 +416,9 @@ QVERIFY(waitForTargetApplicationToStart(10000)); RemoteObjectNameWidget widget; + + QVERIFY(waitForNamesToBeRegistered(&widget, 10000)); + KLineEdit* lineEdit = objectNameLineEdit(&widget); lineEdit->setText("Duplicated "); @@ -288,6 +427,8 @@ KCompletionBox* completionBox = lineEdit->completionBox(); QStringList completionItems = completionBox->items(); + QVERIFY(completionBox->isVisible()); + QCOMPARE(completionItems.count(), 4); QCOMPARE(completionItems[0], QString("Duplicated grandparent/Duplicated parent/" @@ -300,11 +441,100 @@ QString("The object name 8/Duplicated object")); } +void RemoteObjectNameWidgetTest::testNameCompletionDuringNameRegisterUpdate() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + QVERIFY(waitForTargetApplicationToStart(10000)); + + RemoteObjectNameWidget widget; + + KLineEdit* lineEdit = objectNameLineEdit(&widget); + lineEdit->setText("Duplicated "); + + QTest::keyClick(lineEdit, Qt::Key_O, Qt::NoModifier); + + KCompletionBox* completionBox = lineEdit->completionBox(); + QStringList completionItems = completionBox->items(); + + //Ensure that the register is still being updated + QVERIFY(widget.mRemoteObjectNameRegister->isBeingUpdated()); + + QVERIFY(!completionBox->isVisible()); + QCOMPARE(completionItems.count(), 0); + + QVERIFY(waitForNamesToBeRegistered(&widget, 10000)); + + completionBox = lineEdit->completionBox(); + completionItems = completionBox->items(); + + QVERIFY(completionBox->isVisible()); + + QCOMPARE(completionItems.count(), 4); + QCOMPARE(completionItems[0], + QString("Duplicated grandparent/Duplicated parent/" + "Duplicated object")); + QCOMPARE(completionItems[1], + QString("The object name 50/Duplicated object")); + QCOMPARE(completionItems[2], + QString("The object name 7/Duplicated object")); + QCOMPARE(completionItems[3], + QString("The object name 8/Duplicated object")); +} + +void RemoteObjectNameWidgetTest:: + testNameSubcompletionDuringNameRegisterUpdate() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + QVERIFY(waitForTargetApplicationToStart(10000)); + + RemoteObjectNameWidget widget; + + KLineEdit* lineEdit = objectNameLineEdit(&widget); + + //Using the configured KShortcut with QTest::keyClick is too cumbersome, + //so the binding is overriden for this test + lineEdit->setKeyBinding(KCompletionBase::SubstringCompletion, + KShortcut("Ctrl+T")); + QTest::keyClick(lineEdit, Qt::Key_T, Qt::ControlModifier); + + KCompletionBox* completionBox = lineEdit->completionBox(); + QStringList completionItems = completionBox->items(); + + //Ensure that the register is still being updated + QVERIFY(widget.mRemoteObjectNameRegister->isBeingUpdated()); + + QVERIFY(!completionBox->isVisible()); + QCOMPARE(completionItems.count(), 0); + + QVERIFY(waitForNamesToBeRegistered(&widget, 10000)); + + completionBox = lineEdit->completionBox(); + completionItems = completionBox->items(); + + QVERIFY(completionBox->isVisible()); + + QCOMPARE(completionItems.count(), 82); + QVERIFY(completionItems.contains("The object name 42")); + QVERIFY(completionItems.contains("The object name 420")); + QVERIFY(completionItems.contains("The object name 421")); + QVERIFY(completionItems.contains("The object name 422")); + QVERIFY(completionItems.contains("The object name 423")); + QVERIFY(completionItems.contains("Duplicated grandparent/Duplicated parent/" + "Duplicated object")); + QVERIFY(completionItems.contains("The object name 50/Duplicated object")); + QVERIFY(completionItems.contains("The object name 7/Duplicated object")); + QVERIFY(completionItems.contains("The object name 8/Duplicated object")); +} + void RemoteObjectNameWidgetTest::testTargetApplicationStartedAfterWidget() { RemoteObjectNameWidget widget; QSignalSpy remoteObjectChosenSpy(&widget, SIGNAL(remoteObjectChosen(RemoteObject*))); + QCOMPARE(objectNameLineEdit(&widget)->cursor().shape(), Qt::IBeamCursor); + QStringList items = objectNameLineEdit(&widget)->completionObject()-> items(); QCOMPARE(items.count(), 0); @@ -320,6 +550,12 @@ QVERIFY(waitForTargetApplicationToStart(10000)); + QCOMPARE(objectNameLineEdit(&widget)->cursor().shape(), Qt::BusyCursor); + + QVERIFY(waitForNamesToBeRegistered(&widget, 10000)); + + QCOMPARE(objectNameLineEdit(&widget)->cursor().shape(), Qt::IBeamCursor); + items = objectNameLineEdit(&widget)->completionObject()->items(); assertCompletionItems(items); @@ -341,6 +577,9 @@ QVERIFY(waitForTargetApplicationToStart(10000)); RemoteObjectNameWidget widget; + + QVERIFY(waitForNamesToBeRegistered(&widget, 10000)); + QSignalSpy remoteObjectChosenSpy(&widget, SIGNAL(remoteObjectChosen(RemoteObject*))); @@ -452,6 +691,38 @@ return waitFor(&isTargetApplicationStopped, timeout); } +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 RegisterNotBeingUpdatedCondition { +public: + const RemoteObjectNameRegister* mRemoteObjectNameRegister; + + bool condition() const { + return !mRemoteObjectNameRegister->isBeingUpdated(); + } +}; + +bool RemoteObjectNameWidgetTest::waitForNamesToBeRegistered( + const RemoteObjectNameWidget* widget, int timeout) const { + RegisterNotBeingUpdatedCondition helper; + helper.mRemoteObjectNameRegister = widget->mRemoteObjectNameRegister; + return waitFor(helper, &RegisterNotBeingUpdatedCondition::condition, + 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-07-11 14:29:47 UTC (rev 355) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForPropertyWidgetTest.cpp 2012-08-04 15:54:59 UTC (rev 356) @@ -18,13 +18,18 @@ #include <QtTest> +#define protected public +#define private public #include "WaitForPropertyWidget.h" +#undef private +#undef protected #include <KLineEdit> #include "../data/WaitForProperty.h" #ifdef QT_QTDBUS_FOUND +#include "RemoteObjectNameWidget.h" #define protected public #define private public #include "../targetapplication/TargetApplication.h" @@ -56,6 +61,9 @@ bool waitForTargetApplicationToStart(int timeout) const; + bool waitForSignalCount(const QSignalSpy* spy, int count, + int timeout) const; + }; void WaitForPropertyWidgetTest::init() { @@ -112,6 +120,11 @@ QCOMPARE(waitFor.value(), QString("The new value")); } +#ifdef QT_QTDBUS_FOUND +//RemoteObject* must be declared as a metatype to be used in qvariant_cast +Q_DECLARE_METATYPE(RemoteObject*); +#endif + void WaitForPropertyWidgetTest::testPropertyNameCompletion() { #ifdef QT_QTDBUS_FOUND TargetApplication::self()->setTargetApplicationFilePath( @@ -123,8 +136,20 @@ WaitForProperty waitFor; WaitForPropertyWidget widget(&waitFor); + //RemoteObject* must be registered in order to be used with QSignalSpy + qRegisterMetaType<RemoteObject*>("RemoteObject*"); + QSignalSpy remoteObjectChosenSpy(widget.mRemoteObjectNameWidget, + SIGNAL(remoteObjectChosen(RemoteObject*))); + objectNameLineEdit(&widget)->setText("The object name 830"); + //Wait until the RemoteObjectNameWidget emits the remoteObjectChosen signal + //(as it can take some time until the names of the remote objects are + //registered), and ensure that the chosen remote object is not null + QVERIFY(waitForSignalCount(&remoteObjectChosenSpy, 1, 10000)); + QVariant argument = remoteObjectChosenSpy.at(0).at(0); + QVERIFY(qvariant_cast<RemoteObject*>(argument)); + KCompletion* completion = propertyNameLineEdit(&widget)->completionObject(); QStringList items = completion->items(); QCOMPARE(items.count(), 6); @@ -180,6 +205,40 @@ return waitFor(&isTargetApplicationStarted, timeout); } +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 WaitForPropertyWidgetTest::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(WaitForPropertyWidgetTest) #include "WaitForPropertyWidgetTest.moc" This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2012-07-11 14:29:57
|
Revision: 355 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=355&view=rev Author: danxuliu Date: 2012-07-11 14:29:47 +0000 (Wed, 11 Jul 2012) Log Message: ----------- Add missing attribute documentation. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/KTutorial.h Modified: trunk/ktutorial/ktutorial-library/src/KTutorial.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/KTutorial.h 2012-07-05 18:19:04 UTC (rev 354) +++ trunk/ktutorial/ktutorial-library/src/KTutorial.h 2012-07-11 14:29:47 UTC (rev 355) @@ -197,6 +197,9 @@ */ KAction* mTutorialsAction; + /** + * The helper used to find objects. + */ ObjectFinder* mObjectFinder; /** This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2012-07-05 18:19:12
|
Revision: 354 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=354&view=rev Author: danxuliu Date: 2012-07-05 18:19:04 +0000 (Thu, 05 Jul 2012) Log Message: ----------- Refactor to extract object finding logic from KTutorial to its own class, ObjectFinder. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/CMakeLists.txt trunk/ktutorial/ktutorial-library/src/KTutorial.cpp trunk/ktutorial/ktutorial-library/src/KTutorial.h trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt Added Paths: ----------- trunk/ktutorial/ktutorial-library/src/ObjectFinder.cpp trunk/ktutorial/ktutorial-library/src/ObjectFinder.h trunk/ktutorial/ktutorial-library/tests/ObjectFinderTest.cpp Removed Paths: ------------- trunk/ktutorial/ktutorial-library/tests/KTutorialTest.cpp Modified: trunk/ktutorial/ktutorial-library/src/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/src/CMakeLists.txt 2012-07-05 12:38:07 UTC (rev 353) +++ trunk/ktutorial/ktutorial-library/src/CMakeLists.txt 2012-07-05 18:19:04 UTC (rev 354) @@ -17,6 +17,7 @@ set(ktutorial_LIB_SRCS KTutorial.cpp + ObjectFinder.cpp Option.cpp Step.cpp Tutorial.cpp @@ -49,6 +50,7 @@ set(ktutorial_LIB_HEADERS ktutorial_export.h KTutorial.h + ObjectFinder.h Option.h Step.h Tutorial.h Modified: trunk/ktutorial/ktutorial-library/src/KTutorial.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/KTutorial.cpp 2012-07-05 12:38:07 UTC (rev 353) +++ trunk/ktutorial/ktutorial-library/src/KTutorial.cpp 2012-07-05 18:19:04 UTC (rev 354) @@ -96,146 +96,6 @@ KTutorial* KTutorial::sSelf = new KTutorial(); -QList<QObject*> KTutorial::getBestMatches(const QString& name, - QList< QList<QObject*> > objectPaths) const { - if (name.isEmpty() || objectPaths.isEmpty()) { - return QList<QObject*>(); - } - - if (name.indexOf('/') == -1) { - QList< QList<QObject*> > filteredObjectPaths = - filterObjectPaths(name, objectPaths); - - QList<QObject*> filteredObjects; - foreach (QList<QObject*> filteredObjectPath, filteredObjectPaths) { - filteredObjects.append(filteredObjectPath.first()); - } - - return filteredObjects; - } - - QRegExp slashPattern("/+"); - QString ancestorName = name.left(name.indexOf(slashPattern)); - QString descendantName = name.mid(ancestorName.length() + - slashPattern.matchedLength()); - - return getBestMatches(descendantName, filterObjectPaths(ancestorName, - objectPaths)); -} - -QList< QList<QObject*> > KTutorial::filterObjectPaths(const QString& name, - const QList< QList<QObject*> >& objectPaths) const { - QList< QList<QObject*> > filteredPaths = filterDirectChildren(name, - objectPaths); - if (filteredPaths.size() > 0) { - return filteredPaths; - } - - filteredPaths = filterNestedChildrenWithUnnamedAncestors(name, objectPaths); - if (filteredPaths.size() > 0) { - return filteredPaths; - } - - return filterNestedChildren(name, objectPaths); -} - -QList< QList<QObject*> > KTutorial::filterDirectChildren(const QString& name, - const QList< QList<QObject*> >& objectPaths) const { - QList< QList<QObject*> > filteredPaths; - - foreach (QList<QObject*> objectPath, objectPaths) { - if (objectPath.size() >= 2 && objectPath[1]->objectName() == name) { - objectPath.removeAt(0); - filteredPaths.append(objectPath); - } - } - - return filteredPaths; -} - -QList< QList<QObject*> > KTutorial::filterNestedChildrenWithUnnamedAncestors( - const QString& name, - const QList< QList<QObject*> >& objectPaths) const { - QList< QList<QObject*> > candidatePaths; - - //No need to use std::numeric_limits, as there would never be a 100000 - //levels deep object. - int minimumNumberOfUnnamedAncestors = 100000; - foreach (QList<QObject*> objectPath, objectPaths) { - objectPath.removeAt(0); - - int unnamedAncestorCount = 0; - while (objectPath.size() > unnamedAncestorCount && - objectPath[unnamedAncestorCount]->objectName() == "") { - unnamedAncestorCount++; - } - - if (unnamedAncestorCount > 0 && - objectPath.size() > unnamedAncestorCount && - objectPath[unnamedAncestorCount]->objectName() == name) { - candidatePaths.append(objectPath); - - if (unnamedAncestorCount < minimumNumberOfUnnamedAncestors) { - minimumNumberOfUnnamedAncestors = unnamedAncestorCount; - } - } - } - - QList< QList<QObject*> > filteredPaths; - - foreach (QList<QObject*> candidatePath, candidatePaths) { - if (candidatePath[minimumNumberOfUnnamedAncestors]->objectName() == - name) { - for (int i=0; i<minimumNumberOfUnnamedAncestors; ++i) { - candidatePath.removeAt(0); - } - filteredPaths.append(candidatePath); - } - } - - return filteredPaths; -} - -QList< QList<QObject*> > KTutorial::filterNestedChildren(const QString& name, - const QList< QList<QObject*> >& objectPaths) const { - QList< QList<QObject*> > candidatePaths; - - //No need to use std::numeric_limits, as there would never be a 100000 - //levels deep object. - int minimumNumberOfAncestors = 100000; - foreach (QList<QObject*> objectPath, objectPaths) { - objectPath.removeAt(0); - - int ancestorCount = 0; - while (objectPath.size() > ancestorCount && - objectPath[ancestorCount]->objectName() != name) { - ancestorCount++; - } - - if (ancestorCount > 0 && objectPath.size() > ancestorCount && - objectPath[ancestorCount]->objectName() == name) { - candidatePaths.append(objectPath); - - if (ancestorCount < minimumNumberOfAncestors) { - minimumNumberOfAncestors = ancestorCount; - } - } - } - - QList< QList<QObject*> > filteredPaths; - - foreach (QList<QObject*> candidatePath, candidatePaths) { - if (candidatePath[minimumNumberOfAncestors]->objectName() == name) { - for (int i=0; i<minimumNumberOfAncestors; ++i) { - candidatePath.removeAt(0); - } - filteredPaths.append(candidatePath); - } - } - - return filteredPaths; -} - //private slots: void KTutorial::showTutorialManagerDialog() const { Modified: trunk/ktutorial/ktutorial-library/src/KTutorial.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/KTutorial.h 2012-07-05 12:38:07 UTC (rev 353) +++ trunk/ktutorial/ktutorial-library/src/KTutorial.h 2012-07-05 18:19:04 UTC (rev 354) @@ -23,6 +23,7 @@ #include "ktutorial_export.h" +#include "ObjectFinder.h" #include "TutorialManager.h" class KAction; @@ -171,18 +172,7 @@ */ template <typename T> T findObject(const QString& name) const { - QList<T> candidateObjects; - findObjects<T>(name, mParent, candidateObjects); - - if (candidateObjects.isEmpty()) { - return 0; - } - - if (candidateObjects.count() == 1) { - return candidateObjects.first(); - } - - return getBestMatch(name, candidateObjects); + return mObjectFinder->findObject<T>(name, mParent); } private: @@ -207,6 +197,8 @@ */ KAction* mTutorialsAction; + ObjectFinder* mObjectFinder; + /** * Creates a new KTutorial. * Private to avoid classes other than self to create instances. @@ -216,174 +208,9 @@ mTutorialmanager->setParent(this); mTutorialsAction = 0; mParent = 0; + mObjectFinder = new ObjectFinder(this); } - /** - * Adds to the foundObjects list the objects with the specified name that - * are descendant of the given ancestor, if any. - * The name of the objects can contain ancestor names separated by "/". Note - * that ancestors are not necessarily direct parents. - * - * @param name The name of the objects to find. - * @param ancestor The ancestor to look the objects in. - * @param foundObjects The list to add to the objects with the specified - * name to. - */ - template <typename T> - void findObjects(const QString& name, const QObject* ancestor, - QList<T>& foundObjects) const { - if (name.isEmpty() || ancestor == 0) { - return; - } - - if (name.indexOf('/') == -1) { - foundObjects.append(ancestor->findChildren<T>(name)); - return; - } - - QRegExp slashPattern("/+"); - QString ancestorName = name.left(name.indexOf(slashPattern)); - QString descendantName = name.mid(ancestorName.length() + - slashPattern.matchedLength()); - - QList<QObject*> namedAncestors = - ancestor->findChildren<QObject*>(ancestorName); - foreach (QObject* namedAncestor, namedAncestors) { - findObjects<T>(descendantName, namedAncestor, foundObjects); - } - } - - /** - * Resolves the ambiguity between several objects that match the given name. - * The ambiguity resolving rules are those specified in - * findObject(const QString&). - * - * @param name The name of the object to find. - * @param candidateObjects A list with objects that match the given name. - * @return The object that matches the best the given name. - */ - template <typename T> - T getBestMatch(const QString& name, QList<T> candidateObjects) const { - QList< QList<QObject*> > objectPaths = getObjectPaths(candidateObjects); - - QList<QObject*> bestMatches = getBestMatches(name, objectPaths); - - //Should not happen, but just in case - if (bestMatches.isEmpty()) { - return 0; - } - - return static_cast<T>(bestMatches[0]); - } - - /** - * Returns a list with the paths to the given objects. - * Each path is a list that contains the object and all its ancestors. The - * first object in the list is the more distant ancestor, and the last - * object is the object itself. - * - * @param objects The objects to get their paths. - * @return A list with the paths to the given objects. - */ - template <typename T> - QList< QList<QObject*> > getObjectPaths(const QList<T> objects) const { - QList< QList<QObject*> > objectPaths; - - foreach (T candidateObject, objects) { - QList<QObject*> objectPath; - - QObject* ancestor = candidateObject; - while (ancestor) { - objectPath.prepend(ancestor); - ancestor = ancestor->parent(); - } - - objectPaths.append(objectPath); - } - - return objectPaths; - } - - /** - * Gets the objects from the given object paths that match the best the - * given name. - * The name can contain ancestor names. The ambiguity resolving rules are - * applied recursively for each component of the name, so the object paths - * used to find each component are the ones filtered with the name of its - * ancestor. - * - * @param name The name of the object to get. - * @param objectPaths The paths to get the object from. - * @return The list of objects that match the best the given name. - */ - QList<QObject*> getBestMatches(const QString& name, - QList< QList<QObject*> > objectPaths) const; - - /** - * Returns the object paths that contain a descendant of the base object - * with the given name. - * If direct children are found, their path is used. If not, if descendants - * without named objects between them and the base object are found, their - * path is used. If not, the path of the shallower descendants is used. - * The name must be a single object name, without any ancestor name. - * The returned paths are trimmed to make the object with the given name the - * new base object of the path. - * - * @param name The name of the descendant to find. - * @param objectPaths The paths to search the object in. - * @return The filtered and trimmed object paths. - */ - QList< QList<QObject*> > filterObjectPaths(const QString& name, - const QList< QList<QObject*> >& objectPaths) const; - - /** - * Returns the object paths that contain a direct child from the base object - * with the given name. - * The name must be a single object name, without any ancestor name. - * The returned paths are trimmed to make the object with the given name the - * new base object of the path. - * - * @param name The name of the direct child to find. - * @param objectPaths The paths to search the object in. - * @return The filtered and trimmed object paths. - */ - QList< QList<QObject*> > filterDirectChildren(const QString& name, - const QList< QList<QObject*> >& objectPaths) const; - - /** - * Returns the object paths that contain a descendant from the base object - * with the given name. - * All the objects between the base object and the descendant with the given - * name must have no name. - * If there is more than one descendant with the given name, only the - * shallower ones are taken into account. - * The name must be a single object name, without any ancestor name. - * The returned paths are trimmed to make the object with the given name the - * new base object of the path. - * - * @param name The name of the descendant to find. - * @param objectPaths The paths to search the object in. - * @return The filtered and trimmed object paths. - */ - QList< QList<QObject*> > filterNestedChildrenWithUnnamedAncestors( - const QString& name, const QList< QList<QObject*> >& objectPaths) const; - - /** - * Returns the object paths that contain a descendant from the base object - * with the given name. - * If there is more than one descendant with the given name, only the - * shallower ones are taken into account. - * The name must be a single object name, without any ancestor name. - * The returned paths are trimmed to make the object with the given name the - * new base object of the path. - * - * @param name The name of the descendant to find. - * @param objectPaths The paths to search the object in. - * @return The filtered and trimmed object paths. - */ - QList< QList<QObject*> > filterNestedChildren(const QString& name, - const QList< QList<QObject*> >& objectPaths) const; - private slots: /** Added: trunk/ktutorial/ktutorial-library/src/ObjectFinder.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/ObjectFinder.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/src/ObjectFinder.cpp 2012-07-05 18:19:04 UTC (rev 354) @@ -0,0 +1,161 @@ +/*************************************************************************** + * Copyright (C) 2012 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "ObjectFinder.h" + +//private: + +QList<QObject*> ObjectFinder::getBestMatches(const QString& name, + QList< QList<QObject*> > objectPaths) const { + if (name.isEmpty() || objectPaths.isEmpty()) { + return QList<QObject*>(); + } + + if (name.indexOf('/') == -1) { + QList< QList<QObject*> > filteredObjectPaths = + filterObjectPaths(name, objectPaths); + + QList<QObject*> filteredObjects; + foreach (QList<QObject*> filteredObjectPath, filteredObjectPaths) { + filteredObjects.append(filteredObjectPath.first()); + } + + return filteredObjects; + } + + QRegExp slashPattern("/+"); + QString ancestorName = name.left(name.indexOf(slashPattern)); + QString descendantName = name.mid(ancestorName.length() + + slashPattern.matchedLength()); + + return getBestMatches(descendantName, filterObjectPaths(ancestorName, + objectPaths)); +} + +QList< QList<QObject*> > ObjectFinder::filterObjectPaths(const QString& name, + const QList< QList<QObject*> >& objectPaths) const { + QList< QList<QObject*> > filteredPaths = filterDirectChildren(name, + objectPaths); + if (filteredPaths.size() > 0) { + return filteredPaths; + } + + filteredPaths = filterNestedChildrenWithUnnamedAncestors(name, objectPaths); + if (filteredPaths.size() > 0) { + return filteredPaths; + } + + return filterNestedChildren(name, objectPaths); +} + +QList< QList<QObject*> > ObjectFinder::filterDirectChildren(const QString& name, + const QList< QList<QObject*> >& objectPaths) const { + QList< QList<QObject*> > filteredPaths; + + foreach (QList<QObject*> objectPath, objectPaths) { + if (objectPath.size() >= 2 && objectPath[1]->objectName() == name) { + objectPath.removeAt(0); + filteredPaths.append(objectPath); + } + } + + return filteredPaths; +} + +QList< QList<QObject*> > ObjectFinder::filterNestedChildrenWithUnnamedAncestors( + const QString& name, + const QList< QList<QObject*> >& objectPaths) const { + QList< QList<QObject*> > candidatePaths; + + //No need to use std::numeric_limits, as there would never be a 100000 + //levels deep object. + int minimumNumberOfUnnamedAncestors = 100000; + foreach (QList<QObject*> objectPath, objectPaths) { + objectPath.removeAt(0); + + int unnamedAncestorCount = 0; + while (objectPath.size() > unnamedAncestorCount && + objectPath[unnamedAncestorCount]->objectName() == "") { + unnamedAncestorCount++; + } + + if (unnamedAncestorCount > 0 && + objectPath.size() > unnamedAncestorCount && + objectPath[unnamedAncestorCount]->objectName() == name) { + candidatePaths.append(objectPath); + + if (unnamedAncestorCount < minimumNumberOfUnnamedAncestors) { + minimumNumberOfUnnamedAncestors = unnamedAncestorCount; + } + } + } + + QList< QList<QObject*> > filteredPaths; + + foreach (QList<QObject*> candidatePath, candidatePaths) { + if (candidatePath[minimumNumberOfUnnamedAncestors]->objectName() == + name) { + for (int i=0; i<minimumNumberOfUnnamedAncestors; ++i) { + candidatePath.removeAt(0); + } + filteredPaths.append(candidatePath); + } + } + + return filteredPaths; +} + +QList< QList<QObject*> > ObjectFinder::filterNestedChildren(const QString& name, + const QList< QList<QObject*> >& objectPaths) const { + QList< QList<QObject*> > candidatePaths; + + //No need to use std::numeric_limits, as there would never be a 100000 + //levels deep object. + int minimumNumberOfAncestors = 100000; + foreach (QList<QObject*> objectPath, objectPaths) { + objectPath.removeAt(0); + + int ancestorCount = 0; + while (objectPath.size() > ancestorCount && + objectPath[ancestorCount]->objectName() != name) { + ancestorCount++; + } + + if (ancestorCount > 0 && objectPath.size() > ancestorCount && + objectPath[ancestorCount]->objectName() == name) { + candidatePaths.append(objectPath); + + if (ancestorCount < minimumNumberOfAncestors) { + minimumNumberOfAncestors = ancestorCount; + } + } + } + + QList< QList<QObject*> > filteredPaths; + + foreach (QList<QObject*> candidatePath, candidatePaths) { + if (candidatePath[minimumNumberOfAncestors]->objectName() == name) { + for (int i=0; i<minimumNumberOfAncestors; ++i) { + candidatePath.removeAt(0); + } + filteredPaths.append(candidatePath); + } + } + + return filteredPaths; +} Property changes on: trunk/ktutorial/ktutorial-library/src/ObjectFinder.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/src/ObjectFinder.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/ObjectFinder.h (rev 0) +++ trunk/ktutorial/ktutorial-library/src/ObjectFinder.h 2012-07-05 18:19:04 UTC (rev 354) @@ -0,0 +1,242 @@ +/*************************************************************************** + * Copyright (C) 2012 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef OBJECTFINDER_H +#define OBJECTFINDER_H + +#include <QObject> +#include <QRegExp> + +#include "ktutorial_export.h" + +/** + * Helper class to find objects. + * This class is not intended to be used directly. Instead, use + * KTutorial::findObject(const QString&). + */ +class KTUTORIAL_EXPORT ObjectFinder: public QObject { +Q_OBJECT +public: + + /** + * Creates a new ObjectFinder with the given parent. + * + * @param parent The parent object. + */ + ObjectFinder(QObject* parent): QObject(parent) { + } + + /** + * Returns the object with the specified name, if any. + * Objects are searched in the children of the given base object. + * + * For details please refer to KTutorial::findObject(const QString&) + * documentation. + * + * @param name The name of the object to find. + * @param baseObject The base object to search from. + * @return The object with the specified name, or null if there is none. + * @see KTutorial::findObject(const QString&) + */ + template <typename T> + T findObject(const QString& name, const QObject* baseObject) const { + QList<T> candidateObjects; + findObjects<T>(name, baseObject, candidateObjects); + + if (candidateObjects.isEmpty()) { + return 0; + } + + if (candidateObjects.count() == 1) { + return candidateObjects.first(); + } + + return getBestMatch(name, candidateObjects); + } + +private: + + /** + * Adds to the foundObjects list the objects with the specified name that + * are descendant of the given ancestor, if any. + * The name of the objects can contain ancestor names separated by "/". Note + * that ancestors are not necessarily direct parents. + * + * @param name The name of the objects to find. + * @param ancestor The ancestor to look the objects in. + * @param foundObjects The list to add to the objects with the specified + * name to. + */ + template <typename T> + void findObjects(const QString& name, const QObject* ancestor, + QList<T>& foundObjects) const { + if (name.isEmpty() || ancestor == 0) { + return; + } + + if (name.indexOf('/') == -1) { + foundObjects.append(ancestor->findChildren<T>(name)); + return; + } + + QRegExp slashPattern("/+"); + QString ancestorName = name.left(name.indexOf(slashPattern)); + QString descendantName = name.mid(ancestorName.length() + + slashPattern.matchedLength()); + + QList<QObject*> namedAncestors = + ancestor->findChildren<QObject*>(ancestorName); + foreach (QObject* namedAncestor, namedAncestors) { + findObjects<T>(descendantName, namedAncestor, foundObjects); + } + } + + /** + * Resolves the ambiguity between several objects that match the given name. + * The ambiguity resolving rules are those specified in + * findObject(const QString&). + * + * @param name The name of the object to find. + * @param candidateObjects A list with objects that match the given name. + * @return The object that matches the best the given name. + */ + template <typename T> + T getBestMatch(const QString& name, QList<T> candidateObjects) const { + QList< QList<QObject*> > objectPaths = getObjectPaths(candidateObjects); + + QList<QObject*> bestMatches = getBestMatches(name, objectPaths); + + //Should not happen, but just in case + if (bestMatches.isEmpty()) { + return 0; + } + + return static_cast<T>(bestMatches[0]); + } + + /** + * Returns a list with the paths to the given objects. + * Each path is a list that contains the object and all its ancestors. The + * first object in the list is the more distant ancestor, and the last + * object is the object itself. + * + * @param objects The objects to get their paths. + * @return A list with the paths to the given objects. + */ + template <typename T> + QList< QList<QObject*> > getObjectPaths(const QList<T> objects) const { + QList< QList<QObject*> > objectPaths; + + foreach (T candidateObject, objects) { + QList<QObject*> objectPath; + + QObject* ancestor = candidateObject; + while (ancestor) { + objectPath.prepend(ancestor); + ancestor = ancestor->parent(); + } + + objectPaths.append(objectPath); + } + + return objectPaths; + } + + /** + * Gets the objects from the given object paths that match the best the + * given name. + * The name can contain ancestor names. The ambiguity resolving rules are + * applied recursively for each component of the name, so the object paths + * used to find each component are the ones filtered with the name of its + * ancestor. + * + * @param name The name of the object to get. + * @param objectPaths The paths to get the object from. + * @return The list of objects that match the best the given name. + */ + QList<QObject*> getBestMatches(const QString& name, + QList< QList<QObject*> > objectPaths) const; + + /** + * Returns the object paths that contain a descendant of the base object + * with the given name. + * If direct children are found, their path is used. If not, if descendants + * without named objects between them and the base object are found, their + * path is used. If not, the path of the shallower descendants is used. + * The name must be a single object name, without any ancestor name. + * The returned paths are trimmed to make the object with the given name the + * new base object of the path. + * + * @param name The name of the descendant to find. + * @param objectPaths The paths to search the object in. + * @return The filtered and trimmed object paths. + */ + QList< QList<QObject*> > filterObjectPaths(const QString& name, + const QList< QList<QObject*> >& objectPaths) const; + + /** + * Returns the object paths that contain a direct child from the base object + * with the given name. + * The name must be a single object name, without any ancestor name. + * The returned paths are trimmed to make the object with the given name the + * new base object of the path. + * + * @param name The name of the direct child to find. + * @param objectPaths The paths to search the object in. + * @return The filtered and trimmed object paths. + */ + QList< QList<QObject*> > filterDirectChildren(const QString& name, + const QList< QList<QObject*> >& objectPaths) const; + + /** + * Returns the object paths that contain a descendant from the base object + * with the given name. + * All the objects between the base object and the descendant with the given + * name must have no name. + * If there is more than one descendant with the given name, only the + * shallower ones are taken into account. + * The name must be a single object name, without any ancestor name. + * The returned paths are trimmed to make the object with the given name the + * new base object of the path. + * + * @param name The name of the descendant to find. + * @param objectPaths The paths to search the object in. + * @return The filtered and trimmed object paths. + */ + QList< QList<QObject*> > filterNestedChildrenWithUnnamedAncestors( + const QString& name, const QList< QList<QObject*> >& objectPaths) const; + + /** + * Returns the object paths that contain a descendant from the base object + * with the given name. + * If there is more than one descendant with the given name, only the + * shallower ones are taken into account. + * The name must be a single object name, without any ancestor name. + * The returned paths are trimmed to make the object with the given name the + * new base object of the path. + * + * @param name The name of the descendant to find. + * @param objectPaths The paths to search the object in. + * @return The filtered and trimmed object paths. + */ + QList< QList<QObject*> > filterNestedChildren(const QString& name, + const QList< QList<QObject*> >& objectPaths) const; + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-library/src/ObjectFinder.h ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt 2012-07-05 12:38:07 UTC (rev 353) +++ trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt 2012-07-05 18:19:04 UTC (rev 354) @@ -26,7 +26,7 @@ ENDMACRO(UNIT_TESTS) unit_tests( - KTutorial + ObjectFinder Option Step Tutorial @@ -51,7 +51,7 @@ ENDMACRO(MEM_TESTS) mem_tests( - KTutorial + ObjectFinder Option Step Tutorial Deleted: trunk/ktutorial/ktutorial-library/tests/KTutorialTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/KTutorialTest.cpp 2012-07-05 12:38:07 UTC (rev 353) +++ trunk/ktutorial/ktutorial-library/tests/KTutorialTest.cpp 2012-07-05 18:19:04 UTC (rev 354) @@ -1,454 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2011-2012 by Daniel Calviño Sánchez * - * dan...@gm... * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; If not, see <http://www.gnu.org/licenses/>. * - ***************************************************************************/ - -#include <QtTest> - -#include <QAction> - -#include "KTutorial.h" - -class KTutorialTest: public QObject { -Q_OBJECT - -private slots: - - void initTestCase(); - void cleanupTestCase(); - - void testFindObjectSingleName(); - void testFindObjectSingleNameUnknown(); - void testFindObjectComplexNameDirectChild(); - void testFindObjectComplexNameNestedChild(); - void testFindObjectComplexNameAncestorNameNotUnique(); - void testFindObjectComplexNameUnknownParent(); - - void testFindObjectAmbiguousSingleNameDirectChild(); - void testFindObjectAmbiguousSingleNameNestedChildUnnamedAncestors(); - void testFindObjectAmbiguousSingleNameNestedChildUnnamedAncestorsDeeperThanNamed(); - void testFindObjectAmbiguousSingleNameNestedChildNamedAncestors(); - void testFindObjectAmbiguousSingleNameNestedChildNamedAncestorsSameDeepThanMixed(); - void testFindObjectAmbiguousSingleNameNestedChildMixedAncestorsSameDeepThanNamed(); - void testFindObjectAmbiguousComplexName(); - void testFindObjectAmbiguousComplexNameUniqueAncestor(); - void testFindObjectComplexNameDifferentAncestorIfSolvingAmbiguity(); - - void testFindObjectEmptyName(); - void testFindObjectSingleSlash(); - void testFindObjectSlashEndedName(); - void testFindObjectSeveralSlashes(); - -private: - - KXmlGuiWindow* mMainWindow; - QObject* mObject1_1_1; - QAction* mAction1_1_2; - QObject* mObject1_2_1; - QObject* mObject2_1_1; - QObject* mObject2_1_2; - QAction* mAction2_1_3; - QAction* mAction2_1_4; - QObject* mObject3_1_1; - QObject* mObject3_2_1_1; - QAction* mAction3_3_1_1; - QObject* mAmbiguousObject5; - QObject* mAmbiguousObject8_1_2; - QObject* mAmbiguousObject10_1; - QObject* mAmbiguousObject11_1_2; - QObject* mAmbiguousObject12_1; - QObject* mAmbiguousObject12_2_3; - QObject* mObject14_1_2; - QObject* mAmbiguousObject15_2_2_1; - QObject* mAmbiguousObject16_2_1_1_1; - - void assertFindObject(const QString& objectName, QObject* object) const; - void assertFindAction(const QString& objectName, QAction* action) const; - -}; - -void KTutorialTest::initTestCase() { - mMainWindow = new KXmlGuiWindow(); - KTutorial::self()->setup(mMainWindow); - - //-Grand parent1 - // |-Parent1 - // | |-The object - // | |-The action - // |-Parent2 - // |-The object - //-Grand parent2 - // |-Parent1 - // |-The object - // |-Another object - // |-The action - // |-Another action - //-Grand parent3 - // |-Parent1 - // | |-The object - // |-Nested parent - // | |-Another object - // |-Nested timer - // |-Another action - QObject* grandParent1 = new QObject(mMainWindow); - grandParent1->setObjectName("Grand parent1"); - QObject* parent1_1 = new QObject(grandParent1); - parent1_1->setObjectName("Parent1"); - mObject1_1_1 = new QObject(parent1_1); - mObject1_1_1->setObjectName("The object"); - mAction1_1_2 = new QAction(parent1_1); - mAction1_1_2->setObjectName("The action"); - - QObject* parent1_2 = new QObject(grandParent1); - parent1_2->setObjectName("Parent2"); - mObject1_2_1 = new QObject(parent1_2); - mObject1_2_1->setObjectName("The object"); - - QObject* grandParent2 = new QObject(mMainWindow); - grandParent2->setObjectName("Grand parent2"); - QObject* parent2_1 = new QObject(grandParent2); - parent2_1->setObjectName("Parent1"); - mObject2_1_1 = new QObject(parent2_1); - mObject2_1_1->setObjectName("The object"); - mObject2_1_2 = new QObject(parent2_1); - mObject2_1_2->setObjectName("Another object"); - mAction2_1_3 = new QAction(parent2_1); - mAction2_1_3->setObjectName("The action"); - mAction2_1_4 = new QAction(parent2_1); - mAction2_1_4->setObjectName("Another action"); - - QObject* grandParent3 = new QObject(mMainWindow); - grandParent3->setObjectName("Grand parent3"); - QObject* parent3_1 = new QObject(grandParent3); - parent3_1->setObjectName("Parent1"); - mObject3_1_1 = new QObject(parent3_1); - mObject3_1_1->setObjectName("The object"); - - QObject* parent3_2 = new QObject(grandParent3); - parent3_2->setObjectName("Nested parent"); - QObject* parent3_2_1 = new QObject(parent3_2); - mObject3_2_1_1 = new QObject(parent3_2_1); - mObject3_2_1_1->setObjectName("Another object"); - - QTimer* parent3_3 = new QTimer(grandParent3); - parent3_3->setObjectName("Nested timer"); - QTimer* parent3_3_1 = new QTimer(parent3_3); - mAction3_3_1_1 = new QAction(parent3_3_1); - mAction3_3_1_1->setObjectName("Another action"); - - //-??? - // |-??? - // | |-Ambiguous object - // |-Ambiguous object - //-Ambiguous object - //-Object 6 - // |-Ambiguous object - // |-Object 6_2 - // |-Ambiguous object - //-Ambiguous object - QObject* unnamedObject4 = new QObject(mMainWindow); - QObject* unnamedObject4_1 = new QObject(unnamedObject4); - QObject* ambiguousObject4_1_1 = new QObject(unnamedObject4_1); - ambiguousObject4_1_1->setObjectName("Ambiguous object"); - QObject* ambiguousObject4_2 = new QObject(unnamedObject4); - ambiguousObject4_2->setObjectName("Ambiguous object"); - - mAmbiguousObject5 = new QObject(mMainWindow); - mAmbiguousObject5->setObjectName("Ambiguous object"); - - QObject* namedObject6 = new QObject(mMainWindow); - namedObject6->setObjectName("Object 6"); - QObject* ambiguousObject6_1 = new QObject(namedObject6); - ambiguousObject6_1->setObjectName("Ambiguous object"); - QObject* namedObject6_2 = new QObject(namedObject6); - namedObject6_2->setObjectName("Object 6_2"); - QObject* ambiguousObject6_2_1 = new QObject(namedObject6_2); - ambiguousObject6_2_1->setObjectName("Ambiguous object"); - - QObject* ambiguousObject7 = new QObject(mMainWindow); - ambiguousObject7->setObjectName("Ambiguous object"); - - //-??? - // |-??? - // | |-Ambiguous object2 - // | |-Ambiguous object3 - //-Object 9 - // |-Ambiguous object2 - // |-Object 9_2 - // | |-Ambiguous object2 - // |-Ambiguous object3 - //-??? - // |-Ambiguous object2 - // |-Ambiguous object2 - QObject* unnamedObject8 = new QObject(mMainWindow); - QObject* unnamedObject8_1 = new QObject(unnamedObject8); - QObject* ambiguousObject8_1_1 = new QObject(unnamedObject8_1); - ambiguousObject8_1_1->setObjectName("Ambiguous object2"); - mAmbiguousObject8_1_2 = new QObject(unnamedObject8_1); - mAmbiguousObject8_1_2->setObjectName("Ambiguous object3"); - - QObject* namedObject9 = new QObject(mMainWindow); - namedObject9->setObjectName("Object 9"); - QObject* ambiguousObject9_1 = new QObject(namedObject9); - ambiguousObject9_1->setObjectName("Ambiguous object2"); - QObject* namedObject9_2 = new QObject(namedObject9); - namedObject9_2->setObjectName("Object 9_2"); - QObject* ambiguousObject9_2_1 = new QObject(namedObject9_2); - ambiguousObject9_2_1->setObjectName("Ambiguous object2"); - QObject* ambiguousObject9_3 = new QObject(namedObject9); - ambiguousObject9_3->setObjectName("Ambiguous object3"); - - QObject* unnamedObject10 = new QObject(mMainWindow); - mAmbiguousObject10_1 = new QObject(unnamedObject10); - mAmbiguousObject10_1->setObjectName("Ambiguous object2"); - QObject* ambiguousObject10_2 = new QObject(unnamedObject10); - ambiguousObject10_2->setObjectName("Ambiguous object2"); - - //-Object 11 - // |-Object 11_1 - // |-Ambiguous object4 - // |-Ambiguous object5 - //-Object 12 - // |-Ambiguous object4 - // |-??? - // |-Ambiguous object4 - // |-Ambiguous object5 - // |-Ambiguous object6 - //-Object 13 - // |-Ambiguous object4 - // |-Object 13_2 - // |-Ambiguous object6 - QObject* namedObject11 = new QObject(mMainWindow); - namedObject11->setObjectName("Object 11"); - QObject* namedObject11_1 = new QObject(namedObject11); - namedObject11_1->setObjectName("Object 11_1"); - QObject* ambiguousObject11_1_1 = new QObject(namedObject11_1); - ambiguousObject11_1_1->setObjectName("Ambiguous object4"); - mAmbiguousObject11_1_2 = new QObject(namedObject11_1); - mAmbiguousObject11_1_2->setObjectName("Ambiguous object5"); - - QObject* namedObject12 = new QObject(mMainWindow); - namedObject12->setObjectName("Object 12"); - mAmbiguousObject12_1 = new QObject(namedObject12); - mAmbiguousObject12_1->setObjectName("Ambiguous object4"); - QObject* unnamedObject12_2 = new QObject(namedObject12); - QObject* ambiguousObject12_2_1 = new QObject(unnamedObject12_2); - ambiguousObject12_2_1->setObjectName("Ambiguous object4"); - QObject* ambiguousObject12_2_2 = new QObject(unnamedObject12_2); - ambiguousObject12_2_2->setObjectName("Ambiguous object5"); - mAmbiguousObject12_2_3 = new QObject(unnamedObject12_2); - mAmbiguousObject12_2_3->setObjectName("Ambiguous object6"); - - QObject* namedObject13 = new QObject(mMainWindow); - namedObject13->setObjectName("Object 13"); - QObject* ambiguousObject13_1 = new QObject(namedObject13); - ambiguousObject13_1->setObjectName("Ambiguous object4"); - QObject* namedObject13_2 = new QObject(namedObject13); - namedObject13_2->setObjectName("Object 13_2"); - QObject* ambiguousObject13_2_1 = new QObject(namedObject13_2); - ambiguousObject13_2_1->setObjectName("Ambiguous object6"); - - //-Object 14 - // |-Ambiguous ancestor - // |-Ambiguous object7 - // |-The object - //-??? - // |-Ambiguous ancestor - // | |-Object 15_1_1 - // | |-Ambiguous object7 - // |-Unique ancestor - // |-Object 15_2_1 - // | |-Ambiguous object7 - // |-??? - // |-Ambiguous object7 - //-??? - // |-??? - // | |-Ambiguous ancestor - // | |-Ambiguous object7 - // |-Ambiguous ancestor - // |-??? - // | |-??? - // | |-Ambiguous object7 - // |-Object 16_2_2 - // |-Ambiguous object7 - QObject* namedObject14 = new QObject(mMainWindow); - namedObject14->setObjectName("Object 14"); - QObject* ambiguousAncestor14_1 = new QObject(namedObject14); - ambiguousAncestor14_1->setObjectName("Ambiguous ancestor"); - QObject* ambiguousObject14_1_1 = new QObject(ambiguousAncestor14_1); - ambiguousObject14_1_1->setObjectName("Ambiguous object7"); - mObject14_1_2 = new QObject(ambiguousAncestor14_1); - mObject14_1_2->setObjectName("The object"); - - QObject* unnamedObject15 = new QObject(mMainWindow); - QObject* ambiguousAncestor15_1 = new QObject(unnamedObject15); - ambiguousAncestor15_1->setObjectName("Ambiguous ancestor"); - QObject* namedObject15_1_1 = new QObject(ambiguousAncestor15_1); - namedObject15_1_1->setObjectName("Object 15_1_1"); - QObject* ambiguousObject15_1_1_1 = new QObject(namedObject15_1_1); - ambiguousObject15_1_1_1->setObjectName("Ambiguous object7"); - QObject* uniqueAncestor15_2 = new QObject(unnamedObject15); - uniqueAncestor15_2->setObjectName("Unique ancestor"); - QObject* namedObject15_2_1 = new QObject(uniqueAncestor15_2); - namedObject15_2_1->setObjectName("Object 15_2_1"); - QObject* ambiguousObject15_2_1_1 = new QObject(namedObject15_2_1); - ambiguousObject15_2_1_1->setObjectName("Ambiguous object7"); - QObject* unnamedObject15_2_2 = new QObject(uniqueAncestor15_2); - mAmbiguousObject15_2_2_1 = new QObject(unnamedObject15_2_2); - mAmbiguousObject15_2_2_1->setObjectName("Ambiguous object7"); - - QObject* unnamedObject16 = new QObject(mMainWindow); - QObject* unnamedObject16_1 = new QObject(unnamedObject16); - QObject* ambiguousAncestor16_1_1 = new QObject(unnamedObject16_1); - ambiguousAncestor16_1_1->setObjectName("Ambiguous ancestor"); - QObject* ambiguousObject16_1_1_1 = new QObject(ambiguousAncestor16_1_1); - ambiguousObject16_1_1_1->setObjectName("Ambiguous object7"); - QObject* ambiguousAncestor16_2 = new QObject(unnamedObject16); - ambiguousAncestor16_2->setObjectName("Ambiguous ancestor"); - QObject* unnamedObject16_2_1 = new QObject(ambiguousAncestor16_2); - QObject* unnamedObject16_2_1_1 = new QObject(unnamedObject16_2_1); - mAmbiguousObject16_2_1_1_1 = new QObject(unnamedObject16_2_1_1); - mAmbiguousObject16_2_1_1_1->setObjectName("Ambiguous object7"); - QObject* namedObject16_2_2 = new QObject(ambiguousAncestor16_2); - namedObject16_2_2->setObjectName("Object 16_2_2"); - QObject* ambiguousObject16_2_2_1 = new QObject(namedObject16_2_2); - ambiguousObject16_2_2_1->setObjectName("Ambiguous object7"); -} - -void KTutorialTest::cleanupTestCase() { - delete mMainWindow; -} - -void KTutorialTest::testFindObjectSingleName() { - assertFindObject("The object", mObject1_1_1); -} - -void KTutorialTest::testFindObjectSingleNameUnknown() { - assertFindObject("Unknown object", 0); -} - -void KTutorialTest::testFindObjectComplexNameDirectChild() { - assertFindObject("Grand parent1/Parent1/The object", mObject1_1_1); - assertFindObject("Grand parent1/Parent2/The object", mObject1_2_1); - assertFindObject("Grand parent2/Parent1/The object", mObject2_1_1); - - assertFindAction("Grand parent1/Parent1/The action", mAction1_1_2); - assertFindAction("Grand parent2/Parent1/The action", mAction2_1_3); -} - -void KTutorialTest::testFindObjectComplexNameNestedChild() { - assertFindObject("Grand parent2/The object", mObject2_1_1); - assertFindObject("Nested parent/Another object", mObject3_2_1_1); - - assertFindAction("Grand parent2/The action", mAction2_1_3); - assertFindAction("Nested timer/Another action", mAction3_3_1_1); -} - -void KTutorialTest::testFindObjectComplexNameAncestorNameNotUnique() { - //The ancestor name is not unique, but the full path is - assertFindObject("Parent1/Another object", mObject2_1_2); - - assertFindAction("Parent1/Another action", mAction2_1_4); -} - -void KTutorialTest::testFindObjectComplexNameUnknownParent() { - assertFindObject("Unknown grand parent/The object", 0); - assertFindObject("Grand parent1/Unknown parent/The object", 0); -} - -void KTutorialTest::testFindObjectAmbiguousSingleNameDirectChild() { - assertFindObject("Ambiguous object", mAmbiguousObject5); -} - -void KTutorialTest:: -testFindObjectAmbiguousSingleNameNestedChildUnnamedAncestors() { - assertFindObject("Ambiguous object2", mAmbiguousObject10_1); -} - -void KTutorialTest:: -testFindObjectAmbiguousSingleNameNestedChildUnnamedAncestorsDeeperThanNamed() { - assertFindObject("Ambiguous object3", mAmbiguousObject8_1_2); -} - -void KTutorialTest:: -testFindObjectAmbiguousSingleNameNestedChildNamedAncestors() { - assertFindObject("Ambiguous object4", mAmbiguousObject12_1); -} - -void KTutorialTest:: -testFindObjectAmbiguousSingleNameNestedChildNamedAncestorsSameDeepThanMixed() { - assertFindObject("Ambiguous object5", mAmbiguousObject11_1_2); -} - -void KTutorialTest:: -testFindObjectAmbiguousSingleNameNestedChildMixedAncestorsSameDeepThanNamed() { - assertFindObject("Ambiguous object6", mAmbiguousObject12_2_3); -} - -void KTutorialTest::testFindObjectAmbiguousComplexName() { - //The ancestor is selected by the rule of shallower unnamed ancestors. The - //object is selected by the rule of unnamed ancestor even if there are - //shallower objects, but with named ancestors. - assertFindObject("Ambiguous ancestor/Ambiguous object7", - mAmbiguousObject16_2_1_1_1); -} - -void KTutorialTest::testFindObjectAmbiguousComplexNameUniqueAncestor() { - //The ancestor is unique, although the object itself is ambiguous (even - //among the descendants of that unique ancestor). - assertFindObject("Unique ancestor/Ambiguous object7", - mAmbiguousObject15_2_2_1); -} - -void KTutorialTest:: -testFindObjectComplexNameDifferentAncestorIfSolvingAmbiguity() { - //The full name is unique, but if the rules to resolve ambiguity were - //applied no object would be found, as the "Ambiguous ancestor" found using - //the ambiguity resolving rules have no "The object" descendants. - assertFindObject("Ambiguous ancestor/The object", mObject14_1_2); -} - -void KTutorialTest::testFindObjectEmptyName() { - assertFindObject("", 0); -} - -void KTutorialTest::testFindObjectSingleSlash() { - assertFindObject("/", 0); -} - -void KTutorialTest::testFindObjectSlashEndedName() { - assertFindObject("Parent/", 0); -} - -void KTutorialTest::testFindObjectSeveralSlashes() { - assertFindObject("Parent1///The object", mObject1_1_1); -} - -/////////////////////////////////Helpers//////////////////////////////////////// - -void KTutorialTest::assertFindObject(const QString& objectName, - QObject* object) const { - QCOMPARE(KTutorial::self()->findObject<QObject*>(objectName), object); -} - -void KTutorialTest::assertFindAction(const QString& objectName, - QAction* action) const { - QCOMPARE(KTutorial::self()->findObject<QAction*>(objectName), action); -} - -QTEST_MAIN(KTutorialTest) - -#include "KTutorialTest.moc" Copied: trunk/ktutorial/ktutorial-library/tests/ObjectFinderTest.cpp (from rev 353, trunk/ktutorial/ktutorial-library/tests/KTutorialTest.cpp) =================================================================== --- trunk/ktutorial/ktutorial-library/tests/ObjectFinderTest.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/tests/ObjectFinderTest.cpp 2012-07-05 18:19:04 UTC (rev 354) @@ -0,0 +1,457 @@ +/*************************************************************************** + * Copyright (C) 2011-2012 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include <QtTest> + +#include <QAction> + +#include <KXmlGuiWindow> + +#include "ObjectFinder.h" + +class ObjectFinderTest: public QObject { +Q_OBJECT + +private slots: + + void initTestCase(); + void cleanupTestCase(); + + void testFindObjectSingleName(); + void testFindObjectSingleNameUnknown(); + void testFindObjectComplexNameDirectChild(); + void testFindObjectComplexNameNestedChild(); + void testFindObjectComplexNameAncestorNameNotUnique(); + void testFindObjectComplexNameUnknownParent(); + + void testFindObjectAmbiguousSingleNameDirectChild(); + void testFindObjectAmbiguousSingleNameNestedChildUnnamedAncestors(); + void testFindObjectAmbiguousSingleNameNestedChildUnnamedAncestorsDeeperThanNamed(); + void testFindObjectAmbiguousSingleNameNestedChildNamedAncestors(); + void testFindObjectAmbiguousSingleNameNestedChildNamedAncestorsSameDeepThanMixed(); + void testFindObjectAmbiguousSingleNameNestedChildMixedAncestorsSameDeepThanNamed(); + void testFindObjectAmbiguousComplexName(); + void testFindObjectAmbiguousComplexNameUniqueAncestor(); + void testFindObjectComplexNameDifferentAncestorIfSolvingAmbiguity(); + + void testFindObjectEmptyName(); + void testFindObjectSingleSlash(); + void testFindObjectSlashEndedName(); + void testFindObjectSeveralSlashes(); + +private: + + KXmlGuiWindow* mMainWindow; + QObject* mObject1_1_1; + QAction* mAction1_1_2; + QObject* mObject1_2_1; + QObject* mObject2_1_1; + QObject* mObject2_1_2; + QAction* mAction2_1_3; + QAction* mAction2_1_4; + QObject* mObject3_1_1; + QObject* mObject3_2_1_1; + QAction* mAction3_3_1_1; + QObject* mAmbiguousObject5; + QObject* mAmbiguousObject8_1_2; + QObject* mAmbiguousObject10_1; + QObject* mAmbiguousObject11_1_2; + QObject* mAmbiguousObject12_1; + QObject* mAmbiguousObject12_2_3; + QObject* mObject14_1_2; + QObject* mAmbiguousObject15_2_2_1; + QObject* mAmbiguousObject16_2_1_1_1; + + void assertFindObject(const QString& objectName, QObject* object) const; + void assertFindAction(const QString& objectName, QAction* action) const; + +}; + +void ObjectFinderTest::initTestCase() { + mMainWindow = new KXmlGuiWindow(); + + //-Grand parent1 + // |-Parent1 + // | |-The object + // | |-The action + // |-Parent2 + // |-The object + //-Grand parent2 + // |-Parent1 + // |-The object + // |-Another object + // |-The action + // |-Another action + //-Grand parent3 + // |-Parent1 + // | |-The object + // |-Nested parent + // | |-Another object + // |-Nested timer + // |-Another action + QObject* grandParent1 = new QObject(mMainWindow); + grandParent1->setObjectName("Grand parent1"); + QObject* parent1_1 = new QObject(grandParent1); + parent1_1->setObjectName("Parent1"); + mObject1_1_1 = new QObject(parent1_1); + mObject1_1_1->setObjectName("The object"); + mAction1_1_2 = new QAction(parent1_1); + mAction1_1_2->setObjectName("The action"); + + QObject* parent1_2 = new QObject(grandParent1); + parent1_2->setObjectName("Parent2"); + mObject1_2_1 = new QObject(parent1_2); + mObject1_2_1->setObjectName("The object"); + + QObject* grandParent2 = new QObject(mMainWindow); + grandParent2->setObjectName("Grand parent2"); + QObject* parent2_1 = new QObject(grandParent2); + parent2_1->setObjectName("Parent1"); + mObject2_1_1 = new QObject(parent2_1); + mObject2_1_1->setObjectName("The object"); + mObject2_1_2 = new QObject(parent2_1); + mObject2_1_2->setObjectName("Another object"); + mAction2_1_3 = new QAction(parent2_1); + mAction2_1_3->setObjectName("The action"); + mAction2_1_4 = new QAction(parent2_1); + mAction2_1_4->setObjectName("Another action"); + + QObject* grandParent3 = new QObject(mMainWindow); + grandParent3->setObjectName("Grand parent3"); + QObject* parent3_1 = new QObject(grandParent3); + parent3_1->setObjectName("Parent1"); + mObject3_1_1 = new QObject(parent3_1); + mObject3_1_1->setObjectName("The object"); + + QObject* parent3_2 = new QObject(grandParent3); + parent3_2->setObjectName("Nested parent"); + QObject* parent3_2_1 = new QObject(parent3_2); + mObject3_2_1_1 = new QObject(parent3_2_1); + mObject3_2_1_1->setObjectName("Another object"); + + QTimer* parent3_3 = new QTimer(grandParent3); + parent3_3->setObjectName("Nested timer"); + QTimer* parent3_3_1 = new QTimer(parent3_3); + mAction3_3_1_1 = new QAction(parent3_3_1); + mAction3_3_1_1->setObjectName("Another action"); + + //-??? + // |-??? + // | |-Ambiguous object + // |-Ambiguous object + //-Ambiguous object + //-Object 6 + // |-Ambiguous object + // |-Object 6_2 + // |-Ambiguous object + //-Ambiguous object + QObject* unnamedObject4 = new QObject(mMainWindow); + QObject* unnamedObject4_1 = new QObject(unnamedObject4); + QObject* ambiguousObject4_1_1 = new QObject(unnamedObject4_1); + ambiguousObject4_1_1->setObjectName("Ambiguous object"); + QObject* ambiguousObject4_2 = new QObject(unnamedObject4); + ambiguousObject4_2->setObjectName("Ambiguous object"); + + mAmbiguousObject5 = new QObject(mMainWindow); + mAmbiguousObject5->setObjectName("Ambiguous object"); + + QObject* namedObject6 = new QObject(mMainWindow); + namedObject6->setObjectName("Object 6"); + QObject* ambiguousObject6_1 = new QObject(namedObject6); + ambiguousObject6_1->setObjectName("Ambiguous object"); + QObject* namedObject6_2 = new QObject(namedObject6); + namedObject6_2->setObjectName("Object 6_2"); + QObject* ambiguousObject6_2_1 = new QObject(namedObject6_2); + ambiguousObject6_2_1->setObjectName("Ambiguous object"); + + QObject* ambiguousObject7 = new QObject(mMainWindow); + ambiguousObject7->setObjectName("Ambiguous object"); + + //-??? + // |-??? + // | |-Ambiguous object2 + // | |-Ambiguous object3 + //-Object 9 + // |-Ambiguous object2 + // |-Object 9_2 + // | |-Ambiguous object2 + // |-Ambiguous object3 + //-??? + // |-Ambiguous object2 ... [truncated message content] |
From: <dan...@us...> - 2012-07-05 12:38:17
|
Revision: 353 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=353&view=rev Author: danxuliu Date: 2012-07-05 12:38:07 +0000 (Thu, 05 Jul 2012) Log Message: ----------- Improve handling of ambiguity in names when finding objects. Until now, if finding an object yield more than one result the first one was used. Now, a set of rules to try to resolve the ambiguity are used. Those rules try to handle scenarios like, for example, "Ok" buttons in nested dialogs, as, before, looking for the "Parent dialog/Ok button" may return the button from the child dialog instead of the button of the parent dialog. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/KTutorial.cpp trunk/ktutorial/ktutorial-library/src/KTutorial.h trunk/ktutorial/ktutorial-library/tests/KTutorialTest.cpp Modified: trunk/ktutorial/ktutorial-library/src/KTutorial.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/KTutorial.cpp 2012-07-03 08:59:08 UTC (rev 352) +++ trunk/ktutorial/ktutorial-library/src/KTutorial.cpp 2012-07-05 12:38:07 UTC (rev 353) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2008-2010 by Daniel Calviño Sánchez * + * Copyright (C) 2008-2012 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -96,6 +96,148 @@ KTutorial* KTutorial::sSelf = new KTutorial(); +QList<QObject*> KTutorial::getBestMatches(const QString& name, + QList< QList<QObject*> > objectPaths) const { + if (name.isEmpty() || objectPaths.isEmpty()) { + return QList<QObject*>(); + } + + if (name.indexOf('/') == -1) { + QList< QList<QObject*> > filteredObjectPaths = + filterObjectPaths(name, objectPaths); + + QList<QObject*> filteredObjects; + foreach (QList<QObject*> filteredObjectPath, filteredObjectPaths) { + filteredObjects.append(filteredObjectPath.first()); + } + + return filteredObjects; + } + + QRegExp slashPattern("/+"); + QString ancestorName = name.left(name.indexOf(slashPattern)); + QString descendantName = name.mid(ancestorName.length() + + slashPattern.matchedLength()); + + return getBestMatches(descendantName, filterObjectPaths(ancestorName, + objectPaths)); +} + +QList< QList<QObject*> > KTutorial::filterObjectPaths(const QString& name, + const QList< QList<QObject*> >& objectPaths) const { + QList< QList<QObject*> > filteredPaths = filterDirectChildren(name, + objectPaths); + if (filteredPaths.size() > 0) { + return filteredPaths; + } + + filteredPaths = filterNestedChildrenWithUnnamedAncestors(name, objectPaths); + if (filteredPaths.size() > 0) { + return filteredPaths; + } + + return filterNestedChildren(name, objectPaths); +} + +QList< QList<QObject*> > KTutorial::filterDirectChildren(const QString& name, + const QList< QList<QObject*> >& objectPaths) const { + QList< QList<QObject*> > filteredPaths; + + foreach (QList<QObject*> objectPath, objectPaths) { + if (objectPath.size() >= 2 && objectPath[1]->objectName() == name) { + objectPath.removeAt(0); + filteredPaths.append(objectPath); + } + } + + return filteredPaths; +} + +QList< QList<QObject*> > KTutorial::filterNestedChildrenWithUnnamedAncestors( + const QString& name, + const QList< QList<QObject*> >& objectPaths) const { + QList< QList<QObject*> > candidatePaths; + + //No need to use std::numeric_limits, as there would never be a 100000 + //levels deep object. + int minimumNumberOfUnnamedAncestors = 100000; + foreach (QList<QObject*> objectPath, objectPaths) { + objectPath.removeAt(0); + + int unnamedAncestorCount = 0; + while (objectPath.size() > unnamedAncestorCount && + objectPath[unnamedAncestorCount]->objectName() == "") { + unnamedAncestorCount++; + } + + if (unnamedAncestorCount > 0 && + objectPath.size() > unnamedAncestorCount && + objectPath[unnamedAncestorCount]->objectName() == name) { + candidatePaths.append(objectPath); + + if (unnamedAncestorCount < minimumNumberOfUnnamedAncestors) { + minimumNumberOfUnnamedAncestors = unnamedAncestorCount; + } + } + } + + QList< QList<QObject*> > filteredPaths; + + foreach (QList<QObject*> candidatePath, candidatePaths) { + if (candidatePath[minimumNumberOfUnnamedAncestors]->objectName() == + name) { + for (int i=0; i<minimumNumberOfUnnamedAncestors; ++i) { + candidatePath.removeAt(0); + } + filteredPaths.append(candidatePath); + } + } + + return filteredPaths; +} + +QList< QList<QObject*> > KTutorial::filterNestedChildren(const QString& name, + const QList< QList<QObject*> >& objectPaths) const { + QList< QList<QObject*> > candidatePaths; + + //No need to use std::numeric_limits, as there would never be a 100000 + //levels deep object. + int minimumNumberOfAncestors = 100000; + foreach (QList<QObject*> objectPath, objectPaths) { + objectPath.removeAt(0); + + int ancestorCount = 0; + while (objectPath.size() > ancestorCount && + objectPath[ancestorCount]->objectName() != name) { + ancestorCount++; + } + + if (ancestorCount > 0 && objectPath.size() > ancestorCount && + objectPath[ancestorCount]->objectName() == name) { + candidatePaths.append(objectPath); + + if (ancestorCount < minimumNumberOfAncestors) { + minimumNumberOfAncestors = ancestorCount; + } + } + } + + QList< QList<QObject*> > filteredPaths; + + foreach (QList<QObject*> candidatePath, candidatePaths) { + if (candidatePath[minimumNumberOfAncestors]->objectName() == name) { + for (int i=0; i<minimumNumberOfAncestors; ++i) { + candidatePath.removeAt(0); + } + filteredPaths.append(candidatePath); + } + } + + return filteredPaths; +} + +//private slots: + void KTutorial::showTutorialManagerDialog() const { QDialog* dialog = new TutorialManagerDialog(mTutorialmanager, mParent); dialog->setAttribute(Qt::WA_DeleteOnClose); Modified: trunk/ktutorial/ktutorial-library/src/KTutorial.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/KTutorial.h 2012-07-03 08:59:08 UTC (rev 352) +++ trunk/ktutorial/ktutorial-library/src/KTutorial.h 2012-07-05 12:38:07 UTC (rev 353) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2008-2011 by Daniel Calviño Sánchez * + * Copyright (C) 2008-2012 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -123,6 +123,7 @@ * Returns the object with the specified name, if any. * Objects are searched in the children of the main window of the * application. + * * When the name of the desired object is not unique the name of some * ancestor must be included. Ancestor names are separated using a "/". That * is, "Ancestor name/The object with a repeated name". As many ancestor @@ -132,6 +133,36 @@ * name not unique/The object to find" is valid if, among all the objects * called "Ancestor name not unique", there is only one that has a * descendant called "The object to find". + * + * In some cases it may be posible that a unique name can not be provided, + * even using ancestor names. In those cases there are some rules to resolve + * the ambiguity. Between two or more objects with the same name, the + * selected one is: + * -A direct child of the base object. + * -A nested child without named ancestors between it and the base object. + * -If there is more than one, the one nearer to the base object (that is, + * with the lesser number of intermediate ancestors). + * -A nested child with named or unnamed ancestors between it and the base + * object. + * -If there is more than one, the one nearer to the base object (that is, + * with the lesser number of intermediate ancestors). + * + * If the ambiguous name contains ancestor names the rules are applied for + * each component of the name. Each component is searched for only from the + * already selected previous components. That is, if the ambiguous name is + * "Ancestor name/The object", the rules are applied to find the objects + * named "Ancestor name" using the main window as base object. Then, the + * rules are applied to find "The object" using the "Ancestor name" objects + * as base, so "The object" is searched only in the children of the + * "Ancestor name" objects previously found. Note that, even with the + * ambiguity resolving rules, it may be searched for from several "Ancestor + * name" if, for example, there are several "Ancestor name" child of the + * main window. However, if there were other "Ancestor name" objects that + * were not direct children of the main window they will not be used in the + * search, as the direct children would have taken precedence. + * + * If after applying the rules there is still more than one object that + * matches, the first one is selected. * * @param name The name of the object to find. * @return The object with the specified name, or null if there is none. @@ -140,7 +171,18 @@ */ template <typename T> T findObject(const QString& name) const { - return findObject<T>(name, mParent); + QList<T> candidateObjects; + findObjects<T>(name, mParent, candidateObjects); + + if (candidateObjects.isEmpty()) { + return 0; + } + + if (candidateObjects.count() == 1) { + return candidateObjects.first(); + } + + return getBestMatch(name, candidateObjects); } private: @@ -177,24 +219,26 @@ } /** - * Returns the object with the specified name that is descendant of the - * given ancestor, if any. - * The name of the object can contain ancestor names separated by "/". The - * first object that matches the whole path is returned (note that ancestors - * are not necessarily direct parents). + * Adds to the foundObjects list the objects with the specified name that + * are descendant of the given ancestor, if any. + * The name of the objects can contain ancestor names separated by "/". Note + * that ancestors are not necessarily direct parents. * - * @param name The name of the object to find. - * @param ancestor The ancestor to look the object in. - * @return The object with the specified name, or null if there is none. + * @param name The name of the objects to find. + * @param ancestor The ancestor to look the objects in. + * @param foundObjects The list to add to the objects with the specified + * name to. */ template <typename T> - T findObject(const QString& name, const QObject* ancestor) const { + void findObjects(const QString& name, const QObject* ancestor, + QList<T>& foundObjects) const { if (name.isEmpty() || ancestor == 0) { - return T(); + return; } if (name.indexOf('/') == -1) { - return ancestor->findChild<T>(name); + foundObjects.append(ancestor->findChildren<T>(name)); + return; } QRegExp slashPattern("/+"); @@ -205,15 +249,141 @@ QList<QObject*> namedAncestors = ancestor->findChildren<QObject*>(ancestorName); foreach (QObject* namedAncestor, namedAncestors) { - T object = findObject<T>(descendantName, namedAncestor); - if (object) { - return object; + findObjects<T>(descendantName, namedAncestor, foundObjects); + } + } + + /** + * Resolves the ambiguity between several objects that match the given name. + * The ambiguity resolving rules are those specified in + * findObject(const QString&). + * + * @param name The name of the object to find. + * @param candidateObjects A list with objects that match the given name. + * @return The object that matches the best the given name. + */ + template <typename T> + T getBestMatch(const QString& name, QList<T> candidateObjects) const { + QList< QList<QObject*> > objectPaths = getObjectPaths(candidateObjects); + + QList<QObject*> bestMatches = getBestMatches(name, objectPaths); + + //Should not happen, but just in case + if (bestMatches.isEmpty()) { + return 0; + } + + return static_cast<T>(bestMatches[0]); + } + + /** + * Returns a list with the paths to the given objects. + * Each path is a list that contains the object and all its ancestors. The + * first object in the list is the more distant ancestor, and the last + * object is the object itself. + * + * @param objects The objects to get their paths. + * @return A list with the paths to the given objects. + */ + template <typename T> + QList< QList<QObject*> > getObjectPaths(const QList<T> objects) const { + QList< QList<QObject*> > objectPaths; + + foreach (T candidateObject, objects) { + QList<QObject*> objectPath; + + QObject* ancestor = candidateObject; + while (ancestor) { + objectPath.prepend(ancestor); + ancestor = ancestor->parent(); } + + objectPaths.append(objectPath); } - return T(); + return objectPaths; } + /** + * Gets the objects from the given object paths that match the best the + * given name. + * The name can contain ancestor names. The ambiguity resolving rules are + * applied recursively for each component of the name, so the object paths + * used to find each component are the ones filtered with the name of its + * ancestor. + * + * @param name The name of the object to get. + * @param objectPaths The paths to get the object from. + * @return The list of objects that match the best the given name. + */ + QList<QObject*> getBestMatches(const QString& name, + QList< QList<QObject*> > objectPaths) const; + + /** + * Returns the object paths that contain a descendant of the base object + * with the given name. + * If direct children are found, their path is used. If not, if descendants + * without named objects between them and the base object are found, their + * path is used. If not, the path of the shallower descendants is used. + * The name must be a single object name, without any ancestor name. + * The returned paths are trimmed to make the object with the given name the + * new base object of the path. + * + * @param name The name of the descendant to find. + * @param objectPaths The paths to search the object in. + * @return The filtered and trimmed object paths. + */ + QList< QList<QObject*> > filterObjectPaths(const QString& name, + const QList< QList<QObject*> >& objectPaths) const; + + /** + * Returns the object paths that contain a direct child from the base object + * with the given name. + * The name must be a single object name, without any ancestor name. + * The returned paths are trimmed to make the object with the given name the + * new base object of the path. + * + * @param name The name of the direct child to find. + * @param objectPaths The paths to search the object in. + * @return The filtered and trimmed object paths. + */ + QList< QList<QObject*> > filterDirectChildren(const QString& name, + const QList< QList<QObject*> >& objectPaths) const; + + /** + * Returns the object paths that contain a descendant from the base object + * with the given name. + * All the objects between the base object and the descendant with the given + * name must have no name. + * If there is more than one descendant with the given name, only the + * shallower ones are taken into account. + * The name must be a single object name, without any ancestor name. + * The returned paths are trimmed to make the object with the given name the + * new base object of the path. + * + * @param name The name of the descendant to find. + * @param objectPaths The paths to search the object in. + * @return The filtered and trimmed object paths. + */ + QList< QList<QObject*> > filterNestedChildrenWithUnnamedAncestors( + const QString& name, const QList< QList<QObject*> >& objectPaths) const; + + /** + * Returns the object paths that contain a descendant from the base object + * with the given name. + * If there is more than one descendant with the given name, only the + * shallower ones are taken into account. + * The name must be a single object name, without any ancestor name. + * The returned paths are trimmed to make the object with the given name the + * new base object of the path. + * + * @param name The name of the descendant to find. + * @param objectPaths The paths to search the object in. + * @return The filtered and trimmed object paths. + */ + QList< QList<QObject*> > filterNestedChildren(const QString& name, + const QList< QList<QObject*> >& objectPaths) const; + private slots: /** Modified: trunk/ktutorial/ktutorial-library/tests/KTutorialTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/KTutorialTest.cpp 2012-07-03 08:59:08 UTC (rev 352) +++ trunk/ktutorial/ktutorial-library/tests/KTutorialTest.cpp 2012-07-05 12:38:07 UTC (rev 353) @@ -36,6 +36,17 @@ void testFindObjectComplexNameNestedChild(); void testFindObjectComplexNameAncestorNameNotUnique(); void testFindObjectComplexNameUnknownParent(); + + void testFindObjectAmbiguousSingleNameDirectChild(); + void testFindObjectAmbiguousSingleNameNestedChildUnnamedAncestors(); + void testFindObjectAmbiguousSingleNameNestedChildUnnamedAncestorsDeeperThanNamed(); + void testFindObjectAmbiguousSingleNameNestedChildNamedAncestors(); + void testFindObjectAmbiguousSingleNameNestedChildNamedAncestorsSameDeepThanMixed(); + void testFindObjectAmbiguousSingleNameNestedChildMixedAncestorsSameDeepThanNamed(); + void testFindObjectAmbiguousComplexName(); + void testFindObjectAmbiguousComplexNameUniqueAncestor(); + void testFindObjectComplexNameDifferentAncestorIfSolvingAmbiguity(); + void testFindObjectEmptyName(); void testFindObjectSingleSlash(); void testFindObjectSlashEndedName(); @@ -54,6 +65,15 @@ QObject* mObject3_1_1; QObject* mObject3_2_1_1; QAction* mAction3_3_1_1; + QObject* mAmbiguousObject5; + QObject* mAmbiguousObject8_1_2; + QObject* mAmbiguousObject10_1; + QObject* mAmbiguousObject11_1_2; + QObject* mAmbiguousObject12_1; + QObject* mAmbiguousObject12_2_3; + QObject* mObject14_1_2; + QObject* mAmbiguousObject15_2_2_1; + QObject* mAmbiguousObject16_2_1_1_1; void assertFindObject(const QString& objectName, QObject* object) const; void assertFindAction(const QString& objectName, QAction* action) const; @@ -64,6 +84,25 @@ mMainWindow = new KXmlGuiWindow(); KTutorial::self()->setup(mMainWindow); + //-Grand parent1 + // |-Parent1 + // | |-The object + // | |-The action + // |-Parent2 + // |-The object + //-Grand parent2 + // |-Parent1 + // |-The object + // |-Another object + // |-The action + // |-Another action + //-Grand parent3 + // |-Parent1 + // | |-The object + // |-Nested parent + // | |-Another object + // |-Nested timer + // |-Another action QObject* grandParent1 = new QObject(mMainWindow); grandParent1->setObjectName("Grand parent1"); QObject* parent1_1 = new QObject(grandParent1); @@ -109,6 +148,184 @@ QTimer* parent3_3_1 = new QTimer(parent3_3); mAction3_3_1_1 = new QAction(parent3_3_1); mAction3_3_1_1->setObjectName("Another action"); + + //-??? + // |-??? + // | |-Ambiguous object + // |-Ambiguous object + //-Ambiguous object + //-Object 6 + // |-Ambiguous object + // |-Object 6_2 + // |-Ambiguous object + //-Ambiguous object + QObject* unnamedObject4 = new QObject(mMainWindow); + QObject* unnamedObject4_1 = new QObject(unnamedObject4); + QObject* ambiguousObject4_1_1 = new QObject(unnamedObject4_1); + ambiguousObject4_1_1->setObjectName("Ambiguous object"); + QObject* ambiguousObject4_2 = new QObject(unnamedObject4); + ambiguousObject4_2->setObjectName("Ambiguous object"); + + mAmbiguousObject5 = new QObject(mMainWindow); + mAmbiguousObject5->setObjectName("Ambiguous object"); + + QObject* namedObject6 = new QObject(mMainWindow); + namedObject6->setObjectName("Object 6"); + QObject* ambiguousObject6_1 = new QObject(namedObject6); + ambiguousObject6_1->setObjectName("Ambiguous object"); + QObject* namedObject6_2 = new QObject(namedObject6); + namedObject6_2->setObjectName("Object 6_2"); + QObject* ambiguousObject6_2_1 = new QObject(namedObject6_2); + ambiguousObject6_2_1->setObjectName("Ambiguous object"); + + QObject* ambiguousObject7 = new QObject(mMainWindow); + ambiguousObject7->setObjectName("Ambiguous object"); + + //-??? + // |-??? + // | |-Ambiguous object2 + // | |-Ambiguous object3 + //-Object 9 + // |-Ambiguous object2 + // |-Object 9_2 + // | |-Ambiguous object2 + // |-Ambiguous object3 + //-??? + // |-Ambiguous object2 + // |-Ambiguous object2 + QObject* unnamedObject8 = new QObject(mMainWindow); + QObject* unnamedObject8_1 = new QObject(unnamedObject8); + QObject* ambiguousObject8_1_1 = new QObject(unnamedObject8_1); + ambiguousObject8_1_1->setObjectName("Ambiguous object2"); + mAmbiguousObject8_1_2 = new QObject(unnamedObject8_1); + mAmbiguousObject8_1_2->setObjectName("Ambiguous object3"); + + QObject* namedObject9 = new QObject(mMainWindow); + namedObject9->setObjectName("Object 9"); + QObject* ambiguousObject9_1 = new QObject(namedObject9); + ambiguousObject9_1->setObjectName("Ambiguous object2"); + QObject* namedObject9_2 = new QObject(namedObject9); + namedObject9_2->setObjectName("Object 9_2"); + QObject* ambiguousObject9_2_1 = new QObject(namedObject9_2); + ambiguousObject9_2_1->setObjectName("Ambiguous object2"); + QObject* ambiguousObject9_3 = new QObject(namedObject9); + ambiguousObject9_3->setObjectName("Ambiguous object3"); + + QObject* unnamedObject10 = new QObject(mMainWindow); + mAmbiguousObject10_1 = new QObject(unnamedObject10); + mAmbiguousObject10_1->setObjectName("Ambiguous object2"); + QObject* ambiguousObject10_2 = new QObject(unnamedObject10); + ambiguousObject10_2->setObjectName("Ambiguous object2"); + + //-Object 11 + // |-Object 11_1 + // |-Ambiguous object4 + // |-Ambiguous object5 + //-Object 12 + // |-Ambiguous object4 + // |-??? + // |-Ambiguous object4 + // |-Ambiguous object5 + // |-Ambiguous object6 + //-Object 13 + // |-Ambiguous object4 + // |-Object 13_2 + // |-Ambiguous object6 + QObject* namedObject11 = new QObject(mMainWindow); + namedObject11->setObjectName("Object 11"); + QObject* namedObject11_1 = new QObject(namedObject11); + namedObject11_1->setObjectName("Object 11_1"); + QObject* ambiguousObject11_1_1 = new QObject(namedObject11_1); + ambiguousObject11_1_1->setObjectName("Ambiguous object4"); + mAmbiguousObject11_1_2 = new QObject(namedObject11_1); + mAmbiguousObject11_1_2->setObjectName("Ambiguous object5"); + + QObject* namedObject12 = new QObject(mMainWindow); + namedObject12->setObjectName("Object 12"); + mAmbiguousObject12_1 = new QObject(namedObject12); + mAmbiguousObject12_1->setObjectName("Ambiguous object4"); + QObject* unnamedObject12_2 = new QObject(namedObject12); + QObject* ambiguousObject12_2_1 = new QObject(unnamedObject12_2); + ambiguousObject12_2_1->setObjectName("Ambiguous object4"); + QObject* ambiguousObject12_2_2 = new QObject(unnamedObject12_2); + ambiguousObject12_2_2->setObjectName("Ambiguous object5"); + mAmbiguousObject12_2_3 = new QObject(unnamedObject12_2); + mAmbiguousObject12_2_3->setObjectName("Ambiguous object6"); + + QObject* namedObject13 = new QObject(mMainWindow); + namedObject13->setObjectName("Object 13"); + QObject* ambiguousObject13_1 = new QObject(namedObject13); + ambiguousObject13_1->setObjectName("Ambiguous object4"); + QObject* namedObject13_2 = new QObject(namedObject13); + namedObject13_2->setObjectName("Object 13_2"); + QObject* ambiguousObject13_2_1 = new QObject(namedObject13_2); + ambiguousObject13_2_1->setObjectName("Ambiguous object6"); + + //-Object 14 + // |-Ambiguous ancestor + // |-Ambiguous object7 + // |-The object + //-??? + // |-Ambiguous ancestor + // | |-Object 15_1_1 + // | |-Ambiguous object7 + // |-Unique ancestor + // |-Object 15_2_1 + // | |-Ambiguous object7 + // |-??? + // |-Ambiguous object7 + //-??? + // |-??? + // | |-Ambiguous ancestor + // | |-Ambiguous object7 + // |-Ambiguous ancestor + // |-??? + // | |-??? + // | |-Ambiguous object7 + // |-Object 16_2_2 + // |-Ambiguous object7 + QObject* namedObject14 = new QObject(mMainWindow); + namedObject14->setObjectName("Object 14"); + QObject* ambiguousAncestor14_1 = new QObject(namedObject14); + ambiguousAncestor14_1->setObjectName("Ambiguous ancestor"); + QObject* ambiguousObject14_1_1 = new QObject(ambiguousAncestor14_1); + ambiguousObject14_1_1->setObjectName("Ambiguous object7"); + mObject14_1_2 = new QObject(ambiguousAncestor14_1); + mObject14_1_2->setObjectName("The object"); + + QObject* unnamedObject15 = new QObject(mMainWindow); + QObject* ambiguousAncestor15_1 = new QObject(unnamedObject15); + ambiguousAncestor15_1->setObjectName("Ambiguous ancestor"); + QObject* namedObject15_1_1 = new QObject(ambiguousAncestor15_1); + namedObject15_1_1->setObjectName("Object 15_1_1"); + QObject* ambiguousObject15_1_1_1 = new QObject(namedObject15_1_1); + ambiguousObject15_1_1_1->setObjectName("Ambiguous object7"); + QObject* uniqueAncestor15_2 = new QObject(unnamedObject15); + uniqueAncestor15_2->setObjectName("Unique ancestor"); + QObject* namedObject15_2_1 = new QObject(uniqueAncestor15_2); + namedObject15_2_1->setObjectName("Object 15_2_1"); + QObject* ambiguousObject15_2_1_1 = new QObject(namedObject15_2_1); + ambiguousObject15_2_1_1->setObjectName("Ambiguous object7"); + QObject* unnamedObject15_2_2 = new QObject(uniqueAncestor15_2); + mAmbiguousObject15_2_2_1 = new QObject(unnamedObject15_2_2); + mAmbiguousObject15_2_2_1->setObjectName("Ambiguous object7"); + + QObject* unnamedObject16 = new QObject(mMainWindow); + QObject* unnamedObject16_1 = new QObject(unnamedObject16); + QObject* ambiguousAncestor16_1_1 = new QObject(unnamedObject16_1); + ambiguousAncestor16_1_1->setObjectName("Ambiguous ancestor"); + QObject* ambiguousObject16_1_1_1 = new QObject(ambiguousAncestor16_1_1); + ambiguousObject16_1_1_1->setObjectName("Ambiguous object7"); + QObject* ambiguousAncestor16_2 = new QObject(unnamedObject16); + ambiguousAncestor16_2->setObjectName("Ambiguous ancestor"); + QObject* unnamedObject16_2_1 = new QObject(ambiguousAncestor16_2); + QObject* unnamedObject16_2_1_1 = new QObject(unnamedObject16_2_1); + mAmbiguousObject16_2_1_1_1 = new QObject(unnamedObject16_2_1_1); + mAmbiguousObject16_2_1_1_1->setObjectName("Ambiguous object7"); + QObject* namedObject16_2_2 = new QObject(ambiguousAncestor16_2); + namedObject16_2_2->setObjectName("Object 16_2_2"); + QObject* ambiguousObject16_2_2_1 = new QObject(namedObject16_2_2); + ambiguousObject16_2_2_1->setObjectName("Ambiguous object7"); } void KTutorialTest::cleanupTestCase() { @@ -152,6 +369,58 @@ assertFindObject("Grand parent1/Unknown parent/The object", 0); } +void KTutorialTest::testFindObjectAmbiguousSingleNameDirectChild() { + assertFindObject("Ambiguous object", mAmbiguousObject5); +} + +void KTutorialTest:: +testFindObjectAmbiguousSingleNameNestedChildUnnamedAncestors() { + assertFindObject("Ambiguous object2", mAmbiguousObject10_1); +} + +void KTutorialTest:: +testFindObjectAmbiguousSingleNameNestedChildUnnamedAncestorsDeeperThanNamed() { + assertFindObject("Ambiguous object3", mAmbiguousObject8_1_2); +} + +void KTutorialTest:: +testFindObjectAmbiguousSingleNameNestedChildNamedAncestors() { + assertFindObject("Ambiguous object4", mAmbiguousObject12_1); +} + +void KTutorialTest:: +testFindObjectAmbiguousSingleNameNestedChildNamedAncestorsSameDeepThanMixed() { + assertFindObject("Ambiguous object5", mAmbiguousObject11_1_2); +} + +void KTutorialTest:: +testFindObjectAmbiguousSingleNameNestedChildMixedAncestorsSameDeepThanNamed() { + assertFindObject("Ambiguous object6", mAmbiguousObject12_2_3); +} + +void KTutorialTest::testFindObjectAmbiguousComplexName() { + //The ancestor is selected by the rule of shallower unnamed ancestors. The + //object is selected by the rule of unnamed ancestor even if there are + //shallower objects, but with named ancestors. + assertFindObject("Ambiguous ancestor/Ambiguous object7", + mAmbiguousObject16_2_1_1_1); +} + +void KTutorialTest::testFindObjectAmbiguousComplexNameUniqueAncestor() { + //The ancestor is unique, although the object itself is ambiguous (even + //among the descendants of that unique ancestor). + assertFindObject("Unique ancestor/Ambiguous object7", + mAmbiguousObject15_2_2_1); +} + +void KTutorialTest:: +testFindObjectComplexNameDifferentAncestorIfSolvingAmbiguity() { + //The full name is unique, but if the rules to resolve ambiguity were + //applied no object would be found, as the "Ambiguous ancestor" found using + //the ambiguity resolving rules have no "The object" descendants. + assertFindObject("Ambiguous ancestor/The object", mObject14_1_2); +} + void KTutorialTest::testFindObjectEmptyName() { assertFindObject("", 0); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2012-07-03 08:59:19
|
Revision: 352 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=352&view=rev Author: danxuliu Date: 2012-07-03 08:59:08 +0000 (Tue, 03 Jul 2012) Log Message: ----------- Fix the name of the actions. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/tests/KTutorialTest.cpp Modified: trunk/ktutorial/ktutorial-library/tests/KTutorialTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/KTutorialTest.cpp 2012-06-29 16:05:44 UTC (rev 351) +++ trunk/ktutorial/ktutorial-library/tests/KTutorialTest.cpp 2012-07-03 08:59:08 UTC (rev 352) @@ -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 * @@ -56,7 +56,7 @@ QAction* mAction3_3_1_1; void assertFindObject(const QString& objectName, QObject* object) const; - void assertFindAction(const QString& objectName, QAction* widget) const; + void assertFindAction(const QString& objectName, QAction* action) const; }; @@ -71,7 +71,7 @@ mObject1_1_1 = new QObject(parent1_1); mObject1_1_1->setObjectName("The object"); mAction1_1_2 = new QAction(parent1_1); - mAction1_1_2->setObjectName("The widget"); + mAction1_1_2->setObjectName("The action"); QObject* parent1_2 = new QObject(grandParent1); parent1_2->setObjectName("Parent2"); @@ -87,9 +87,9 @@ mObject2_1_2 = new QObject(parent2_1); mObject2_1_2->setObjectName("Another object"); mAction2_1_3 = new QAction(parent2_1); - mAction2_1_3->setObjectName("The widget"); + mAction2_1_3->setObjectName("The action"); mAction2_1_4 = new QAction(parent2_1); - mAction2_1_4->setObjectName("Another widget"); + mAction2_1_4->setObjectName("Another action"); QObject* grandParent3 = new QObject(mMainWindow); grandParent3->setObjectName("Grand parent3"); @@ -108,7 +108,7 @@ parent3_3->setObjectName("Nested timer"); QTimer* parent3_3_1 = new QTimer(parent3_3); mAction3_3_1_1 = new QAction(parent3_3_1); - mAction3_3_1_1->setObjectName("Another widget"); + mAction3_3_1_1->setObjectName("Another action"); } void KTutorialTest::cleanupTestCase() { @@ -128,23 +128,23 @@ assertFindObject("Grand parent1/Parent2/The object", mObject1_2_1); assertFindObject("Grand parent2/Parent1/The object", mObject2_1_1); - assertFindAction("Grand parent1/Parent1/The widget", mAction1_1_2); - assertFindAction("Grand parent2/Parent1/The widget", mAction2_1_3); + assertFindAction("Grand parent1/Parent1/The action", mAction1_1_2); + assertFindAction("Grand parent2/Parent1/The action", mAction2_1_3); } void KTutorialTest::testFindObjectComplexNameNestedChild() { assertFindObject("Grand parent2/The object", mObject2_1_1); assertFindObject("Nested parent/Another object", mObject3_2_1_1); - assertFindAction("Grand parent2/The widget", mAction2_1_3); - assertFindAction("Nested timer/Another widget", mAction3_3_1_1); + assertFindAction("Grand parent2/The action", mAction2_1_3); + assertFindAction("Nested timer/Another action", mAction3_3_1_1); } void KTutorialTest::testFindObjectComplexNameAncestorNameNotUnique() { //The ancestor name is not unique, but the full path is assertFindObject("Parent1/Another object", mObject2_1_2); - assertFindAction("Parent1/Another widget", mAction2_1_4); + assertFindAction("Parent1/Another action", mAction2_1_4); } void KTutorialTest::testFindObjectComplexNameUnknownParent() { @@ -176,8 +176,8 @@ } void KTutorialTest::assertFindAction(const QString& objectName, - QAction* widget) const { - QCOMPARE(KTutorial::self()->findObject<QAction*>(objectName), widget); + QAction* action) const { + QCOMPARE(KTutorial::self()->findObject<QAction*>(objectName), action); } QTEST_MAIN(KTutorialTest) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2012-06-29 16:05:51
|
Revision: 351 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=351&view=rev Author: danxuliu Date: 2012-06-29 16:05:44 +0000 (Fri, 29 Jun 2012) Log Message: ----------- Add skipped failing test as reference of an unsolved bug: when setting the base window in the WindowOnTopEnforcer only the base window can be visible. If there is a modal widget or modal dialog visible (for example, if a tutorial is tested in the editor when the target application is showing a modal dialog), the window to keep on top is not reparented properly, and it appears on top only of its base window, but not on top of the modal widgets or modal dialogs. I have not found a fix for it yet. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/tests/view/WindowOnTopEnforcerTest.cpp Modified: trunk/ktutorial/ktutorial-library/tests/view/WindowOnTopEnforcerTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/view/WindowOnTopEnforcerTest.cpp 2012-06-29 15:57:55 UTC (rev 350) +++ trunk/ktutorial/ktutorial-library/tests/view/WindowOnTopEnforcerTest.cpp 2012-06-29 16:05:44 UTC (rev 351) @@ -59,6 +59,8 @@ void testShowNestedModalWidgetsInMixedOrder(); + void testSetBaseWindowAfterSeveralModalWidgetsWereShown(); + private: void queueAssertParent(QWidget* widget, QWidget* parent, int timeToWait); @@ -826,6 +828,101 @@ QVERIFY(!windowToKeepOnTop); } +//There is a limitation in the current behavior of WindowOnTopEnforcer which can +//show up as a bug when testing a tutorial from the editor. The limitation is +//that, to work properly, the base window must be set in the WindowOnTopEnforcer +//when there is only one window. If there are modal widgets or modal dialogs the +//reparenting does not work as it should, and the widget to keep on top of the +//others is only kept on top of the main window, but below the modal widgets or +//modal dialogs. This can happen, for example, if a tutorial is tested in the +//edtior when the target application is showing a modal dialog. +//The reason is that the reparenting system works using a stack that stores +//the order in which modal widgets or modal dialogs were opened, so it can +//reparent them back to the proper one when they are hidden. That stack is built +//once the base window was set by spying the show and hidden events of its child +//windows. However, I have not found any way to initialize it with the proper +//order of windows if they are already visible when the base window is set. +//The modal stack is stored by Qt in the variable QWidgetList* qt_modal_stack +//(declared in qapplication.cpp). Alas, this variable seems to be not exported, +//so it can not be used to initialize the WindowOnTopEnforcer stack. +//I have tried another approach spying WindowBlocked and WindowUnblocked events +//and not using a stack at all, but I was not able to come with anything that +//worked in all the cases. +//So... I just keep this test here as a reference of something that should be +//fixed, but that unfortunately I do not know how to do it :( +void WindowOnTopEnforcerTest:: + testSetBaseWindowAfterSeveralModalWidgetsWereShown() { + QSKIP("Pending bug: WindowOnTopEnforcer does not work properly if there \ +are modal widgets already shown when the base window is set. However, I have \ +not found a fix for it yet :(", SkipAll); + + QWidget* window = new QWidget(); + window->setWindowTitle("Window"); + window->show(); + + QPointer<QWidget> windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowTitle("Window to keep on top"); + windowToKeepOnTop->setWindowFlags(Qt::Dialog); + windowToKeepOnTop->show(); + + QWidget* siblingModalWidget = new QWidget(window); + siblingModalWidget->setWindowTitle("Sibling modal widget"); + siblingModalWidget->setWindowFlags(Qt::Window); + siblingModalWidget->setWindowModality(Qt::ApplicationModal); + siblingModalWidget->show(); + + QWidget* modalWidget = new QWidget(window); + modalWidget->setWindowTitle("Modal widget"); + modalWidget->setWindowFlags(Qt::Window); + modalWidget->setWindowModality(Qt::ApplicationModal); + modalWidget->show(); + + QWidget* nestedModalWidget = new QWidget(window); + nestedModalWidget->setWindowTitle("Nested modal widget"); + nestedModalWidget->setWindowFlags(Qt::Window); + nestedModalWidget->setWindowModality(Qt::ApplicationModal); + nestedModalWidget->show(); + + QWidget* nestedSiblingModalWidget = new QWidget(siblingModalWidget); + nestedSiblingModalWidget->setWindowTitle("Nested sibling modal widget"); + nestedSiblingModalWidget->setWindowFlags(Qt::Window); + nestedSiblingModalWidget->setWindowModality(Qt::ApplicationModal); + nestedSiblingModalWidget->show(); + + WindowOnTopEnforcer* enforcer = new WindowOnTopEnforcer(windowToKeepOnTop); + enforcer->setBaseWindow(window); + + assertWindow(windowToKeepOnTop, nestedSiblingModalWidget); + + nestedSiblingModalWidget->hide(); + + assertWindow(windowToKeepOnTop, nestedModalWidget); + + nestedSiblingModalWidget->show(); + + assertWindow(windowToKeepOnTop, nestedSiblingModalWidget); + + delete nestedSiblingModalWidget; + + assertWindow(windowToKeepOnTop, nestedModalWidget); + + delete nestedModalWidget; + + assertWindow(windowToKeepOnTop, modalWidget); + + delete modalWidget; + + assertWindow(windowToKeepOnTop, siblingModalWidget); + + delete siblingModalWidget; + + assertWindow(windowToKeepOnTop, window); + + delete window; + + QVERIFY(!windowToKeepOnTop); +} + /////////////////////////////////// Helpers //////////////////////////////////// //The dialogs are modal, so they won't return to the test code until they are This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2012-06-29 15:58:06
|
Revision: 350 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=350&view=rev Author: danxuliu Date: 2012-06-29 15:57:55 +0000 (Fri, 29 Jun 2012) Log Message: ----------- Set the window title to widgets and dialogs to ease debugging failures. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/tests/view/WindowOnTopEnforcerTest.cpp Modified: trunk/ktutorial/ktutorial-library/tests/view/WindowOnTopEnforcerTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/view/WindowOnTopEnforcerTest.cpp 2012-06-27 21:37:57 UTC (rev 349) +++ trunk/ktutorial/ktutorial-library/tests/view/WindowOnTopEnforcerTest.cpp 2012-06-29 15:57:55 UTC (rev 350) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 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 * @@ -77,9 +77,11 @@ void WindowOnTopEnforcerTest::testBaseWindow() { QWidget* window = new QWidget(); + window->setWindowTitle("Window"); window->show(); QPointer<QWidget> windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowTitle("Window to keep on top"); windowToKeepOnTop->setWindowFlags(Qt::Window); windowToKeepOnTop->show(); @@ -103,9 +105,11 @@ void WindowOnTopEnforcerTest::testChildWindow() { QWidget* window = new QWidget(); + window->setWindowTitle("Window"); window->show(); QPointer<QWidget> windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowTitle("Window to keep on top"); windowToKeepOnTop->setWindowFlags(Qt::Window); windowToKeepOnTop->show(); @@ -113,6 +117,7 @@ enforcer->setBaseWindow(window); QWidget* childWindow = new QWidget(window); + childWindow->setWindowTitle("Child window"); childWindow->setWindowFlags(Qt::Window); childWindow->show(); @@ -137,9 +142,11 @@ void WindowOnTopEnforcerTest::testModalDialog() { QWidget* window = new QWidget(); + window->setWindowTitle("Window"); window->show(); QPointer<QWidget> windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowTitle("Window to keep on top"); windowToKeepOnTop->setWindowFlags(Qt::Window); windowToKeepOnTop->show(); @@ -147,6 +154,7 @@ enforcer->setBaseWindow(window); QDialog* modalDialog = new QDialog(window); + modalDialog->setWindowTitle("Modal dialog"); QTimer timerAccept; timerAccept.setSingleShot(true); @@ -168,9 +176,11 @@ void WindowOnTopEnforcerTest::testNestedModalDialog() { QWidget* window = new QWidget(); + window->setWindowTitle("Window"); window->show(); QPointer<QWidget> windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowTitle("Window to keep on top"); windowToKeepOnTop->setWindowFlags(Qt::Window); windowToKeepOnTop->show(); @@ -178,7 +188,9 @@ enforcer->setBaseWindow(window); QDialog* modalDialog = new QDialog(window); + modalDialog->setWindowTitle("Modal dialog"); QDialog* nestedModalDialog = new QDialog(modalDialog); + nestedModalDialog->setWindowTitle("Nested modal dialog"); QTimer timerAccept; timerAccept.setSingleShot(true); @@ -216,9 +228,11 @@ void WindowOnTopEnforcerTest::testSeveralModalDialogs() { QWidget* window = new QWidget(); + window->setWindowTitle("Window"); window->show(); QPointer<QWidget> windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowTitle("Window to keep on top"); windowToKeepOnTop->setWindowFlags(Qt::Window); windowToKeepOnTop->show(); @@ -226,8 +240,11 @@ enforcer->setBaseWindow(window); QDialog* modalDialog = new QDialog(window); + modalDialog->setWindowTitle("Modal dialog"); QDialog* nestedModalDialog = new QDialog(modalDialog); + nestedModalDialog->setWindowTitle("Nested modal dialog"); QDialog* nestedModalDialog2 = new QDialog(modalDialog); + nestedModalDialog2->setWindowTitle("Nested modal dialog 2"); QTimer timerAccept; timerAccept.setSingleShot(true); @@ -277,7 +294,9 @@ assertWindow(windowToKeepOnTop, window); QDialog* modalDialog2 = new QDialog(window); + modalDialog2->setWindowTitle("Modal dialog 2"); QDialog* nestedModalDialog3 = new QDialog(modalDialog); + nestedModalDialog3->setWindowTitle("Nested modal dialog 3"); QTimer timerAccept2; timerAccept2.setSingleShot(true); @@ -315,9 +334,11 @@ void WindowOnTopEnforcerTest::testNestedModalDialogOnChildWindow() { QWidget* window = new QWidget(); + window->setWindowTitle("Window"); window->show(); QPointer<QWidget> windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowTitle("Window to keep on top"); windowToKeepOnTop->setWindowFlags(Qt::Window); windowToKeepOnTop->show(); @@ -325,10 +346,12 @@ enforcer->setBaseWindow(window); QWidget* childWindow = new QWidget(window); + childWindow->setWindowTitle("Child window"); childWindow->setWindowFlags(Qt::Window); childWindow->show(); QDialog* nestedModalDialog = new QDialog(childWindow); + nestedModalDialog->setWindowTitle("Nested modal dialog"); QTimer timerAccept; timerAccept.setSingleShot(true); @@ -350,9 +373,11 @@ void WindowOnTopEnforcerTest::testModalWidget() { QWidget* window = new QWidget(); + window->setWindowTitle("Window"); window->show(); QPointer<QWidget> windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowTitle("Window to keep on top"); windowToKeepOnTop->setWindowFlags(Qt::Window); windowToKeepOnTop->show(); @@ -360,6 +385,7 @@ enforcer->setBaseWindow(window); QWidget* modalWidget = new QWidget(window); + modalWidget->setWindowTitle("Modal widget"); modalWidget->setWindowFlags(Qt::Window); modalWidget->setWindowModality(Qt::ApplicationModal); modalWidget->show(); @@ -397,9 +423,11 @@ //Hopefully I'll get enough information about this weird behavior to fill a bug. void WindowOnTopEnforcerTest::testModalWidgetSiblingOfParent() { QWidget* window = new QWidget(); + window->setWindowTitle("Window"); window->show(); QPointer<QWidget> windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowTitle("Window to keep on top"); windowToKeepOnTop->setWindowFlags(Qt::Window); windowToKeepOnTop->show(); @@ -407,11 +435,13 @@ enforcer->setBaseWindow(window); QWidget* siblingModalWidget = new QWidget(window); + siblingModalWidget->setWindowTitle("Sibling modal widget"); siblingModalWidget->setWindowFlags(Qt::Window); siblingModalWidget->setWindowModality(Qt::ApplicationModal); siblingModalWidget->show(); QWidget* modalWidget = new QWidget(window); + modalWidget->setWindowTitle("Modal widget"); modalWidget->setWindowFlags(Qt::Window); modalWidget->setWindowModality(Qt::ApplicationModal); modalWidget->show(); @@ -478,9 +508,11 @@ //This test is a synthesized version of all the things explained above. void WindowOnTopEnforcerTest::testShowModalWidgetTwice() { QWidget* window = new QWidget(); + window->setWindowTitle("Window"); window->show(); QPointer<QWidget> windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowTitle("Window to keep on top"); windowToKeepOnTop->setWindowFlags(Qt::Window); windowToKeepOnTop->show(); @@ -488,6 +520,7 @@ enforcer->setBaseWindow(window); QWidget* modalWidget = new QWidget(window); + modalWidget->setWindowTitle("Modal widget"); modalWidget->setWindowFlags(Qt::Window); modalWidget->setWindowModality(Qt::ApplicationModal); modalWidget->show(); @@ -508,9 +541,11 @@ void WindowOnTopEnforcerTest::testHideNestedModalWidgetsInOrder() { QWidget* window = new QWidget(); + window->setWindowTitle("Window"); window->show(); QPointer<QWidget> windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowTitle("Window to keep on top"); windowToKeepOnTop->setWindowFlags(Qt::Window); windowToKeepOnTop->show(); @@ -518,11 +553,13 @@ enforcer->setBaseWindow(window); QWidget* modalWidget = new QWidget(window); + modalWidget->setWindowTitle("Modal widget"); modalWidget->setWindowFlags(Qt::Window); modalWidget->setWindowModality(Qt::ApplicationModal); modalWidget->show(); QWidget* nestedModalWidget = new QWidget(modalWidget); + nestedModalWidget->setWindowTitle("Nested modal widget"); nestedModalWidget->setWindowFlags(Qt::Window); nestedModalWidget->setWindowModality(Qt::ApplicationModal); nestedModalWidget->show(); @@ -548,9 +585,11 @@ void WindowOnTopEnforcerTest::testHideNestedModalWidgetsInReverseOrder() { QWidget* window = new QWidget(); + window->setWindowTitle("Window"); window->show(); QPointer<QWidget> windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowTitle("Window to keep on top"); windowToKeepOnTop->setWindowFlags(Qt::Window); windowToKeepOnTop->show(); @@ -558,11 +597,13 @@ enforcer->setBaseWindow(window); QWidget* modalWidget = new QWidget(window); + modalWidget->setWindowTitle("Modal widget"); modalWidget->setWindowFlags(Qt::Window); modalWidget->setWindowModality(Qt::ApplicationModal); modalWidget->show(); QWidget* nestedModalWidget = new QWidget(modalWidget); + nestedModalWidget->setWindowTitle("Nested modal widget"); nestedModalWidget->setWindowFlags(Qt::Window); nestedModalWidget->setWindowModality(Qt::ApplicationModal); nestedModalWidget->show(); @@ -588,9 +629,11 @@ void WindowOnTopEnforcerTest::testShowNestedModalWidgetsInOrder() { QWidget* window = new QWidget(); + window->setWindowTitle("Window"); window->show(); QPointer<QWidget> windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowTitle("Window to keep on top"); windowToKeepOnTop->setWindowFlags(Qt::Window); windowToKeepOnTop->show(); @@ -598,11 +641,13 @@ enforcer->setBaseWindow(window); QWidget* modalWidget = new QWidget(window); + modalWidget->setWindowTitle("Modal widget"); modalWidget->setWindowFlags(Qt::Window); modalWidget->setWindowModality(Qt::ApplicationModal); modalWidget->show(); QWidget* nestedModalWidget = new QWidget(modalWidget); + nestedModalWidget->setWindowTitle("Nested modal widget"); nestedModalWidget->setWindowFlags(Qt::Window); nestedModalWidget->setWindowModality(Qt::ApplicationModal); nestedModalWidget->show(); @@ -640,9 +685,11 @@ void WindowOnTopEnforcerTest::testShowNestedModalWidgetsInReverseOrder() { QWidget* window = new QWidget(); + window->setWindowTitle("Window"); window->show(); QPointer<QWidget> windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowTitle("Window to keep on top"); windowToKeepOnTop->setWindowFlags(Qt::Window); windowToKeepOnTop->show(); @@ -650,11 +697,13 @@ enforcer->setBaseWindow(window); QWidget* modalWidget = new QWidget(window); + modalWidget->setWindowTitle("Modal widget"); modalWidget->setWindowFlags(Qt::Window); modalWidget->setWindowModality(Qt::ApplicationModal); modalWidget->show(); QWidget* nestedModalWidget = new QWidget(modalWidget); + nestedModalWidget->setWindowTitle("Nested modal widget"); nestedModalWidget->setWindowFlags(Qt::Window); nestedModalWidget->setWindowModality(Qt::ApplicationModal); nestedModalWidget->show(); @@ -692,9 +741,11 @@ void WindowOnTopEnforcerTest::testShowNestedModalWidgetsInMixedOrder() { QWidget* window = new QWidget(); + window->setWindowTitle("Window"); window->show(); QPointer<QWidget> windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowTitle("Window to keep on top"); windowToKeepOnTop->setWindowFlags(Qt::Window); windowToKeepOnTop->show(); @@ -702,22 +753,26 @@ enforcer->setBaseWindow(window); QWidget* modalWidget = new QWidget(window); + modalWidget->setWindowTitle("Modal widget"); modalWidget->setWindowFlags(Qt::Window); modalWidget->setWindowModality(Qt::ApplicationModal); modalWidget->show(); QWidget* nestedModalWidget = new QWidget(modalWidget); + nestedModalWidget->setWindowTitle("Nested modal widget"); nestedModalWidget->setWindowFlags(Qt::Window); nestedModalWidget->setWindowModality(Qt::ApplicationModal); nestedModalWidget->show(); QWidget* nestedNestedModalWidget = new QWidget(nestedModalWidget); + nestedNestedModalWidget->setWindowTitle("Nested nested modal widget"); nestedNestedModalWidget->setWindowFlags(Qt::Window); nestedNestedModalWidget->setWindowModality(Qt::ApplicationModal); nestedNestedModalWidget->show(); QWidget* nestedNestedNestedModalWidget = new QWidget(nestedNestedModalWidget); + nestedNestedNestedModalWidget->setWindowTitle("Nested nested nested modal widget"); nestedNestedNestedModalWidget->setWindowFlags(Qt::Window); nestedNestedNestedModalWidget->setWindowModality(Qt::ApplicationModal); nestedNestedNestedModalWidget->show(); @@ -794,7 +849,12 @@ public slots: void assertParent() { - QCOMPARE(mAssertWidget->parentWidget(), mAssertParent); + QString message = "\nActual: " + + mAssertWidget->parentWidget()->windowTitle() + + "\nExpected: " + mAssertParent->windowTitle() + + "\n"; + QVERIFY2(mAssertWidget->parentWidget() == mAssertParent, + message.toStdString().c_str()); } void assertIsVisibleWindow() { @@ -828,7 +888,10 @@ void WindowOnTopEnforcerTest::assertWindow(QWidget* window, QWidget* expectedParent) const { QVERIFY(window); - QCOMPARE(window->parentWidget(), expectedParent); + QString message = "\nActual: " + window->parentWidget()->windowTitle() + + "\nExpected: " + expectedParent->windowTitle() + "\n"; + QVERIFY2(window->parentWidget() == expectedParent, + message.toStdString().c_str()); QVERIFY(window->isVisible()); QVERIFY(window->windowFlags() & Qt::Window); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2012-06-27 21:38:04
|
Revision: 349 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=349&view=rev Author: danxuliu Date: 2012-06-27 21:37:57 +0000 (Wed, 27 Jun 2012) Log Message: ----------- Fix a bug when trying to start a target application that uses DBus but does not use Qt. I don't know why, but those applications (for example, Audacity) do not return an error when calling "Introspect" on "/ktutorial" (but they should, as they do not provide the "/ktutorial" DBus object). Instead, they return just an empty node list. So, instead of just checking if there is no error, now it is checked if there is no error and if the string "org.kde.ktutorial.EditorSupport" is found in the reply message (as it is the interface implemented by "/ktutorial"). Unfortunately, no unit test is provided for this problem, as I don't know how to implement a TargetApplicationStub with the behavior shown by those applications. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/targetapplication/TargetApplication.cpp Modified: trunk/ktutorial/ktutorial-editor/src/targetapplication/TargetApplication.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/TargetApplication.cpp 2012-06-27 16:49:40 UTC (rev 348) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/TargetApplication.cpp 2012-06-27 21:37:57 UTC (rev 349) @@ -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 * @@ -168,7 +168,8 @@ //Just a generic DBus call to check whether /ktutorial is already available //or not QDBusReply<QString> reply = interface.call("Introspect"); - if (!reply.isValid()) { + if (!reply.isValid() || + !reply.value().contains("org.kde.ktutorial.EditorSupport")) { QTimer::singleShot(500, this, SLOT(checkKTutorialSupportModule())); return; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2012-06-27 16:49:51
|
Revision: 348 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=348&view=rev Author: danxuliu Date: 2012-06-27 16:49:40 +0000 (Wed, 27 Jun 2012) Log Message: ----------- Fix stopping the highlighting of a widget when a context menu was just shown for the link to another widget, even if the action to highlight the other widget was not activated. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.cpp trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.h trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp Modified: trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.cpp 2012-06-27 11:35:47 UTC (rev 347) +++ trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.cpp 2012-06-27 16:49:40 UTC (rev 348) @@ -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 * @@ -34,8 +34,8 @@ StepTextWidget::StepTextWidget(QWidget* parent /*= 0*/): KTextEdit(parent), - mCurrentWidget(0), - mCurrentWidgetIsBeingHighlighted(false) { + mLastReferencedWidget(0), + mWidgetBeingHighlighted(0) { setReadOnly(true); setFrameShape(QFrame::NoFrame); setFrameShadow(QFrame::Plain); @@ -61,13 +61,13 @@ } StepTextWidget::~StepTextWidget() { - if (mCurrentWidgetIsBeingHighlighted) { + if (mWidgetBeingHighlighted) { stopHighlightingCurrentWidget(); } } bool StepTextWidget::eventFilter(QObject* watched, QEvent* event) { - if (watched != mCurrentWidget) { + if (watched != mWidgetBeingHighlighted) { return false; } @@ -107,16 +107,10 @@ QMenu* menu = new QMenu(this); - if (mCurrentWidgetIsBeingHighlighted && - mCurrentWidget != widgetForAnchor(anchor)) { - stopHighlightingCurrentWidget(); - } - - if (!mCurrentWidgetIsBeingHighlighted) { + mLastReferencedWidget = widgetForAnchor(anchor); + if (mLastReferencedWidget != mWidgetBeingHighlighted) { menu->addAction(i18nc("@item:inmenu", "Highlight"), - this, SLOT(highlightCurrentWidget())); - - mCurrentWidget = widgetForAnchor(anchor); + this, SLOT(highlightLastReferencedWidget())); } else { menu->addAction(i18nc("@item:inmenu", "Stop highlighting"), this, SLOT(stopHighlightingCurrentWidget())); @@ -144,15 +138,9 @@ return; } - if (mCurrentWidgetIsBeingHighlighted && - mCurrentWidget != widgetForAnchor(anchor)) { - stopHighlightingCurrentWidget(); - } - - if (!mCurrentWidgetIsBeingHighlighted) { - mCurrentWidget = widgetForAnchor(anchor); - - highlightCurrentWidget(); + mLastReferencedWidget = widgetForAnchor(anchor); + if (mLastReferencedWidget != mWidgetBeingHighlighted) { + highlightLastReferencedWidget(); } else { stopHighlightingCurrentWidget(); } @@ -186,36 +174,37 @@ void StepTextWidget::updateText() { updateGeometry(); - if (mCurrentWidgetIsBeingHighlighted) { + if (mWidgetBeingHighlighted) { stopHighlightingCurrentWidget(); } } -void StepTextWidget::highlightCurrentWidget() { - if (!mCurrentWidget) { +void StepTextWidget::highlightLastReferencedWidget() { + if (!mLastReferencedWidget) { kWarning() << "The widget to highlight was not found!"; return; } - WidgetHighlighterManager::self()->highlight(mCurrentWidget); + if (mWidgetBeingHighlighted) { + stopHighlightingCurrentWidget(); + } - mCurrentWidget->installEventFilter(this); + WidgetHighlighterManager::self()->highlight(mLastReferencedWidget); - mCurrentWidgetIsBeingHighlighted = true; + mLastReferencedWidget->installEventFilter(this); + mWidgetBeingHighlighted = mLastReferencedWidget; } void StepTextWidget::stopHighlightingCurrentWidget() { - if (!mCurrentWidget) { + if (!mWidgetBeingHighlighted) { kWarning() << "The widget to stop highlighting was not found!"; return; } - WidgetHighlighterManager::self()->stopHighlighting(mCurrentWidget); + WidgetHighlighterManager::self()->stopHighlighting(mWidgetBeingHighlighted); - mCurrentWidget->removeEventFilter(this); - mCurrentWidget = 0; - - mCurrentWidgetIsBeingHighlighted = false; + mWidgetBeingHighlighted->removeEventFilter(this); + mWidgetBeingHighlighted = 0; } } Modified: trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.h 2012-06-27 11:35:47 UTC (rev 347) +++ trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.h 2012-06-27 16:49:40 UTC (rev 348) @@ -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 * @@ -109,8 +109,7 @@ * "widget:" anchor. * The context menu will contain a "Stop highlighting" or a "Highlight" item * depending on whether the widget is the one currently being highlighted or - * not. If the widget currently being highlighted is another one, it is - * stopped. + * not. * * @param event The context menu event. */ @@ -126,7 +125,6 @@ /** * When the mouse is pressed on a "widget:" anchor, it is highlighted or * stopped being highlighted. - * If the widget currently being highlighted is another one, it is stopped. * * @param event The mouse press event. */ @@ -138,12 +136,12 @@ * The last widget referenced by a link activated or selected in this * StepTextWidget. */ - QPointer<QWidget> mCurrentWidget; + QPointer<QWidget> mLastReferencedWidget; /** - * Whether the current widget is being highlighted or not. + * The widget being highlighted. */ - bool mCurrentWidgetIsBeingHighlighted; + QPointer<QWidget> mWidgetBeingHighlighted; /** * Returns the size for the given text width. @@ -172,18 +170,18 @@ /** * Notifies the layout that the geometry of the widget has changed and stops - * the highlighting of the current widget, if any. + * the widget currently being highlighted, if any. */ void updateText(); /** - * Starts highlighting the current widget. + * Starts highlighting the last referenced widget. + * If there is any widget currently being highlighted, it is stopped. */ - void highlightCurrentWidget(); + void highlightLastReferencedWidget(); /** - * Stops highlighting the current widget. - * The current widget is cleared. + * Stops highlighting the widget currently being highlighted. */ void stopHighlightingCurrentWidget(); Modified: trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp 2012-06-27 11:35:47 UTC (rev 347) +++ trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp 2012-06-27 16:49:40 UTC (rev 348) @@ -51,6 +51,7 @@ void testHighlightWidgetUsingContextMenu(); void testStopHighlightingWidgetUsingContextMenu(); void testCancelContextMenu(); + void testCancelContextMenuOnOtherLink(); void testHighlightSeveralWidgets(); void testHighlightUnknownWidget(); @@ -232,11 +233,46 @@ showContextMenuCheckFirstOptionAndCancel(widget, position, i18n("Stop highlighting")); + //Ensure that just showing the context menu does not stop the highlighting + QVERIFY(!waitForHighlighterToStop(widgetToHighlight, 1000)); + //Ensure that the option text is the right one after cancelling the menu showContextMenuCheckFirstOptionAndCancel(widget, position, i18n("Stop highlighting")); } +void StepTextWidgetTest::testCancelContextMenuOnOtherLink() { + KXmlGuiWindow mainWindow; + KTutorial::self()->setup(&mainWindow); + QWidget* widgetToHighlight1 = new QWidget(&mainWindow); + widgetToHighlight1->setObjectName("widget1"); + QWidget* widgetToHighlight2 = new QWidget(&mainWindow); + widgetToHighlight2->setObjectName("widget2"); + + StepTextWidget widget; + widget.setText("The <a href=\"widget:widget1\">first widget</a> and the " +"<a href=\"widget:widget2\">second widget</a>"); + widget.show(); + + QTest::qWaitForWindowShown(&widget); + + QPoint position1 = centerOfText(widget, "first widget"); + QPoint position2 = centerOfText(widget, "second widget"); + + showContextMenuAndSelectFirstOption(widget, position1); + + showContextMenuCheckFirstOptionAndCancel(widget, position2, + i18n("Highlight")); + + //Ensure that just showing the context menu on the second link does not stop + //the highlighting of the first widget + QVERIFY(!waitForHighlighterToStop(widgetToHighlight1, 1000)); + + //Ensure that the option text is the right one after cancelling the menu + showContextMenuCheckFirstOptionAndCancel(widget, position2, + i18n("Highlight")); +} + void StepTextWidgetTest::testHighlightSeveralWidgets() { KXmlGuiWindow mainWindow; KTutorial::self()->setup(&mainWindow); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2012-06-27 11:35:58
|
Revision: 347 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=347&view=rev Author: danxuliu Date: 2012-06-27 11:35:47 +0000 (Wed, 27 Jun 2012) Log Message: ----------- Set the text area window in the UsingKTutorial tutorial to stay on top. This prevents the text area to be hidden when the user clicks on the "continue" button if the application that shows the tutorial is maximized (as when the button is clicked the tutorial widget gets the focus, and the main application window hides the text area window). Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/tutorials/UsingKTutorial.cpp Modified: trunk/ktutorial/ktutorial-library/src/tutorials/UsingKTutorial.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/tutorials/UsingKTutorial.cpp 2012-06-27 09:34:46 UTC (rev 346) +++ trunk/ktutorial/ktutorial-library/src/tutorials/UsingKTutorial.cpp 2012-06-27 11:35:47 UTC (rev 347) @@ -62,7 +62,7 @@ virtual void setup() { QTextEdit* textEdit = new QTextEdit(KTutorial::self()->parentWidget()); textEdit->setAttribute(Qt::WA_DeleteOnClose); - textEdit->setWindowFlags(Qt::Window); + textEdit->setWindowFlags(Qt::Window | Qt::WindowStaysOnTopHint); textEdit->setObjectName("usingKTutorialTextEdit"); textEdit->setText(i18nc("@info/plain Plain text in a QTextEdit", "Look at me! I am the text area!")); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2012-06-27 09:34:57
|
Revision: 346 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=346&view=rev Author: danxuliu Date: 2012-06-27 09:34:46 +0000 (Wed, 27 Jun 2012) Log Message: ----------- Remove unused code. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/TargetApplicationTest.cpp Modified: trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/TargetApplicationTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/TargetApplicationTest.cpp 2012-06-26 22:14:40 UTC (rev 345) +++ trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/TargetApplicationTest.cpp 2012-06-27 09:34:46 UTC (rev 346) @@ -34,18 +34,6 @@ class TargetApplicationTest: public QObject { Q_OBJECT -public slots: - - void adaptStartFailedSignal(TargetApplication::Error error) { - qDebug() << "Error: " << error; - qDebug() << "Int: " << (int)error; - emit startFailed((int)error); - } - -Q_SIGNALS: - - void startFailed(int error); - private slots: void initTestCase(); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2012-06-26 22:14:46
|
Revision: 345 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=345&view=rev Author: danxuliu Date: 2012-06-26 22:14:40 +0000 (Tue, 26 Jun 2012) Log Message: ----------- Include the name of the signal when reporting that the object that emits the signal to wait for is null. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/WaitForSignal.cpp Modified: trunk/ktutorial/ktutorial-library/src/WaitForSignal.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/WaitForSignal.cpp 2012-06-26 22:12:09 UTC (rev 344) +++ trunk/ktutorial/ktutorial-library/src/WaitForSignal.cpp 2012-06-26 22:14:40 UTC (rev 345) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2008-2010 by Daniel Calviño Sánchez * + * Copyright (C) 2008-2012 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -33,7 +33,8 @@ void WaitForSignal::setSignal(QObject* sender, const QString& signal) { if (!sender) { - kWarning() << "The object that emits the signal to wait for is null!"; + kWarning() << "The object that emits the signal" << signal + << "to wait for is null!"; return; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2012-06-26 22:12:16
|
Revision: 344 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=344&view=rev Author: danxuliu Date: 2012-06-26 22:12:09 +0000 (Tue, 26 Jun 2012) Log Message: ----------- Change the way that actions are enqueued in RemoteObjectChooserTest to prevent the tests from failing in slow systems. Instead of queueing and executing the actions after a fixed amount of time, it is checked if the action can be performed and, if not, the action is retried again after a little amount of time. The retries continue until the timeout expires. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectChooserTest.cpp Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectChooserTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectChooserTest.cpp 2012-06-26 21:53:28 UTC (rev 343) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectChooserTest.cpp 2012-06-26 22:12:09 UTC (rev 344) @@ -78,10 +78,13 @@ QString mPath; void closeInformationMessageBox(int timeToWait); + void closeInformationMessageBoxOnceTargetApplicationEnds(int timeToWait); void closeSorryMessageBox(QWidget* widget, int timeToWait); - void killTargetApplication(int timeToWait); + void killTargetApplicationOnceMessageBoxIsShown(int timeToWait); + bool waitForSorryMessageBoxToBeClosed(QWidget* widget, int timeout); + bool waitForTargetApplicationToStart(int timeout) const; bool waitForTargetApplicationToStop(int timeout) const; @@ -135,7 +138,7 @@ dialog->show(); //Queue closing the information message box - closeInformationMessageBox(1000); + closeInformationMessageBox(10000); RemoteObjectChooser* chooser = new RemoteObjectChooser(dialog); chooser->show(); @@ -162,7 +165,7 @@ dialog->show(); //Queue closing the information message box - closeInformationMessageBox(1000); + closeInformationMessageBox(10000); RemoteObjectChooser* chooser = new RemoteObjectChooser(dialog); chooser->show(); @@ -186,12 +189,10 @@ QPointer<RemoteObjectChooser> chooser = new RemoteObjectChooser(dialog); chooser->show(); - //Queue closing the sorry message box - closeSorryMessageBox(chooser, 1000); + //The sorry message box will be shown and then closed once the target + //application fails to start + QVERIFY(waitForSorryMessageBoxToBeClosed(chooser, 10000)); - //Give the target application time to fail to start - QTest::qWait(1000); - //Process deleteLater() QCoreApplication::sendPostedEvents(chooser, QEvent::DeferredDelete); @@ -213,12 +214,10 @@ QPointer<RemoteObjectChooser> chooser = new RemoteObjectChooser(dialog); chooser->show(); - //Queue closing the sorry message box - closeSorryMessageBox(chooser, 3500); + //The sorry message box will be shown and then closed once the target + //application fails to start + QVERIFY(waitForSorryMessageBoxToBeClosed(chooser, 10000)); - //Give the target application time to fail to start - QTest::qWait(3500); - //Process deleteLater() QCoreApplication::sendPostedEvents(chooser, QEvent::DeferredDelete); @@ -236,14 +235,14 @@ dialog->show(); //Queue closing the information message box - closeInformationMessageBox(1000); + closeInformationMessageBox(10000); QPointer<RemoteObjectChooser> chooser = new RemoteObjectChooser(dialog); chooser->show(); QVERIFY(waitForTargetApplicationToStart(10000)); //Queue closing the sorry message box - closeSorryMessageBox(chooser, 1000); + closeSorryMessageBox(chooser, 10000); TargetApplication::self()->mProcess->kill(); @@ -266,27 +265,20 @@ QWidget* dialog = new QWidget(&window, Qt::Dialog); dialog->show(); + //Queue killing the target application once the message box is shown + killTargetApplicationOnceMessageBoxIsShown(10000); + + //Queue closing the information message box once the target application was + //killed + closeInformationMessageBoxOnceTargetApplicationEnds(10000); + QPointer<RemoteObjectChooser> chooser = new RemoteObjectChooser(dialog); chooser->show(); - //All the queuing has to be done before the qWait, because the killing must - //be done once the information message is already shown, and the sorry - //message appears just after the information message is closed (and it may - //appear before the wait ends, so queuing it after the wait could not work). + //The sorry message box will be shown and then closed once the information + //message box is closed + QVERIFY(waitForSorryMessageBoxToBeClosed(chooser, 10000)); - //Queue killing the target application - killTargetApplication(1000); - - //Queue closing the information message box - closeInformationMessageBox(2000); - - //Queue closing the sorry message box - closeSorryMessageBox(chooser, 3000); - - //Give the target application time to start and be killed, and to close the - //sorry message box - QTest::qWait(3000); - //Process deleteLater() QCoreApplication::sendPostedEvents(chooser, QEvent::DeferredDelete); @@ -304,7 +296,7 @@ dialog->show(); //Queue closing the information message box - closeInformationMessageBox(1000); + closeInformationMessageBox(10000); QPointer<RemoteObjectChooser> chooser = new RemoteObjectChooser(dialog); chooser->show(); @@ -325,7 +317,7 @@ QWidget window(0, Qt::Window); //Queue closing the information message box - closeInformationMessageBox(1000); + closeInformationMessageBox(10000); RemoteObjectChooser* chooser = new RemoteObjectChooser(&window); chooser->show(); @@ -350,7 +342,7 @@ QWidget window(0, Qt::Window); //Queue closing the information message box - closeInformationMessageBox(1000); + closeInformationMessageBox(10000); RemoteObjectChooser* chooser = new RemoteObjectChooser(&window); chooser->show(); @@ -378,7 +370,7 @@ QWidget window(0, Qt::Window); //Queue closing the information message box - closeInformationMessageBox(1000); + closeInformationMessageBox(10000); RemoteObjectChooser* chooser = new RemoteObjectChooser(&window); chooser->show(); @@ -414,7 +406,7 @@ QWidget window(0, Qt::Window); //Queue closing the information message box - closeInformationMessageBox(1000); + closeInformationMessageBox(10000); RemoteObjectChooser* chooser = new RemoteObjectChooser(&window); chooser->show(); @@ -461,7 +453,7 @@ dialog->show(); //Queue closing the information message box - closeInformationMessageBox(1000); + closeInformationMessageBox(10000); QPointer<RemoteObjectChooser> chooser = new RemoteObjectChooser(dialog); chooser->show(); @@ -505,7 +497,7 @@ dialog->show(); //Queue closing the information message box - closeInformationMessageBox(1000); + closeInformationMessageBox(10000); QPointer<RemoteObjectChooser> chooser = new RemoteObjectChooser(dialog); chooser->show(); @@ -537,76 +529,235 @@ //The message boxes are modal, so they won't return to the test code until they //are closed. Thus, the actions to be performed while the message boxes are //being shown (like closing the message boxes themselves) must be "queued". -class QueuedActionsHelper: public QObject { +//Instead of queueing them to be performed after some fixed amount of time, +//it is checked if the action can be performed and, if not, the action is +//retried again after a little amount of time. The retries continue until the +//timeout expires. +class QueuedAction: public QObject { Q_OBJECT public: - QueuedActionsHelper(QObject* parent = 0): QObject(parent), - mWidget(0) { + QueuedAction(int timeout, QObject* parent): QObject(parent), + mTimeout(timeout), + mFinished(false), + mActionDone(false) { } - void setWidget(QWidget* widget) { - mWidget = widget; + void run() { + mElapsedTime.start(); + mFinished = false; + + checkAction(); } + bool isFinished() const { + return mFinished; + } + + bool isActionDone() const { + return mActionDone; + } + public slots: - void closeInformation() const { + void checkAction() { + mActionDone = action(); + if (!mActionDone && mElapsedTime.elapsed() < mTimeout) { + QTimer::singleShot(100, this, SLOT(checkAction())); + } else { + mFinished = true; + } + } + +protected: + + virtual bool action() = 0; + +private: + + int mTimeout; + QTime mElapsedTime; + bool mFinished; + bool mActionDone; + +}; + +QWidget* findRemoteObjectChooser() { + foreach (QWidget* widget, QApplication::topLevelWidgets()) { + if (qobject_cast<RemoteObjectChooser*>(widget)) { + return widget; + } + } + + return 0; +} + +KDialog* findInformationMessageBox() { + QWidget* widget = findRemoteObjectChooser(); + if (!widget) { + return 0; + } + + return widget->findChild<KDialog*>("information"); +} + +class CloseInformationMessageBoxHelper: public QueuedAction { +public: + + CloseInformationMessageBoxHelper(int timeout, QObject* parent): + QueuedAction(timeout, parent) { + } + +protected: + + virtual bool action() { //The information message box is created in the constructor itself of - //RemoteObjectChooser, so the RemoteObjectChooser can not be passed as - //an argument when this helper is created. It must be got when it is + //RemoteObjectChooser, so the information message box can not be passed + //as an argument when this helper is created. It must be got when it is //going to be destroyed. - QWidget* widget = findRemoteObjectChooser(); - QVERIFY(widget); + KDialog* messageBox = findInformationMessageBox(); + if (!messageBox) { + return false; + } - KDialog* messageBox = widget->findChild<KDialog*>("information"); - QVERIFY(messageBox); delete messageBox; + + return true; } +}; - void closeSorry() const { +class CloseInformationMessageBoxOnceTargetApplicationEndsHelper: + public CloseInformationMessageBoxHelper { +public: + + CloseInformationMessageBoxOnceTargetApplicationEndsHelper(int timeout, + QObject* parent): + CloseInformationMessageBoxHelper(timeout, parent) { + } + +protected: + + virtual bool action() { + if (TargetApplication::self()->remoteEditorSupport()) { + return false; + } + + return CloseInformationMessageBoxHelper::action(); + } +}; + +class CloseSorryMessageBoxHelper: public QueuedAction { +public: + + CloseSorryMessageBoxHelper(int timeout, QObject* parent): + QueuedAction(timeout, parent), + mWidget(0) { + } + + void setWidget(QWidget* widget) { + mWidget = widget; + } + +protected: + + virtual bool action() { KDialog* messageBox = mWidget->findChild<KDialog*>("sorry"); - QVERIFY(messageBox); + if (!messageBox) { + return false; + } + delete messageBox; - } - void killTargetApplication() const { - TargetApplication::self()->mProcess->kill(); + return true; } private: QWidget* mWidget; - QWidget* findRemoteObjectChooser() const { - foreach (QWidget* widget, QApplication::topLevelWidgets()) { - if (qobject_cast<RemoteObjectChooser*>(widget)) { - return widget; - } +}; + +class KillTargetApplicationOnceMessageBoxIsShownHelper: public QueuedAction { +public: + + KillTargetApplicationOnceMessageBoxIsShownHelper(int timeout, + QObject* parent): + QueuedAction(timeout, parent) { + } + +protected: + + virtual bool action() { + //The information message box is created in the constructor itself of + //RemoteObjectChooser, so the information message box can not be passed + //as an argument when this helper is created. + KDialog* messageBox = findInformationMessageBox(); + if (!messageBox) { + return false; } - return 0; + TargetApplication::self()->mProcess->kill(); + + return true; } - }; void RemoteObjectChooserTest::closeInformationMessageBox(int timeToWait) { - QueuedActionsHelper* helper = new QueuedActionsHelper(this); - QTimer::singleShot(timeToWait, helper, SLOT(closeInformation())); + CloseInformationMessageBoxHelper* helper = + new CloseInformationMessageBoxHelper(timeToWait, this); + helper->run(); } +void RemoteObjectChooserTest:: + closeInformationMessageBoxOnceTargetApplicationEnds(int timeToWait) { + CloseInformationMessageBoxOnceTargetApplicationEndsHelper* helper = + new CloseInformationMessageBoxOnceTargetApplicationEndsHelper( + timeToWait, this); + helper->run(); +} + void RemoteObjectChooserTest::closeSorryMessageBox(QWidget* widget, int timeToWait) { - QueuedActionsHelper* helper = new QueuedActionsHelper(this); + CloseSorryMessageBoxHelper* helper = new CloseSorryMessageBoxHelper( + timeToWait, this); helper->setWidget(widget); - QTimer::singleShot(timeToWait, helper, SLOT(closeSorry())); + helper->run(); } -void RemoteObjectChooserTest::killTargetApplication(int timeToWait) { - QueuedActionsHelper* helper = new QueuedActionsHelper(this); - QTimer::singleShot(timeToWait, helper, SLOT(killTargetApplication())); +void RemoteObjectChooserTest::killTargetApplicationOnceMessageBoxIsShown( + int timeToWait) { + KillTargetApplicationOnceMessageBoxIsShownHelper* helper = + new KillTargetApplicationOnceMessageBoxIsShownHelper(timeToWait, this); + helper->run(); } +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; +} + +bool RemoteObjectChooserTest::waitForSorryMessageBoxToBeClosed(QWidget* widget, + int timeout) { + CloseSorryMessageBoxHelper* helper = new CloseSorryMessageBoxHelper(timeout, + this); + helper->setWidget(widget); + helper->run(); + + waitFor<QueuedAction>(*helper, &QueuedAction::isFinished, timeout); + + return helper->isActionDone(); +} + bool waitFor(bool (*condition)(), int timeout) { QElapsedTimer timer; timer.start(); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
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. |
From: <dan...@us...> - 2012-06-20 18:26:03
|
Revision: 342 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=342&view=rev Author: danxuliu Date: 2012-06-20 18:25:57 +0000 (Wed, 20 Jun 2012) Log Message: ----------- Fix DSO linking problems. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/tests/unit/data/CMakeLists.txt trunk/ktutorial/ktutorial-editor/tests/unit/util/CMakeLists.txt trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt trunk/ktutorial/ktutorial-library/tests/scripting/CMakeLists.txt Modified: trunk/ktutorial/ktutorial-editor/tests/unit/data/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/data/CMakeLists.txt 2011-10-16 18:07:26 UTC (rev 341) +++ trunk/ktutorial/ktutorial-editor/tests/unit/data/CMakeLists.txt 2012-06-20 18:25:57 UTC (rev 342) @@ -7,7 +7,7 @@ FOREACH(_className ${ARGN}) set(_testName ${_className}Test) kde4_add_unit_test(${_testName} TESTNAME ktutorial-editor-unit-${_testName} ${_testName}.cpp) - target_link_libraries(${_testName} ktutorial_editor_data ${QT_QTTEST_LIBRARY}) + target_link_libraries(${_testName} ktutorial_editor_data ${QT_QTCORE_LIBRARY} ${QT_QTTEST_LIBRARY}) ENDFOREACH(_className) ENDMACRO(UNIT_TESTS) Modified: trunk/ktutorial/ktutorial-editor/tests/unit/util/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/util/CMakeLists.txt 2011-10-16 18:07:26 UTC (rev 341) +++ trunk/ktutorial/ktutorial-editor/tests/unit/util/CMakeLists.txt 2012-06-20 18:25:57 UTC (rev 342) @@ -7,7 +7,7 @@ FOREACH(_className ${ARGN}) set(_testName ${_className}Test) kde4_add_unit_test(${_testName} TESTNAME ktutorial-editor-unit-${_testName} ${_testName}.cpp) - target_link_libraries(${_testName} ktutorial_editor_util ${QT_QTTEST_LIBRARY}) + target_link_libraries(${_testName} ktutorial_editor_util ${QT_QTCORE_LIBRARY} ${QT_QTTEST_LIBRARY}) ENDFOREACH(_className) ENDMACRO(UNIT_TESTS) Modified: trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt 2011-10-16 18:07:26 UTC (rev 341) +++ trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt 2012-06-20 18:25:57 UTC (rev 342) @@ -21,7 +21,7 @@ FOREACH(_className ${ARGN}) set(_testName ${_className}Test) kde4_add_unit_test(${_testName} TESTNAME ktutorial-${_testName} ${_testName}.cpp) - target_link_libraries(${_testName} ktutorial ${QT_QTTEST_LIBRARY}) + target_link_libraries(${_testName} ktutorial ${QT_QTTEST_LIBRARY} ${KDE4_KDEUI_LIBS}) ENDFOREACH(_className) ENDMACRO(UNIT_TESTS) Modified: trunk/ktutorial/ktutorial-library/tests/scripting/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/tests/scripting/CMakeLists.txt 2011-10-16 18:07:26 UTC (rev 341) +++ trunk/ktutorial/ktutorial-library/tests/scripting/CMakeLists.txt 2012-06-20 18:25:57 UTC (rev 342) @@ -7,7 +7,7 @@ FOREACH(_className ${ARGN}) set(_testName ${_className}Test) kde4_add_unit_test(${_testName} TESTNAME ktutorial-${_testName} ${_testName}.cpp) - target_link_libraries(${_testName} ktutorial ktutorial_scripting ${QT_QTTEST_LIBRARY}) + target_link_libraries(${_testName} ktutorial ktutorial_scripting ${QT_QTGUI_LIBRARY} ${QT_QTTEST_LIBRARY}) ENDFOREACH(_className) ENDMACRO(UNIT_TESTS) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2011-10-16 18:07:35
|
Revision: 341 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=341&view=rev Author: danxuliu Date: 2011-10-16 18:07:26 +0000 (Sun, 16 Oct 2011) Log Message: ----------- Fix test, as the assert may fail if the new object was created at the same memory address as the old one. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterManagerTest.cpp Modified: trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterManagerTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterManagerTest.cpp 2011-10-13 14:29:41 UTC (rev 340) +++ trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterManagerTest.cpp 2011-10-16 18:07:26 UTC (rev 341) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 by Daniel Calviño Sánchez * + * Copyright (C) 2010-2011 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -95,7 +95,11 @@ manager->highlight(child); - WidgetHighlighter* childHighlighter = highlighterOf(child); + //If a normal pointer is used it can happen that the old highligther object + //is deleted and the new one is created at the same memory address, thus + //causing the assert to fail (even if the behavior was right). Using a + //QPointer prevents this, as it is set to 0 when its object is deleted. + QPointer<WidgetHighlighter> childHighlighter = highlighterOf(child); manager->highlight(&widget); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |