2022-04-08 11:08:24 +02:00
|
|
|
/***************************************************************************
|
|
|
|
|
* Copyright (C) 2022 by Gustavo Sousa - gustavocms@gmail.com *
|
|
|
|
|
* *
|
|
|
|
|
* 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 "TMxpEntityTagHandler.h"
|
|
|
|
|
#include "TMxpStubClient.h"
|
2026-03-21 13:45:49 -04:00
|
|
|
#include <QTest>
|
|
|
|
|
#include <TMxpProcessor.h>
|
2022-04-08 11:08:24 +02:00
|
|
|
#include <TMxpTagParser.h>
|
|
|
|
|
#include <TMxpTagProcessor.h>
|
|
|
|
|
|
|
|
|
|
class TMxpEntityTagHandlerTest : public QObject {
|
2026-03-21 13:45:49 -04:00
|
|
|
Q_OBJECT
|
2022-04-08 11:08:24 +02:00
|
|
|
|
|
|
|
|
private:
|
2026-03-21 13:45:49 -04:00
|
|
|
static QSharedPointer<MxpNode> parseNode(const QString &tagText) {
|
|
|
|
|
auto nodes = TMxpTagParser::parseToMxpNodeList(tagText);
|
|
|
|
|
return !nodes.empty() ? nodes.first() : nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void processInput(TMxpProcessor &processor, const std::string& input) {
|
|
|
|
|
for (char ch : input) {
|
|
|
|
|
processor.processMxpInput(ch, true);
|
2022-04-08 11:08:24 +02:00
|
|
|
}
|
2026-03-21 13:45:49 -04:00
|
|
|
}
|
2022-04-08 11:08:24 +02:00
|
|
|
|
2026-03-21 13:45:49 -04:00
|
|
|
private slots:
|
|
|
|
|
void testPublish() {
|
|
|
|
|
TMxpStubClient stub;
|
|
|
|
|
TMxpTagProcessor processor;
|
|
|
|
|
|
|
|
|
|
auto tag = parseNode("<!EN ob \"street lamp\" publish>");
|
|
|
|
|
processor.handleNode(processor, stub, tag.get());
|
|
|
|
|
|
|
|
|
|
QCOMPARE(processor.getEntityResolver().getResolution("&ob;"),
|
|
|
|
|
"street lamp");
|
|
|
|
|
|
|
|
|
|
QCOMPARE(stub.mPublishedEntityName, "&ob;");
|
|
|
|
|
QCOMPARE(stub.mPublishedEntityValue, "street lamp");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void testPrivate() {
|
|
|
|
|
TMxpStubClient stub;
|
|
|
|
|
TMxpTagProcessor processor;
|
|
|
|
|
|
|
|
|
|
auto tag = parseNode("<!EN ob \"street lamp\" private>");
|
|
|
|
|
processor.handleNode(processor, stub, tag.get());
|
|
|
|
|
|
|
|
|
|
QCOMPARE(processor.getEntityResolver().getResolution("&ob;"),
|
|
|
|
|
"street lamp");
|
|
|
|
|
|
|
|
|
|
QCOMPARE(stub.mPublishedEntityName, "");
|
|
|
|
|
QCOMPARE(stub.mPublishedEntityValue, "");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void testAddRemoveStart() {
|
|
|
|
|
TMxpStubClient stub;
|
|
|
|
|
TMxpTagProcessor processor;
|
|
|
|
|
|
|
|
|
|
processor.getEntityResolver().registerEntity("&entity;", "v1");
|
|
|
|
|
QCOMPARE(processor.getEntityResolver().getResolution("&entity;"), "v1");
|
2022-04-08 11:08:24 +02:00
|
|
|
|
2026-03-21 13:45:49 -04:00
|
|
|
processor.handleNode(processor, stub,
|
|
|
|
|
parseNode("<!EN entity \"v2\" add>").get());
|
|
|
|
|
QCOMPARE(processor.getEntityResolver().getResolution("&entity;"), "v1|v2");
|
|
|
|
|
|
|
|
|
|
processor.handleNode(processor, stub,
|
|
|
|
|
parseNode("<!EN entity \"v1\" remove>").get());
|
|
|
|
|
QCOMPARE(processor.getEntityResolver().getResolution("&entity;"), "v2");
|
|
|
|
|
|
|
|
|
|
processor.handleNode(processor, stub,
|
|
|
|
|
parseNode("<!EN entity \"v2\" remove>").get());
|
|
|
|
|
QCOMPARE(processor.getEntityResolver().getResolution("&entity;"), "");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void testAddRemoveEnd() {
|
|
|
|
|
TMxpStubClient stub;
|
|
|
|
|
TMxpTagProcessor processor;
|
|
|
|
|
|
|
|
|
|
processor.getEntityResolver().registerEntity("&entity;", "v1");
|
|
|
|
|
QCOMPARE(processor.getEntityResolver().getResolution("&entity;"), "v1");
|
2022-04-08 11:08:24 +02:00
|
|
|
|
2026-03-21 13:45:49 -04:00
|
|
|
processor.handleNode(processor, stub,
|
|
|
|
|
parseNode("<!EN entity \"v2\" add>").get());
|
|
|
|
|
QCOMPARE(processor.getEntityResolver().getResolution("&entity;"), "v1|v2");
|
2022-04-08 11:08:24 +02:00
|
|
|
|
2026-03-21 13:45:49 -04:00
|
|
|
processor.handleNode(processor, stub,
|
|
|
|
|
parseNode("<!EN entity \"v2\" remove>").get());
|
|
|
|
|
QCOMPARE(processor.getEntityResolver().getResolution("&entity;"), "v1");
|
|
|
|
|
}
|
2022-04-08 11:08:24 +02:00
|
|
|
|
2026-03-21 13:45:49 -04:00
|
|
|
void testAddRemovePartOfItemValue() {
|
|
|
|
|
TMxpStubClient stub;
|
|
|
|
|
TMxpTagProcessor processor;
|
Fix: Cannot delete an MXP entity which is not all lower case (#6854)
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
If a mud creates an MXP entity that is no all lowercase, it cannot be
deleted.
This is because entity names are managed as all lowercase internally...
Except for <!EN ... DELETE> which will fail to find the entity
definition if the name is not all lowercase.
The PR also extends TMxpEntityTagHandlerTest.cpp to check for this
issue.
#### Motivation for adding to Mudlet
Improve MXP compliance by fixing this issue.
#### Other info (issues closed, discussion etc)
I have no real life example for this issue, AFAIK most games don't
delete entities, they just redefine them when needed.
Actually, <B>note</B> that the MXP protocol specifies entities as case
sensitive: <CITE>Also note that as in XML, entities are case
sensitive, so &Start; is different than &start;</CITE>.
Mudlet breaks this requirement. However, entity names are converted to
lower case all over the place in the code. It will be quite painful to
remove this in all places. I also fear that some muds may already rely
on this incompliant behaviour of Mudlet, so it might be better to leave
this as it is. IMHO, a sensible mud developer/coder should really not
use entities which just differ by case.
2023-05-30 19:16:43 +02:00
|
|
|
|
2026-03-21 13:45:49 -04:00
|
|
|
processor.getEntityResolver().registerEntity("&entity;", "my text example");
|
|
|
|
|
QCOMPARE(processor.getEntityResolver().getResolution("&entity;"),
|
|
|
|
|
"my text example");
|
Fix: Cannot delete an MXP entity which is not all lower case (#6854)
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
If a mud creates an MXP entity that is no all lowercase, it cannot be
deleted.
This is because entity names are managed as all lowercase internally...
Except for <!EN ... DELETE> which will fail to find the entity
definition if the name is not all lowercase.
The PR also extends TMxpEntityTagHandlerTest.cpp to check for this
issue.
#### Motivation for adding to Mudlet
Improve MXP compliance by fixing this issue.
#### Other info (issues closed, discussion etc)
I have no real life example for this issue, AFAIK most games don't
delete entities, they just redefine them when needed.
Actually, <B>note</B> that the MXP protocol specifies entities as case
sensitive: <CITE>Also note that as in XML, entities are case
sensitive, so &Start; is different than &start;</CITE>.
Mudlet breaks this requirement. However, entity names are converted to
lower case all over the place in the code. It will be quite painful to
remove this in all places. I also fear that some muds may already rely
on this incompliant behaviour of Mudlet, so it might be better to leave
this as it is. IMHO, a sensible mud developer/coder should really not
use entities which just differ by case.
2023-05-30 19:16:43 +02:00
|
|
|
|
2026-03-21 13:45:49 -04:00
|
|
|
processor.handleNode(processor, stub,
|
|
|
|
|
parseNode("<!EN entity \"my value\" add>").get());
|
|
|
|
|
QCOMPARE(processor.getEntityResolver().getResolution("&entity;"),
|
|
|
|
|
"my text example|my value");
|
2023-05-13 16:11:19 +02:00
|
|
|
|
2026-03-21 13:45:49 -04:00
|
|
|
processor.handleNode(processor, stub,
|
|
|
|
|
parseNode("<!EN entity \"my text\" remove>").get());
|
|
|
|
|
QCOMPARE(processor.getEntityResolver().getResolution("&entity;"),
|
|
|
|
|
"my text example|my value");
|
2023-05-13 16:11:19 +02:00
|
|
|
|
2026-03-21 13:45:49 -04:00
|
|
|
processor.handleNode(processor, stub,
|
|
|
|
|
parseNode("<!EN entity \"example\" remove>").get());
|
|
|
|
|
QCOMPARE(processor.getEntityResolver().getResolution("&entity;"),
|
|
|
|
|
"my text example|my value");
|
2023-05-13 16:11:19 +02:00
|
|
|
|
2026-03-21 13:45:49 -04:00
|
|
|
processor.handleNode(processor, stub,
|
|
|
|
|
parseNode("<!EN entity \"value\" remove>").get());
|
|
|
|
|
QCOMPARE(processor.getEntityResolver().getResolution("&entity;"),
|
|
|
|
|
"my text example|my value");
|
2023-05-13 16:11:19 +02:00
|
|
|
|
2026-03-21 13:45:49 -04:00
|
|
|
processor.handleNode(processor, stub,
|
|
|
|
|
parseNode("<!EN entity \"my\" remove>").get());
|
|
|
|
|
QCOMPARE(processor.getEntityResolver().getResolution("&entity;"),
|
|
|
|
|
"my text example|my value");
|
2023-05-13 16:11:19 +02:00
|
|
|
|
2026-03-21 13:45:49 -04:00
|
|
|
processor.handleNode(processor, stub,
|
|
|
|
|
parseNode("<!EN entity \"my v\" remove>").get());
|
|
|
|
|
QCOMPARE(processor.getEntityResolver().getResolution("&entity;"),
|
|
|
|
|
"my text example|my value");
|
|
|
|
|
}
|
2023-05-13 16:11:19 +02:00
|
|
|
|
2026-03-21 13:45:49 -04:00
|
|
|
void testInterpolation() {
|
|
|
|
|
TMxpStubClient stub;
|
|
|
|
|
TMxpProcessor processor(&stub);
|
|
|
|
|
processor.setMode(6); // !EN and SEND are SECURE tags
|
2023-05-13 16:11:19 +02:00
|
|
|
|
2026-03-21 13:45:49 -04:00
|
|
|
std::string input =
|
|
|
|
|
"<!EN ob \"street lamp\" private><send href=\"kill &ob;\">kill</send>";
|
|
|
|
|
processInput(processor, input);
|
2023-05-13 16:11:19 +02:00
|
|
|
|
2026-03-21 13:45:49 -04:00
|
|
|
QCOMPARE(stub.mHrefs.size(), 1);
|
|
|
|
|
QCOMPARE(stub.mHrefs[0], "send([[kill street lamp]])");
|
|
|
|
|
|
|
|
|
|
QCOMPARE(stub.mHints.size(), 1);
|
|
|
|
|
QCOMPARE(stub.mHints[0], "kill street lamp");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void testDelete() {
|
|
|
|
|
TMxpStubClient stub;
|
|
|
|
|
TMxpTagProcessor processor;
|
|
|
|
|
|
|
|
|
|
processor.getEntityResolver().registerEntity("&entity;", "v1");
|
|
|
|
|
QCOMPARE(processor.getEntityResolver().getResolution("&entity;"), "v1");
|
|
|
|
|
|
|
|
|
|
processor.handleNode(processor, stub,
|
|
|
|
|
parseNode("<!EN entity delete>").get());
|
|
|
|
|
QCOMPARE(processor.getEntityResolver().getResolution("&entity;"),
|
|
|
|
|
"&entity;");
|
|
|
|
|
|
|
|
|
|
processor.getEntityResolver().registerEntity("&myEntity;", "v2");
|
|
|
|
|
QCOMPARE(processor.getEntityResolver().getResolution("&myEntity;"), "v2");
|
|
|
|
|
|
|
|
|
|
processor.handleNode(processor, stub,
|
|
|
|
|
parseNode("<!EN myEntity delete>").get());
|
|
|
|
|
QCOMPARE(processor.getEntityResolver().getResolution("&myEntity;"),
|
|
|
|
|
"&myEntity;");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void testEntityModification() {
|
|
|
|
|
TMxpStubClient stub;
|
|
|
|
|
TMxpTagProcessor processor;
|
|
|
|
|
|
|
|
|
|
processor.getEntityResolver().registerEntity("&entity;", "v1");
|
|
|
|
|
QCOMPARE(processor.getEntityResolver().getResolution("&entity;"), "v1");
|
|
|
|
|
|
|
|
|
|
processor.handleNode(processor, stub, parseNode("<!EN entity ''>").get());
|
|
|
|
|
QCOMPARE(processor.getEntityResolver().getResolution("&entity;"), "");
|
|
|
|
|
|
|
|
|
|
processor.handleNode(processor, stub, parseNode("<!EN entity V2>").get());
|
|
|
|
|
QCOMPARE(processor.getEntityResolver().getResolution("&entity;"), "V2");
|
|
|
|
|
|
|
|
|
|
processor.handleNode(processor, stub, parseNode("<!EN entity>").get());
|
|
|
|
|
QCOMPARE(processor.getEntityResolver().getResolution("&entity;"), "");
|
|
|
|
|
|
|
|
|
|
processor.handleNode(processor, stub, parseNode("<!EN entity V3>").get());
|
|
|
|
|
QCOMPARE(processor.getEntityResolver().getResolution("&entity;"), "V3");
|
|
|
|
|
|
|
|
|
|
processor.handleNode(processor, stub, parseNode("<!EN entity \"\">").get());
|
|
|
|
|
QCOMPARE(processor.getEntityResolver().getResolution("&entity;"), "");
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-27 19:55:26 +02:00
|
|
|
// <!ENTITY> values must be decoded with the session encoding rather than
|
|
|
|
|
// hardcoded UTF-8 - covers quoted and unquoted values in a WINDOWS-1251
|
|
|
|
|
// session ("Гроза" = bytes C3 F0 EE E7 E0)
|
|
|
|
|
void testNonUtf8SessionEntityValue() {
|
|
|
|
|
TMxpStubClient stub;
|
|
|
|
|
stub.mEncoding = QByteArrayLiteral("WINDOWS-1251");
|
|
|
|
|
TMxpProcessor processor(&stub);
|
|
|
|
|
processor.setMode(6);
|
|
|
|
|
|
|
|
|
|
processInput(processor, "<!ENTITY storm \"\xC3\xF0\xEE\xE7\xE0\">");
|
|
|
|
|
processInput(processor, "<!ENTITY storm2 \xC3\xF0\xEE\xE7\xE0>");
|
|
|
|
|
|
|
|
|
|
TEntityResolver &resolver =
|
|
|
|
|
processor.getMxpTagProcessor().getEntityResolver();
|
|
|
|
|
QCOMPARE(resolver.getResolution("&storm;"), QString("Гроза"));
|
|
|
|
|
QCOMPARE(resolver.getResolution("&storm2;"), QString("Гроза"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// UTF-8 sessions must keep resolving non-Latin1 entity values unchanged
|
|
|
|
|
void testUtf8SessionEntityValue() {
|
|
|
|
|
TMxpStubClient stub;
|
|
|
|
|
TMxpProcessor processor(&stub);
|
|
|
|
|
processor.setMode(6);
|
|
|
|
|
|
|
|
|
|
processInput(processor, "<!ENTITY storm \"Гроза\">");
|
|
|
|
|
|
|
|
|
|
QCOMPARE(
|
|
|
|
|
processor.getMxpTagProcessor().getEntityResolver().getResolution(
|
|
|
|
|
"&storm;"),
|
|
|
|
|
QString("Гроза"));
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 13:45:49 -04:00
|
|
|
void testEmptyEntityInterpolation() {
|
|
|
|
|
TMxpStubClient stub;
|
|
|
|
|
TMxpProcessor mxpProcessor(&stub);
|
|
|
|
|
mxpProcessor.setMode(6);
|
2023-05-13 16:11:19 +02:00
|
|
|
|
2026-03-21 13:45:49 -04:00
|
|
|
std::string input = "<!en entity ''><send href=\"examine ob&entity;\" "
|
|
|
|
|
"hint=\"examine&entity;\">examine</send>";
|
|
|
|
|
processInput(mxpProcessor, input);
|
2023-05-13 16:11:19 +02:00
|
|
|
|
2026-03-21 13:45:49 -04:00
|
|
|
QCOMPARE(stub.mHrefs.size(), 1);
|
|
|
|
|
QCOMPARE(stub.mHrefs[0], "send([[examine ob]])");
|
|
|
|
|
|
|
|
|
|
QCOMPARE(stub.mHints.size(), 1);
|
|
|
|
|
QCOMPARE(stub.mHints[0], "examine");
|
|
|
|
|
}
|
2022-04-08 11:08:24 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#include "TMxpEntityTagHandlerTest.moc"
|
|
|
|
|
QTEST_MAIN(TMxpEntityTagHandlerTest)
|