/*************************************************************************** * Copyright (C) 2020 by Gustavo Sousa - gustavocms@gmail.com * * Copyright (C) 2020 by Stephen Lyons - slysven@virginmedia.com * * Copyright (C) 2023 by Michael Weller - michael.weller@t-online.de * * * * 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 2 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, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include #include #include #include #include #include #include #include "TMxpStubClient.h" /* * One can certainly argue if this should be in a separate module, but * I want to include only the handlers needed by this file into the module * for a sensible link time and executable size */ class TMxpStubHandlerContext : public TMxpStubContext { TMxpSendTagHandler sendTagHandler; TMxpFormattingTagsHandler formattingTagsHandler; TMxpColorTagHandler colorTagHandler; public: virtual TMxpTagHandlerResult handleTag(TMxpContext& ctx, TMxpClient& client, MxpTag* tag) { TMxpTagHandler *tagHandler; if (sendTagHandler.supports(ctx, client, tag)) { tagHandler = &sendTagHandler; } else if (formattingTagsHandler.supports(ctx, client, tag)) { tagHandler = &formattingTagsHandler; } else if (colorTagHandler.supports(ctx, client, tag)) { tagHandler = &colorTagHandler; } else { qDebug() << QString("unhandled Tag: [%1%2]").arg(tag->isEndTag() ? "/" : "").arg(tag->getName()); return MXP_TAG_HANDLED; } qDebug() << QString("handleTag([%1%2])").arg(tag->isEndTag() ? "/" : "").arg(tag->getName()); return tag->isStartTag() ? tagHandler->handleStartTag(ctx, client, tag->asStartTag()) : tagHandler->handleEndTag(ctx, client, tag->asEndTag()); } }; class TMxpCustomElementTagHandlerTest : public QObject { Q_OBJECT private: private slots: QSharedPointer parseNode(const QString& tagText) const { auto nodes = TMxpTagParser::parseToMxpNodeList(tagText); return nodes.size() > 0 ? nodes.first() : nullptr; } void testCustomItemCmd() { // Complex Example: Aldebaran defines a profile with definitions like this: // ' ATT='ID'> // Then uses a rock for brevity all over the mud (here plain ITem in Inventory) // Note the use of positional parameter ID (first arg) // Mudlet, up to version 4.10, made the hints ALL UPPER in this context TMxpStubHandlerContext ctx; TMxpStubClient stub; TMxpTagParser parser; ctx.getEntityResolver().registerEntity("&CMDH;", ""); auto defTag = parseNode("' ATT='ID'>)"); auto startTag = parseNode(""); auto endTag = parseNode(""); TMxpElementDefinitionHandler definitionHandler; definitionHandler.handleTag(ctx, stub, defTag->asStartTag()); TMxpCustomElementTagHandler customElementTagHandler; TMxpTagHandler& tagHandler = customElementTagHandler; tagHandler.handleTag(ctx, stub, startTag->asStartTag()); tagHandler.handleContent("a rock"); tagHandler.handleTag(ctx, stub, endTag->asEndTag()); QCOMPARE(stub.mHrefs.size(), 2); QCOMPARE(stub.mHrefs[0], "send([[examine inv##10]])"); QCOMPARE(stub.mHrefs[1], "send([[drop inv##10]])"); QCOMPARE(stub.mHints.size(), 2); QCOMPARE(stub.mHints[0], "examine"); QCOMPARE(stub.mHints[1], "drop"); } void testCustomElementDynamicEntity() { // Complex Example: Aldebaran has a global custom element MAP used in maps printed for // the player to click onto it and then follow the map. // However, it uses the id of the map currently looked at in an entity redefined for // each map you look at. // ' ATT='ID'> // Then uses a rock for brevity all over the mud (here plain ITem in Inventory) // Note the use of positional parameter ID (first arg) // Mudlet, up to version 4.10, made the hints ALL UPPER in this context TMxpStubHandlerContext ctx; TMxpStubClient stub; TMxpTagParser parser; ctx.getEntityResolver().registerEntity("&MID;", "map of the newbie jungle"); auto defTag = parseNode("' ATT='ID'>"); TMxpElementDefinitionHandler definitionHandler; definitionHandler.handleTag(ctx, stub, defTag->asStartTag()); auto startTag = parseNode(""); auto endTag = parseNode(""); TMxpCustomElementTagHandler customElementTagHandler; TMxpTagHandler& tagHandler = customElementTagHandler; tagHandler.handleTag(ctx, stub, startTag->asStartTag()); tagHandler.handleContent("*"); tagHandler.handleTag(ctx, stub, endTag->asEndTag()); QCOMPARE(stub.mHrefs.size(), 1); QCOMPARE(stub.mHrefs[0], "send([[follow map of the newbie jungle to P8x7]])"); QCOMPARE(stub.mHints.size(), 1); QCOMPARE(stub.mHints[0], "go here"); // Now player looks at another map and gets an MXP button to go to another place. // Note the MAP element is NOT redefined, only the entity. ctx.getEntityResolver().registerEntity("&MID;", "map of the south forest"); startTag = parseNode(""); endTag = parseNode(""); tagHandler.handleTag(ctx, stub, startTag->asStartTag()); tagHandler.handleContent("*"); tagHandler.handleTag(ctx, stub, endTag->asEndTag()); QCOMPARE(stub.mHrefs.size(), 1); QCOMPARE(stub.mHrefs[0], "send([[follow map of the south forest to P42]])"); QCOMPARE(stub.mHints.size(), 1); QCOMPARE(stub.mHints[0], "go here"); } void testCustomElementDefaultAttributes() { // This example literally taken from the MXP definition at https://www.zuggsoft.com/zmud/mxp.htm#ELEMENT // // ' ATT='col=red'> // // Then you could use it on the MUD like this: // // This is bold red // This is bold blue text // This is also bold blue text TMxpStubHandlerContext ctx; TMxpStubClient stub; TMxpTagParser parser; auto defTag = parseNode("' ATT='col=red'>"); TMxpElementDefinitionHandler definitionHandler; definitionHandler.handleTag(ctx, stub, defTag->asStartTag()); auto startTag = parseNode(""); auto endTag = parseNode(""); TMxpCustomElementTagHandler customElementTagHandler; TMxpTagHandler& tagHandler = customElementTagHandler; tagHandler.handleTag(ctx, stub, startTag->asStartTag()); tagHandler.handleContent("This is bold red"); // is it? QCOMPARE(stub.bold(), true); QCOMPARE(stub.fgColor, "red"); tagHandler.handleTag(ctx, stub, endTag->asEndTag()); // back to defaults: QCOMPARE(stub.bold(), false); QCOMPARE(stub.fgColor, ""); startTag = parseNode(")"); tagHandler.handleTag(ctx, stub, startTag->asStartTag()); tagHandler.handleContent("This is bold blue text"); // is it? QCOMPARE(stub.bold(), true); QCOMPARE(stub.fgColor, "blue"); tagHandler.handleTag(ctx, stub, endTag->asEndTag()); // back to defaults: QCOMPARE(stub.bold(), false); QCOMPARE(stub.fgColor, ""); startTag = parseNode(")"); tagHandler.handleTag(ctx, stub, startTag->asStartTag()); tagHandler.handleContent("This is also bold blue text"); // is it? QCOMPARE(stub.bold(), true); QCOMPARE(stub.fgColor, "blue"); tagHandler.handleTag(ctx, stub, endTag->asEndTag()); // back to defaults: QCOMPARE(stub.bold(), false); QCOMPARE(stub.fgColor, ""); } void testCustomElementPlayer() { // Real life example from Aldebaran: (there are more sensible ones with EXPIRE which Mudlet does not yet support) // // ' ATT='NAME=someone'> // // Used like Player says: Hello! // However, if player is invisible, playerid is empty, like Someone says: Hello! // // Upper and lower case are mixed in the entity name to make this a more severe test case TMxpStubHandlerContext ctx; TMxpStubClient stub; TMxpTagParser parser; auto defTag = parseNode("' ATT='NAme=someone'>"); TMxpElementDefinitionHandler definitionHandler; definitionHandler.handleTag(ctx, stub, defTag->asStartTag()); auto startTag = parseNode(""); auto endTag = parseNode(""); TMxpCustomElementTagHandler customElementTagHandler; TMxpTagHandler& tagHandler = customElementTagHandler; tagHandler.handleTag(ctx, stub, startTag->asStartTag()); tagHandler.handleContent("Player"); tagHandler.handleTag(ctx, stub, endTag->asEndTag()); QCOMPARE(stub.mHrefs.size(), 3); QCOMPARE(stub.mHrefs[0], "printCmdLine([[whisper playerid ]])"); QCOMPARE(stub.mHrefs[1], "printCmdLine([[finger playerid ]])"); QCOMPARE(stub.mHrefs[2], "printCmdLine([[tell playerid ]])"); QCOMPARE(stub.mHints.size(), 3); QCOMPARE(stub.mHints[0], "whisper playerid"); QCOMPARE(stub.mHints[1], "finger playerid"); QCOMPARE(stub.mHints[2], "tell playerid"); // Now w/o a NAME parameter given: startTag = parseNode(""); tagHandler.handleTag(ctx, stub, startTag->asStartTag()); tagHandler.handleContent("Invisible SuperAdmin"); tagHandler.handleTag(ctx, stub, endTag->asEndTag()); QCOMPARE(stub.mHrefs.size(), 3); QCOMPARE(stub.mHrefs[0], "printCmdLine([[whisper someone ]])"); QCOMPARE(stub.mHrefs[1], "printCmdLine([[finger someone ]])"); QCOMPARE(stub.mHrefs[2], "printCmdLine([[tell someone ]])"); QCOMPARE(stub.mHints.size(), 3); QCOMPARE(stub.mHints[0], "whisper someone"); QCOMPARE(stub.mHints[1], "finger someone"); QCOMPARE(stub.mHints[2], "tell someone"); } void testCustomElementAttributePassThrough() { // Test that attributes from custom element tags are passed through to the definition // '> // North // Should result in: North TMxpStubHandlerContext ctx; TMxpStubClient stub; // Define custom element that expands to auto defTag = parseNode("'>"); TMxpElementDefinitionHandler definitionHandler; definitionHandler.handleTag(ctx, stub, defTag->asStartTag()); // Use custom element with EXPIRE and HREF attributes auto startTag = parseNode(""); auto endTag = parseNode(""); TMxpCustomElementTagHandler customElementTagHandler; TMxpTagHandler& tagHandler = customElementTagHandler; tagHandler.handleTag(ctx, stub, startTag->asStartTag()); tagHandler.handleContent("North"); tagHandler.handleTag(ctx, stub, endTag->asEndTag()); // Verify the attributes were passed through correctly QCOMPARE(stub.mHrefs.size(), 1); QCOMPARE(stub.mHrefs[0], "send([[north]])"); QCOMPARE(stub.mHints.size(), 1); QCOMPARE(stub.mHints[0], "north"); QCOMPARE(stub.mExpireName, "Exits"); } }; #include "TMxpCustomElementTagHandlerTest.moc" QTEST_MAIN(TMxpCustomElementTagHandlerTest)