[Ktutorial-commits] SF.net SVN: ktutorial:[195] trunk/ktutorial/ktutorial-editor
Status: Alpha
Brought to you by:
danxuliu
From: <dan...@us...> - 2010-03-26 08:49:20
|
Revision: 195 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=195&view=rev Author: danxuliu Date: 2010-03-26 08:49:10 +0000 (Fri, 26 Mar 2010) Log Message: ----------- -Add TutorialReader class to deserialize a Tutorial in XML. -Enable exceptions. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/CMakeLists.txt trunk/ktutorial/ktutorial-editor/src/serialization/CMakeLists.txt trunk/ktutorial/ktutorial-editor/tests/unit/serialization/CMakeLists.txt Added Paths: ----------- trunk/ktutorial/ktutorial-editor/src/serialization/DeserializationException.cpp trunk/ktutorial/ktutorial-editor/src/serialization/DeserializationException.h trunk/ktutorial/ktutorial-editor/src/serialization/TutorialReader.cpp trunk/ktutorial/ktutorial-editor/src/serialization/TutorialReader.h trunk/ktutorial/ktutorial-editor/tests/unit/serialization/DeserializationExceptionTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/serialization/TutorialReaderTest.cpp Modified: trunk/ktutorial/ktutorial-editor/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/CMakeLists.txt 2010-03-26 01:10:24 UTC (rev 194) +++ trunk/ktutorial/ktutorial-editor/CMakeLists.txt 2010-03-26 08:49:10 UTC (rev 195) @@ -3,6 +3,7 @@ find_package(KDE4 REQUIRED) include(KDE4Defaults) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${KDE4_ENABLE_EXCEPTIONS}") add_subdirectory(doc) add_subdirectory(po) Modified: trunk/ktutorial/ktutorial-editor/src/serialization/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/src/serialization/CMakeLists.txt 2010-03-26 01:10:24 UTC (rev 194) +++ trunk/ktutorial/ktutorial-editor/src/serialization/CMakeLists.txt 2010-03-26 08:49:10 UTC (rev 195) @@ -1,8 +1,10 @@ include_directories(${KDE4_INCLUDES}) set(ktutorial_editor_serialization_SRCS + DeserializationException.cpp JavascriptExporter.cpp Serialization.cpp + TutorialReader.cpp TutorialWriter.cpp ) Added: trunk/ktutorial/ktutorial-editor/src/serialization/DeserializationException.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/serialization/DeserializationException.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/serialization/DeserializationException.cpp 2010-03-26 08:49:10 UTC (rev 195) @@ -0,0 +1,33 @@ +/*************************************************************************** + * Copyright (C) 2010 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 "DeserializationException.h" + +//public: + +DeserializationException::DeserializationException(const QString& message): + std::exception(), + mMessage(message) { +} + +DeserializationException::~DeserializationException() throw() { +} + +const char* DeserializationException::what() const throw() { + return mMessage.toUtf8(); +} Property changes on: trunk/ktutorial/ktutorial-editor/src/serialization/DeserializationException.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/serialization/DeserializationException.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/serialization/DeserializationException.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/serialization/DeserializationException.h 2010-03-26 08:49:10 UTC (rev 195) @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright (C) 2010 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 DESERIALIZATIONEXCEPTION_H +#define DESERIALIZATIONEXCEPTION_H + +#include <exception> +#include <QString> + +/** + * Thrown when the XML can't be deserialized (for example, when it isn't well + * formed). + */ +class DeserializationException: public std::exception { +public: + + explicit DeserializationException(const QString& message = QString()); + virtual ~DeserializationException() throw(); + + /** + * Returns the exception message. + * + * @return The exception message. + */ + virtual const char* what() const throw(); + +private: + + QString mMessage; + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/serialization/DeserializationException.h ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/serialization/TutorialReader.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/serialization/TutorialReader.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/serialization/TutorialReader.cpp 2010-03-26 08:49:10 UTC (rev 195) @@ -0,0 +1,242 @@ +/*************************************************************************** + * Copyright (C) 2010 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 "TutorialReader.h" + +#include <QDomDocument> + +#include "../Reaction.h" +#include "../Step.h" +#include "../Tutorial.h" +#include "../WaitForComposed.h" +#include "../WaitForEvent.h" +#include "../WaitForNot.h" +#include "../WaitForSignal.h" + +//public: + +TutorialReader::TutorialReader() { +} + +Tutorial* TutorialReader::readTutorial(const QString& data) +throw (DeserializationException) { + QDomDocument document; + + QString errorMessage; + int errorLine; + int errorColumn; + + if (!document.setContent(data, &errorMessage, &errorLine, &errorColumn) || + document.documentElement().tagName() != "tutorial") { + throw DeserializationException(errorMessage); + } + + return readTutorial(document.documentElement()); +} + +//private: + +Tutorial* TutorialReader::readTutorial(const QDomElement& element) { + Tutorial* tutorial = new Tutorial(); + + if (element.hasAttribute("name")) { + tutorial->setName(element.attribute("name")); + } + + QDomElement childElement = element.firstChildElement(); + while (!childElement.isNull()) { + if (childElement.tagName() == "description") { + tutorial->setDescription(childElement.text()); + } else if (childElement.tagName() == "license") { + tutorial->setLicenseText(childElement.text()); + } else if (childElement.tagName() == "setup") { + tutorial->setCustomSetupCode(childElement.text()); + } else if (childElement.tagName() == "tearDown") { + tutorial->setCustomTearDownCode(childElement.text()); + } else if (childElement.tagName() == "step") { + tutorial->addStep(readStep(childElement)); + } + + childElement = childElement.nextSiblingElement(); + } + + return tutorial; +} + +Step* TutorialReader::readStep(const QDomElement& element) { + Step* step = new Step; + + if (element.hasAttribute("id")) { + step->setId(element.attribute("id")); + } + + QDomElement childElement = element.firstChildElement(); + while (!childElement.isNull()) { + if (childElement.tagName() == "text") { + step->setText(childElement.text()); + } else if (childElement.tagName() == "setup") { + step->setCustomSetupCode(childElement.text()); + } else if (childElement.tagName() == "tearDown") { + step->setCustomTearDownCode(childElement.text()); + } else if (childElement.tagName() == "reaction") { + step->addReaction(readReaction(childElement)); + } + + childElement = childElement.nextSiblingElement(); + } + + return step; +} + +Reaction* TutorialReader::readReaction(const QDomElement& element) { + Reaction* reaction = new Reaction(); + + if (element.hasAttribute("triggerType")) { + Reaction::TriggerType triggerType = Reaction::OptionSelected; + if (element.attribute("triggerType") == "ConditionMet") { + triggerType = Reaction::ConditionMet; + } + + reaction->setTriggerType(triggerType); + } + + if (element.hasAttribute("responseType")) { + Reaction::ResponseType responseType = Reaction::NextStep; + if (element.attribute("responseType") == "CustomCode") { + responseType = Reaction::CustomCode; + } + + reaction->setResponseType(responseType); + } + + QDomElement childElement = element.firstChildElement(); + while (!childElement.isNull()) { + if (childElement.tagName() == "option") { + if (childElement.hasAttribute("name")) { + reaction->setOptionName(childElement.attribute("name")); + } + } else if (isWaitForElement(childElement)) { + reaction->setWaitFor(readWaitFor(childElement)); + } else if (childElement.tagName() == "customCode") { + reaction->setCustomCode(childElement.text()); + } else if (childElement.tagName() == "nextStep") { + if (childElement.hasAttribute("id")) { + reaction->setNextStepId(childElement.attribute("id")); + } + } + + childElement = childElement.nextSiblingElement(); + } + + return reaction; +} + +WaitFor* TutorialReader::readWaitFor(const QDomElement& element) { + if (element.tagName() == "waitForComposed") { + return readWaitForComposed(element); + } + if (element.tagName() == "waitForEvent") { + return readWaitForEvent(element); + } + if (element.tagName() == "waitForNot") { + return readWaitForNot(element); + } + if (element.tagName() == "waitForSignal") { + return readWaitForSignal(element); + } + + Q_ASSERT(false); + return 0; +} + +WaitFor* TutorialReader::readWaitForComposed(const QDomElement& element) { + WaitForComposed* waitForComposed = new WaitForComposed(); + + if (element.hasAttribute("compositionType")) { + WaitForComposed::CompositionType compositionType = WaitForComposed::And; + if (element.attribute("compositionType") == "Or") { + compositionType = WaitForComposed::Or; + } + + waitForComposed->setCompositionType(compositionType); + } + + QDomElement childElement = element.firstChildElement(); + while (!childElement.isNull()) { + if (isWaitForElement(childElement)) { + waitForComposed->addWaitFor(readWaitFor(childElement)); + } + + childElement = childElement.nextSiblingElement(); + } + + return waitForComposed; +} + +WaitFor* TutorialReader::readWaitForEvent(const QDomElement& element) { + WaitForEvent* waitForEvent = new WaitForEvent(); + + if (element.hasAttribute("receiverName")) { + waitForEvent->setReceiverName(element.attribute("receiverName")); + } + if (element.hasAttribute("eventName")) { + waitForEvent->setEventName(element.attribute("eventName")); + } + + return waitForEvent; +} + +WaitFor* TutorialReader::readWaitForNot(const QDomElement& element) { + WaitForNot* waitForNot = new WaitForNot(); + + QDomElement childElement = element.firstChildElement(); + while (!childElement.isNull()) { + if (isWaitForElement(childElement)) { + delete waitForNot->negatedWaitFor(); + waitForNot->setNegatedWaitFor(readWaitFor(childElement)); + } + + childElement = childElement.nextSiblingElement(); + } + + return waitForNot; +} + +WaitFor* TutorialReader::readWaitForSignal(const QDomElement& element) { + WaitForSignal* waitForSignal = new WaitForSignal(); + + if (element.hasAttribute("emitterName")) { + waitForSignal->setEmitterName(element.attribute("emitterName")); + } + if (element.hasAttribute("signalName")) { + waitForSignal->setSignalName(element.attribute("signalName")); + } + + return waitForSignal; +} + +bool TutorialReader::isWaitForElement(const QDomElement& element) { + if (element.tagName() != "waitForComposed" && + element.tagName() != "waitForEvent" && + element.tagName() != "waitForNot" && + element.tagName() != "waitForSignal") { + return false; + } + + return true; +} Property changes on: trunk/ktutorial/ktutorial-editor/src/serialization/TutorialReader.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/serialization/TutorialReader.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/serialization/TutorialReader.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/serialization/TutorialReader.h 2010-03-26 08:49:10 UTC (rev 195) @@ -0,0 +1,147 @@ +/*************************************************************************** + * Copyright (C) 2010 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 TUTORIALREADER_H +#define TUTORIALREADER_H + +#include <QXmlStreamWriter> + +#include "DeserializationException.h" + +class QDomElement; +class Reaction; +class Step; +class Tutorial; +class WaitFor; +class WaitForComposed; +class WaitForEvent; +class WaitForNot; +class WaitForSignal; + +/** + * Deserializer for tutorials stored in XML. + * Creates a new Tutorial from the XML data generated by TutorialWriter. Anyway, + * if the XML data is not valid (see Tutorial.xsd for the W3C Schema), it + * ignores unknown attributes and elements, and uses those known to create the + * tutorial. + * + * @see TutorialWriter + */ +class TutorialReader { +public: + + /** + * Creates a new TutorialReader. + */ + TutorialReader(); + + /** + * Returns the Tutorial stored in the given XML serialization. + * The tutorial must be deleted explicitly. + * + * If the XML is not well formed or its root element is not a "tutorial" + * element, a DeserializationException is thrown. In any other case, even if + * the XML is not valid, the deserializer tries to do its best: it ignores + * unknown attributes and elements and deserializes all the attributes and + * elements it knows. + * + * @param data The XML serialization. + * @return The Tutorial stored in the given XML serialization. + * @throw DeserializationException If there was a problem deserializing the + * tutorial. + */ + Tutorial* readTutorial(const QString& data) + throw (DeserializationException); + +private: + + /** + * Reads a new Tutorial from the "tutorial" XML element. + * + * @param element The element to read the Tutorial from. + * @return The new Tutorial. + */ + Tutorial* readTutorial(const QDomElement& element); + + /** + * Reads a new Step from the "step" XML element. + * + * @param element The element to read the Step from. + * @return The new Step. + */ + Step* readStep(const QDomElement& element); + + /** + * Reads a new Reaction from the "reaction" XML element. + * + * @param element The element to read the Reaction from. + * @return The new Reaction. + */ + Reaction* readReaction(const QDomElement& element); + + /** + * Returns a new WaitFor object from the appropriate subclass. + * + * @param element The element to read the WaitFor from. + * @return The new WaitFor. + */ + WaitFor* readWaitFor(const QDomElement& element); + + /** + * Reads a new WaitForComposed from the "waitForComposed" XML element. + * + * @param element The element to read the WaitForComposed from. + * @return The new WaitForComposed. + */ + WaitFor* readWaitForComposed(const QDomElement& element); + + /** + * Reads a new WaitForEvent from the "waitForEvent" XML element. + * + * @param element The element to read the WaitForEvent from. + * @return The new WaitForEvent. + */ + WaitFor* readWaitForEvent(const QDomElement& element); + + /** + * Reads a new WaitForNot from the "waitForNot" XML element. + * + * @param element The element to read the WaitForNot from. + * @return The new WaitForNot. + */ + WaitFor* readWaitForNot(const QDomElement& element); + + /** + * Reads a new WaitForSignal from the "waitForSignal" XML element. + * + * @param element The element to read the WaitForSignal from. + * @return The new WaitForSignal. + */ + WaitFor* readWaitForSignal(const QDomElement& element); + + /** + * Returns whether the given element is one of the WaitFor elements or not. + * + * @param element The element to check. + * @return Whether the given element is one of the WaitFor elements or not. + */ + bool isWaitForElement(const QDomElement& element); + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/serialization/TutorialReader.h ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-editor/tests/unit/serialization/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/serialization/CMakeLists.txt 2010-03-26 01:10:24 UTC (rev 194) +++ trunk/ktutorial/ktutorial-editor/tests/unit/serialization/CMakeLists.txt 2010-03-26 08:49:10 UTC (rev 195) @@ -12,6 +12,7 @@ ENDMACRO(UNIT_TESTS) unit_tests( + DeserializationException JavascriptExporter Serialization TutorialReader @@ -25,6 +26,7 @@ ENDMACRO(MEM_TESTS) mem_tests( + DeserializationException JavascriptExporter Serialization TutorialReader Added: trunk/ktutorial/ktutorial-editor/tests/unit/serialization/DeserializationExceptionTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/serialization/DeserializationExceptionTest.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/tests/unit/serialization/DeserializationExceptionTest.cpp 2010-03-26 08:49:10 UTC (rev 195) @@ -0,0 +1,47 @@ +/*************************************************************************** + * Copyright (C) 2010 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 "DeserializationException.h" + +class DeserializationExceptionTest: public QObject { +Q_OBJECT + +private slots: + + void testConstructor(); + void testConstructorEmpty(); + +}; + +void DeserializationExceptionTest::testConstructor() { + DeserializationException exception(QString("The message")); + + QCOMPARE(exception.what(), "The message"); +} + +void DeserializationExceptionTest::testConstructorEmpty() { + DeserializationException exception; + + QCOMPARE(exception.what(), ""); +} + +QTEST_MAIN(DeserializationExceptionTest) + +#include "DeserializationExceptionTest.moc" Property changes on: trunk/ktutorial/ktutorial-editor/tests/unit/serialization/DeserializationExceptionTest.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/tests/unit/serialization/TutorialReaderTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/serialization/TutorialReaderTest.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/tests/unit/serialization/TutorialReaderTest.cpp 2010-03-26 08:49:10 UTC (rev 195) @@ -0,0 +1,658 @@ +/*************************************************************************** + * Copyright (C) 2010 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 "TutorialReader.h" + +#include "../Reaction.h" +#include "../Step.h" +#include "../Tutorial.h" +#include "../WaitForComposed.h" +#include "../WaitForEvent.h" +#include "../WaitForNot.h" +#include "../WaitForSignal.h" + +#define HEADER_XML \ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + +#define TUTORIAL_ELEMENTS \ +" <description>The description,\nwith < and >\n</description>\n" \ +" <license>The license text,\nwith < and >\n</license>\n" \ +" <setup>The setup code,\nwith < and >\n</setup>\n" \ +" <tearDown>The tear down code,\nwith < and >\n</tearDown>\n" + +#define STEP_PARENT_START \ +HEADER_XML \ +"<tutorial>\n" + +#define STEP_PARENT_END \ +"</tutorial>\n" + +#define STEP_ELEMENTS \ +" <text>The text,\nwith < and >\n</text>\n" \ +" <setup>The setup code,\nwith < and >\n</setup>\n" \ +" <tearDown>The tear down code,\nwith < and >\n</tearDown>\n" + +#define REACTION_PARENT_START \ +STEP_PARENT_START \ +" <step>\n" + +#define REACTION_PARENT_END \ +" </step>\n" \ +STEP_PARENT_END + +#define WAITFOR_PARENT_START \ +REACTION_PARENT_START \ +" <reaction triggerType=\"ConditionMet\" responseType=\"NextStep\">\n" + +#define WAITFOR_PARENT_END \ +" </reaction>\n" \ +REACTION_PARENT_END + +class TutorialReaderTest: public QObject { +Q_OBJECT + +private slots: + + void testTutorial(); + void testTutorialEmpty(); + void testTutorialWithSeveralSteps(); + + void testStep(); + void testStepEmpty(); + void testStepWithSeveralReactions(); + + void testReactionConditionCustomCode(); + void testReactionOptionNextStep(); + void testReactionEmpty(); + + void testWaitForEvent(); + void testWaitForEventEmpty(); + void testWaitForSignal(); + void testWaitForSignalEmpty(); + void testWaitForComposed(); + void testWaitForComposedEmpty(); + void testWaitForNot(); + void testWaitForNotWithoutNegatedWaitFor(); + + void testXmlNotWellFormed(); + void testXmlWithoutRootTutorialElement(); + void testXmlWithGarbageElementsAndAttributes(); + +private: + + void assertWaitForSignal(WaitFor* waitFor, const QString& emitterName, + const QString& signalName) const; + + QString addGarbageToXmlData(const QString& data) const; + +}; + +void TutorialReaderTest::testTutorial() { + QString data = +HEADER_XML +"<tutorial name=\"The "name"\">\n" +" <description>The description,\nwith < and >\n</description>\n" +" <license>The license text,\nwith < and >\n</license>\n" +" <setup>The setup code,\nwith < and >\n</setup>\n" +" <tearDown>The tear down code,\nwith < and >\n</tearDown>\n" +"</tutorial>\n"; + + TutorialReader reader; + QScopedPointer<Tutorial> tutorial(reader.readTutorial(data)); + + QVERIFY(tutorial); + QCOMPARE(tutorial->name(), QString("The \"name\"")); + QCOMPARE(tutorial->description(), + QString("The description,\nwith < and >\n")); + QCOMPARE(tutorial->licenseText(), + QString("The license text,\nwith < and >\n")); + QCOMPARE(tutorial->customSetupCode(), + QString("The setup code,\nwith < and >\n")); + QCOMPARE(tutorial->customTearDownCode(), + QString("The tear down code,\nwith < and >\n")); + QCOMPARE(tutorial->steps().count(), 0); +} + +void TutorialReaderTest::testTutorialEmpty() { + QString data = +HEADER_XML +"<tutorial/>\n"; + + TutorialReader reader; + QScopedPointer<Tutorial> tutorial(reader.readTutorial(data)); + + QVERIFY(tutorial); + QCOMPARE(tutorial->name(), QString("")); + QCOMPARE(tutorial->description(), QString("")); + QCOMPARE(tutorial->licenseText(), QString("")); + QCOMPARE(tutorial->customSetupCode(), QString("")); + QCOMPARE(tutorial->customTearDownCode(), QString("")); + QCOMPARE(tutorial->steps().count(), 0); +} + +void TutorialReaderTest::testTutorialWithSeveralSteps() { + QString data = +HEADER_XML +"<tutorial name=\"The "name"\">\n" +TUTORIAL_ELEMENTS +" <step id=\"The id1\">\n" +" <text>The text1</text>\n" +" </step>\n" +" <step id=\"The id2\">\n" +" <text>The text2</text>\n" +" </step>\n" +"</tutorial>\n"; + + TutorialReader reader; + QScopedPointer<Tutorial> tutorial(reader.readTutorial(data)); + + QVERIFY(tutorial); + QCOMPARE(tutorial->name(), QString("The \"name\"")); + QCOMPARE(tutorial->description(), + QString("The description,\nwith < and >\n")); + QCOMPARE(tutorial->licenseText(), + QString("The license text,\nwith < and >\n")); + QCOMPARE(tutorial->customSetupCode(), + QString("The setup code,\nwith < and >\n")); + QCOMPARE(tutorial->customTearDownCode(), + QString("The tear down code,\nwith < and >\n")); + QCOMPARE(tutorial->steps().count(), 2); + Step* step = tutorial->steps()[0]; + QVERIFY(step); + QCOMPARE(step->id(), QString("The id1")); + QCOMPARE(step->text(), QString("The text1")); + step = tutorial->steps()[1]; + QVERIFY(step); + QCOMPARE(step->id(), QString("The id2")); + QCOMPARE(step->text(), QString("The text2")); +} + +void TutorialReaderTest::testStep() { + QString data = +STEP_PARENT_START +" <step id=\"The "id"\">\n" +" <text>The text,\nwith < and >\n</text>\n" +" <setup>The setup code,\nwith < and >\n</setup>\n" +" <tearDown>The tear down code,\nwith < and >\n</tearDown>\n" +" </step>\n" +STEP_PARENT_END; + + TutorialReader reader; + QScopedPointer<Tutorial> tutorial(reader.readTutorial(data)); + Step* step = tutorial->steps()[0]; + + QVERIFY(step); + QCOMPARE(step->id(), QString("The \"id\"")); + QCOMPARE(step->text(), QString("The text,\nwith < and >\n")); + QCOMPARE(step->customSetupCode(), + QString("The setup code,\nwith < and >\n")); + QCOMPARE(step->customTearDownCode(), + QString("The tear down code,\nwith < and >\n")); + QCOMPARE(step->reactions().count(), 0); +} + +void TutorialReaderTest::testStepEmpty() { + QString data = +STEP_PARENT_START +" <step/>\n" +STEP_PARENT_END; + + TutorialReader reader; + QScopedPointer<Tutorial> tutorial(reader.readTutorial(data)); + Step* step = tutorial->steps()[0]; + + QVERIFY(step); + QCOMPARE(step->id(), QString("")); + QCOMPARE(step->text(), QString("")); + QCOMPARE(step->customSetupCode(), QString("")); + QCOMPARE(step->customTearDownCode(), QString("")); + QCOMPARE(step->reactions().count(), 0); +} + +void TutorialReaderTest::testStepWithSeveralReactions() { + QString data = +STEP_PARENT_START +" <step id=\"The "id"\">\n" +STEP_ELEMENTS +" <reaction triggerType=\"ConditionMet\" responseType=\"CustomCode\"/>\n" +" <reaction triggerType=\"OptionSelected\" responseType=\"NextStep\">\n" +" <option name=\"The option name\"/>\n" +" <nextStep id=\"Another id\"/>\n" +" </reaction>\n" +" </step>\n" +STEP_PARENT_END; + + TutorialReader reader; + QScopedPointer<Tutorial> tutorial(reader.readTutorial(data)); + Step* step = tutorial->steps()[0]; + + QVERIFY(step); + QCOMPARE(step->id(), QString("The \"id\"")); + QCOMPARE(step->text(), QString("The text,\nwith < and >\n")); + QCOMPARE(step->customSetupCode(), + QString("The setup code,\nwith < and >\n")); + QCOMPARE(step->customTearDownCode(), + QString("The tear down code,\nwith < and >\n")); + QCOMPARE(step->reactions().count(), 2); + Reaction* reaction = step->reactions()[0]; + QVERIFY(reaction); + QCOMPARE(reaction->triggerType(), Reaction::ConditionMet); + QCOMPARE(reaction->responseType(), Reaction::CustomCode); + reaction = step->reactions()[1]; + QVERIFY(reaction); + QCOMPARE(reaction->triggerType(), Reaction::OptionSelected); + QCOMPARE(reaction->optionName(), QString("The option name")); + QCOMPARE(reaction->responseType(), Reaction::NextStep); + QCOMPARE(reaction->nextStepId(), QString("Another id")); +} + +void TutorialReaderTest::testReactionConditionCustomCode() { + QString data = +REACTION_PARENT_START +" <reaction triggerType=\"ConditionMet\" responseType=\"CustomCode\">\n" +" <option name=\"The "option" name\"/>\n" +" <waitForSignal emitterName=\"The emitter name\" \ +signalName=\"theSignalName(Argument1Type, Argument2Type)\"/>\n" +" <customCode>The custom code,\nwith < and >\n</customCode>\n" +" <nextStep id=\"Another "id"\"/>\n" +" </reaction>\n" +REACTION_PARENT_END; + + TutorialReader reader; + QScopedPointer<Tutorial> tutorial(reader.readTutorial(data)); + Reaction* reaction = tutorial->steps()[0]->reactions()[0]; + + QVERIFY(reaction); + QCOMPARE(reaction->triggerType(), Reaction::ConditionMet); + QCOMPARE(reaction->optionName(), QString("The \"option\" name")); + assertWaitForSignal(reaction->waitFor(), "The emitter name", + "theSignalName(Argument1Type, Argument2Type)"); + QCOMPARE(reaction->responseType(), Reaction::CustomCode); + QCOMPARE(reaction->customCode(), + QString("The custom code,\nwith < and >\n")); + QCOMPARE(reaction->nextStepId(), QString("Another \"id\"")); +} + +void TutorialReaderTest::testReactionOptionNextStep() { + QString data = +REACTION_PARENT_START +" <reaction triggerType=\"OptionSelected\" responseType=\"NextStep\">\n" +" <option name=\"The "option" name\"/>\n" +" <waitForSignal emitterName=\"The emitter name\" \ +signalName=\"theSignalName(Argument1Type, Argument2Type)\"/>\n" +" <customCode>The custom code,\nwith < and >\n</customCode>\n" +" <nextStep id=\"Another "id"\"/>\n" +" </reaction>\n" +REACTION_PARENT_END; + + TutorialReader reader; + QScopedPointer<Tutorial> tutorial(reader.readTutorial(data)); + Reaction* reaction = tutorial->steps()[0]->reactions()[0]; + + QVERIFY(reaction); + QCOMPARE(reaction->triggerType(), Reaction::OptionSelected); + QCOMPARE(reaction->optionName(), QString("The \"option\" name")); + assertWaitForSignal(reaction->waitFor(), "The emitter name", + "theSignalName(Argument1Type, Argument2Type)"); + QCOMPARE(reaction->responseType(), Reaction::NextStep); + QCOMPARE(reaction->customCode(), + QString("The custom code,\nwith < and >\n")); + QCOMPARE(reaction->nextStepId(), QString("Another \"id\"")); +} + +void TutorialReaderTest::testReactionEmpty() { + QString data = +REACTION_PARENT_START +" <reaction triggerType=\"ConditionMet\" responseType=\"CustomCode\"/>\n" +REACTION_PARENT_END; + + TutorialReader reader; + QScopedPointer<Tutorial> tutorial(reader.readTutorial(data)); + Reaction* reaction = tutorial->steps()[0]->reactions()[0]; + + QVERIFY(reaction); + QCOMPARE(reaction->triggerType(), Reaction::ConditionMet); + QCOMPARE(reaction->optionName(), QString("")); + QCOMPARE(reaction->waitFor(), (WaitFor*)0); + QCOMPARE(reaction->responseType(), Reaction::CustomCode); + QCOMPARE(reaction->customCode(), QString("")); + QCOMPARE(reaction->nextStepId(), QString("")); +} + +void TutorialReaderTest::testWaitForEvent() { + QString data = +WAITFOR_PARENT_START +" <waitForEvent receiverName=\"The "receiver" name\" \ +eventName=\"The"Event"Name\"/>\n" +WAITFOR_PARENT_END; + + TutorialReader reader; + QScopedPointer<Tutorial> tutorial(reader.readTutorial(data)); + WaitFor* waitFor = tutorial->steps()[0]->reactions()[0]->waitFor(); + + WaitForEvent* waitForEvent = qobject_cast<WaitForEvent*>(waitFor); + QVERIFY(waitForEvent); + QCOMPARE(waitForEvent->receiverName(), QString("The \"receiver\" name")); + QCOMPARE(waitForEvent->eventName(), QString("The\"Event\"Name")); +} + +void TutorialReaderTest::testWaitForEventEmpty() { + QString data = +WAITFOR_PARENT_START +" <waitForEvent/>\n" +WAITFOR_PARENT_END; + + TutorialReader reader; + QScopedPointer<Tutorial> tutorial(reader.readTutorial(data)); + WaitFor* waitFor = tutorial->steps()[0]->reactions()[0]->waitFor(); + + WaitForEvent* waitForEvent = qobject_cast<WaitForEvent*>(waitFor); + QVERIFY(waitForEvent); + QCOMPARE(waitForEvent->receiverName(), QString("")); + QCOMPARE(waitForEvent->eventName(), QString("")); +} + +void TutorialReaderTest::testWaitForSignal() { + QString data = +WAITFOR_PARENT_START +" <waitForSignal emitterName=\"The "emitter" name\" \ +signalName=\"theSignalName("Argument1Type")\"/>\n" +WAITFOR_PARENT_END; + + TutorialReader reader; + QScopedPointer<Tutorial> tutorial(reader.readTutorial(data)); + WaitFor* waitFor = tutorial->steps()[0]->reactions()[0]->waitFor(); + + assertWaitForSignal(waitFor, "The \"emitter\" name", + "theSignalName(\"Argument1Type\")"); +} + +void TutorialReaderTest::testWaitForSignalEmpty() { + QString data = +WAITFOR_PARENT_START +" <waitForSignal/>\n" +WAITFOR_PARENT_END; + + TutorialReader reader; + QScopedPointer<Tutorial> tutorial(reader.readTutorial(data)); + WaitFor* waitFor = tutorial->steps()[0]->reactions()[0]->waitFor(); + + assertWaitForSignal(waitFor, "", ""); +} + +void TutorialReaderTest::testWaitForComposed() { + QString data = +WAITFOR_PARENT_START +" <waitForComposed compositionType=\"And\">\n" +" <waitForSignal emitterName=\"The emitter name1\" \ +signalName=\"theSignalName1()\"/>\n" +" <waitForComposed compositionType=\"Or\">\n" +" <waitForSignal emitterName=\"The emitter name2\" \ +signalName=\"theSignalName2()\"/>\n" +" <waitForSignal emitterName=\"The emitter name3\" \ +signalName=\"theSignalName3()\"/>\n" +" </waitForComposed>\n" +" </waitForComposed>\n" +WAITFOR_PARENT_END; + + TutorialReader reader; + QScopedPointer<Tutorial> tutorial(reader.readTutorial(data)); + WaitFor* waitFor = tutorial->steps()[0]->reactions()[0]->waitFor(); + + WaitForComposed* waitForAnd = qobject_cast<WaitForComposed*>(waitFor); + QVERIFY(waitForAnd); + QCOMPARE(waitForAnd->compositionType(), WaitForComposed::And); + QCOMPARE(waitForAnd->waitFors().count(), 2); + assertWaitForSignal(waitForAnd->waitFors()[0], "The emitter name1", + "theSignalName1()"); + + WaitForComposed* waitForOr = + qobject_cast<WaitForComposed*>(waitForAnd->waitFors()[1]); + QVERIFY(waitForOr); + QCOMPARE(waitForOr->compositionType(), WaitForComposed::Or); + QCOMPARE(waitForOr->waitFors().count(), 2); + assertWaitForSignal(waitForOr->waitFors()[0], "The emitter name2", + "theSignalName2()"); + assertWaitForSignal(waitForOr->waitFors()[1], "The emitter name3", + "theSignalName3()"); +} + +void TutorialReaderTest::testWaitForComposedEmpty() { + QString data = +WAITFOR_PARENT_START +" <waitForComposed compositionType=\"And\">\n" +" <waitForComposed compositionType=\"And\"/>\n" +" <waitForComposed compositionType=\"Or\"/>\n" +" </waitForComposed>\n" +WAITFOR_PARENT_END; + + TutorialReader reader; + QScopedPointer<Tutorial> tutorial(reader.readTutorial(data)); + WaitFor* waitFor = tutorial->steps()[0]->reactions()[0]->waitFor(); + + WaitForComposed* waitForAnd = qobject_cast<WaitForComposed*>(waitFor); + QVERIFY(waitForAnd); + QCOMPARE(waitForAnd->compositionType(), WaitForComposed::And); + QCOMPARE(waitForAnd->waitFors().count(), 2); + + WaitForComposed* waitForAndChild = + qobject_cast<WaitForComposed*>(waitForAnd->waitFors()[0]); + QVERIFY(waitForAndChild); + QCOMPARE(waitForAndChild->compositionType(), WaitForComposed::And); + QCOMPARE(waitForAndChild->waitFors().count(), 0); + + WaitForComposed* waitForOrChild = + qobject_cast<WaitForComposed*>(waitForAnd->waitFors()[1]); + QVERIFY(waitForOrChild); + QCOMPARE(waitForOrChild->compositionType(), WaitForComposed::Or); + QCOMPARE(waitForOrChild->waitFors().count(), 0); +} + +void TutorialReaderTest::testWaitForNot() { + QString data = +WAITFOR_PARENT_START +" <waitForNot>\n" +" <waitForSignal emitterName=\"The emitter name\" \ +signalName=\"theSignalName()\"/>\n" +" </waitForNot>\n" +WAITFOR_PARENT_END; + + TutorialReader reader; + QScopedPointer<Tutorial> tutorial(reader.readTutorial(data)); + WaitFor* waitFor = tutorial->steps()[0]->reactions()[0]->waitFor(); + + WaitForNot* waitForNot = qobject_cast<WaitForNot*>(waitFor); + QVERIFY(waitForNot); + WaitForSignal* waitForSignal = + qobject_cast<WaitForSignal*>(waitForNot->negatedWaitFor()); + QVERIFY(waitForSignal); + QCOMPARE(waitForSignal->emitterName(), QString("The emitter name")); + QCOMPARE(waitForSignal->signalName(), QString("theSignalName()")); +} + +void TutorialReaderTest::testWaitForNotWithoutNegatedWaitFor() { + QString data = +WAITFOR_PARENT_START +" <waitForNot/>\n" +WAITFOR_PARENT_END; + + TutorialReader reader; + QScopedPointer<Tutorial> tutorial(reader.readTutorial(data)); + WaitFor* waitFor = tutorial->steps()[0]->reactions()[0]->waitFor(); + + WaitForNot* waitForNot = qobject_cast<WaitForNot*>(waitFor); + QVERIFY(waitForNot); + QCOMPARE(waitForNot->negatedWaitFor(), (WaitFor*)0); +} + +void TutorialReaderTest::testXmlNotWellFormed() { + QString data = +HEADER_XML +"<tutorial>\n" +" <step>\n" +" </invalidEndElement>\n" +"</tutorial>\n"; + + TutorialReader reader; + try { + QScopedPointer<Tutorial> tutorial(reader.readTutorial(data)); + QFAIL("Expected DeserializationException not thrown"); + } catch (DeserializationException e) { + } +} + +void TutorialReaderTest::testXmlWithoutRootTutorialElement() { + QString data = +HEADER_XML +"<unknownRootElement>\n" +"</unknownRootElement>\n"; + + TutorialReader reader; + try { + QScopedPointer<Tutorial> tutorial(reader.readTutorial(data)); + QFAIL("Expected DeserializationException not thrown"); + } catch (DeserializationException e) { + } +} + +void TutorialReaderTest::testXmlWithGarbageElementsAndAttributes() { + QString data = +HEADER_XML +"<tutorial name=\"The "name"\">\n" +TUTORIAL_ELEMENTS +" <step id=\"The id1\">\n" +STEP_ELEMENTS +" </step>\n" +" <step id=\"The id2\">\n" +" <text>The text2</text>\n" +" <reaction triggerType=\"OptionSelected\" responseType=\"NextStep\">\n" +" <option name=\"The "option" name\"/>\n" +" <customCode>The custom code,\nwith < and >\n</customCode>\n" +" <nextStep id=\"Another "id"\"/>\n" +" </reaction>\n" +" <reaction triggerType=\"ConditionMet\" responseType=\"CustomCode\">\n" +" <waitForComposed compositionType=\"And\">\n" +" <waitForSignal emitterName=\"The emitter name1\" \ +signalName=\"theSignalName1()\"/>\n" +" <waitForComposed compositionType=\"Or\">\n" +" <waitForSignal emitterName=\"The emitter name2\" \ +signalName=\"theSignalName2()\"/>\n" +" <waitForSignal emitterName=\"The emitter name3\" \ +signalName=\"theSignalName3()\"/>\n" +" </waitForComposed>\n" +" </waitForComposed>\n" +" </reaction>\n" +" </step>\n" +"</tutorial>\n"; + + data = addGarbageToXmlData(data); + + TutorialReader reader; + QScopedPointer<Tutorial> tutorial(reader.readTutorial(data)); + + QVERIFY(tutorial); + QCOMPARE(tutorial->name(), QString("The \"name\"")); + QCOMPARE(tutorial->description(), + QString("The description,\nwith < and >\n")); + QCOMPARE(tutorial->licenseText(), + QString("The license text,\nwith < and >\n")); + QCOMPARE(tutorial->customSetupCode(), + QString("The setup code,\nwith < and >\n")); + QCOMPARE(tutorial->customTearDownCode(), + QString("The tear down code,\nwith < and >\n")); + QCOMPARE(tutorial->steps().count(), 2); + + Step* step = tutorial->steps()[0]; + QVERIFY(step); + QCOMPARE(step->id(), QString("The id1")); + QCOMPARE(step->text(), QString("The text,\nwith < and >\n")); + QCOMPARE(step->customSetupCode(), + QString("The setup code,\nwith < and >\n")); + QCOMPARE(step->customTearDownCode(), + QString("The tear down code,\nwith < and >\n")); + QCOMPARE(step->reactions().count(), 0); + + step = tutorial->steps()[1]; + QVERIFY(step); + QCOMPARE(step->id(), QString("The id2")); + QCOMPARE(step->text(), QString("The text2")); + QCOMPARE(step->reactions().count(), 2); + + Reaction* reaction = step->reactions()[0]; + QCOMPARE(reaction->triggerType(), Reaction::OptionSelected); + QCOMPARE(reaction->optionName(), QString("The \"option\" name")); + QCOMPARE(reaction->waitFor(), (WaitFor*)0); + QCOMPARE(reaction->responseType(), Reaction::NextStep); + QCOMPARE(reaction->customCode(), + QString("The custom code,\nwith < and >\n")); + QCOMPARE(reaction->nextStepId(), QString("Another \"id\"")); + + reaction = step->reactions()[1]; + QCOMPARE(reaction->triggerType(), Reaction::ConditionMet); + QCOMPARE(reaction->optionName(), QString("")); + QCOMPARE(reaction->responseType(), Reaction::CustomCode); + QCOMPARE(reaction->customCode(), QString("")); + QCOMPARE(reaction->nextStepId(), QString("")); + WaitFor* waitFor = reaction->waitFor(); + + WaitForComposed* waitForAnd = qobject_cast<WaitForComposed*>(waitFor); + QVERIFY(waitForAnd); + QCOMPARE(waitForAnd->compositionType(), WaitForComposed::And); + QCOMPARE(waitForAnd->waitFors().count(), 2); + assertWaitForSignal(waitForAnd->waitFors()[0], "The emitter name1", + "theSignalName1()"); + + WaitForComposed* waitForOr = + qobject_cast<WaitForComposed*>(waitForAnd->waitFors()[1]); + QVERIFY(waitForOr); + QCOMPARE(waitForOr->compositionType(), WaitForComposed::Or); + QCOMPARE(waitForOr->waitFors().count(), 2); + assertWaitForSignal(waitForOr->waitFors()[0], "The emitter name2", + "theSignalName2()"); + assertWaitForSignal(waitForOr->waitFors()[1], "The emitter name3", + "theSignalName3()"); +} + +/////////////////////////////////// Helpers //////////////////////////////////// + +void TutorialReaderTest::assertWaitForSignal(WaitFor* waitFor, + const QString& emitterName, + const QString& signalName) const { + WaitForSignal* waitForSignal = qobject_cast<WaitForSignal*>(waitFor); + QVERIFY(waitForSignal); + QCOMPARE(waitForSignal->emitterName(), emitterName); + QCOMPARE(waitForSignal->signalName(), signalName); +} + +QString TutorialReaderTest::addGarbageToXmlData(const QString& data) const { + QString garbagedData = data; + //Add a " garbage=\"trash\"" attribute to every element + garbagedData.replace(QRegExp("(<\\w+( \\w+=\"[ &;\"\\w]*\")*)(>|/>)"), + "\\1 garbage=\"trash\"\\3"); + //Add a "<garbage/>" element after each start element (even for text + //elements) + garbagedData.replace(QRegExp("(<\\w+( \\w+=\"[ &;\"\\w]*\")*>)"), + "\\1<garbage/>"); + return garbagedData; +} + +QTEST_MAIN(TutorialReaderTest) + +#include "TutorialReaderTest.moc" Property changes on: trunk/ktutorial/ktutorial-editor/tests/unit/serialization/TutorialReaderTest.cpp ___________________________________________________________________ Added: svn:eol-style + native This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |