File indexing completed on 2024-06-09 03:58:20

0001 /*
0002   This file is part of the KTextTemplate library
0003 
0004   SPDX-FileCopyrightText: 2009, 2010 Stephen Kelly <steveire@gmail.com>
0005 
0006   SPDX-License-Identifier: LGPL-2.1-or-later
0007 
0008 */
0009 
0010 #ifndef FILTERSTEST_H
0011 #define FILTERSTEST_H
0012 
0013 #include <QDebug>
0014 #include <QDir>
0015 #include <QFileInfo>
0016 #include <QTest>
0017 
0018 #include "context.h"
0019 #include "engine.h"
0020 #include "ktexttemplate_paths.h"
0021 #include "template.h"
0022 #include <util.h>
0023 
0024 using Dict = QHash<QString, QVariant>;
0025 
0026 Q_DECLARE_METATYPE(KTextTemplate::Error)
0027 
0028 using namespace KTextTemplate;
0029 
0030 class TestFilters : public QObject
0031 {
0032     Q_OBJECT
0033 
0034 private Q_SLOTS:
0035     void initTestCase();
0036     void cleanupTestCase();
0037 
0038     void testDateBasedFilters_data();
0039     void testDateBasedFilters()
0040     {
0041         doTest();
0042     }
0043 
0044     void testStringFilters_data();
0045     void testStringFilters()
0046     {
0047         doTest();
0048     }
0049 
0050     void testListFilters_data();
0051     void testListFilters()
0052     {
0053         doTest();
0054     }
0055 
0056     void testLogicFilters_data();
0057     void testLogicFilters()
0058     {
0059         doTest();
0060     }
0061 
0062     void testMiscFilters_data();
0063     void testMiscFilters()
0064     {
0065         doTest();
0066     }
0067 
0068     void testIntegerFilters_data();
0069     void testIntegerFilters()
0070     {
0071         doTest();
0072     }
0073 
0074 private:
0075     void doTest();
0076 
0077     QSharedPointer<InMemoryTemplateLoader> loader;
0078     Engine *m_engine = nullptr;
0079 };
0080 
0081 void TestFilters::initTestCase()
0082 {
0083     m_engine = new Engine(this);
0084 
0085     loader = QSharedPointer<InMemoryTemplateLoader>(new InMemoryTemplateLoader());
0086     m_engine->addTemplateLoader(loader);
0087 
0088     auto appDirPath = QFileInfo(QCoreApplication::applicationDirPath()).absoluteDir().path();
0089     m_engine->setPluginPaths({
0090         QStringLiteral(KTEXTTEMPLATE_PLUGIN_PATH),
0091         appDirPath + QStringLiteral("/tests/") // For testtags.qs
0092     });
0093 }
0094 
0095 void TestFilters::cleanupTestCase()
0096 {
0097     delete m_engine;
0098 }
0099 
0100 void TestFilters::doTest()
0101 {
0102     QFETCH(QString, input);
0103     QFETCH(Dict, dict);
0104     QFETCH(QString, output);
0105     QFETCH(KTextTemplate::Error, error);
0106 
0107     auto t = m_engine->newTemplate(input, QLatin1String(QTest::currentDataTag()));
0108 
0109     Context context(dict);
0110 
0111     auto result = t->render(&context);
0112 
0113     if (t->error() != NoError) {
0114         if (t->error() != error)
0115             qDebug() << t->errorString();
0116         QCOMPARE(t->error(), error);
0117         return;
0118     }
0119 
0120     // Didn't catch any errors, so make sure I didn't expect any.
0121     QCOMPARE(NoError, error);
0122 
0123     QCOMPARE(t->error(), NoError);
0124 
0125     QCOMPARE(result, output);
0126 }
0127 
0128 void TestFilters::testDateBasedFilters_data()
0129 {
0130     QTest::addColumn<QString>("input");
0131     QTest::addColumn<Dict>("dict");
0132     QTest::addColumn<QString>("output");
0133     QTest::addColumn<KTextTemplate::Error>("error");
0134 
0135     Dict dict;
0136     auto now = QDateTime::currentDateTimeUtc();
0137 
0138     dict.insert(QStringLiteral("a"), now.addSecs(-70));
0139 
0140     QTest::newRow("filter-timesince01") << QStringLiteral("{{ a|timesince }}") << dict << QStringLiteral("1 minute") << NoError;
0141 
0142     dict.clear();
0143 
0144     dict.insert(QStringLiteral("a"), now.addDays(-1).addSecs(-60));
0145 
0146     QTest::newRow("filter-timesince02") << QStringLiteral("{{ a|timesince }}") << dict << QStringLiteral("1 day") << NoError;
0147 
0148     dict.clear();
0149 
0150     dict.insert(QStringLiteral("a"), now.addSecs(-1 * 60 * 60).addSecs(-1 * 25 * 60).addSecs(-1 * 10));
0151     QTest::newRow("filter-timesince03") << QStringLiteral("{{ a|timesince }}") << dict << QStringLiteral("1 hour, 25 minutes") << NoError;
0152 
0153     dict.clear();
0154 
0155     //  Compare to a given parameter
0156 
0157     dict.insert(QStringLiteral("a"), now.addDays(-2));
0158     dict.insert(QStringLiteral("b"), now.addDays(-1));
0159 
0160     QTest::newRow("filter-timesince04") << QStringLiteral("{{ a|timesince:b }}") << dict << QStringLiteral("1 day") << NoError;
0161 
0162     dict.clear();
0163 
0164     dict.insert(QStringLiteral("a"), now.addDays(-2).addSecs(-60));
0165     dict.insert(QStringLiteral("b"), now.addDays(-2));
0166 
0167     QTest::newRow("filter-timesince05") << QStringLiteral("{{ a|timesince:b }}") << dict << QStringLiteral("1 minute") << NoError;
0168 
0169     dict.clear();
0170 
0171     //  Check that timezone is respected
0172 
0173     //  {"a":now_tz - timedelta(hours=8), "b":now_tz
0174     //   QTest::newRow( "filter-timesince06" ) << QString::fromLatin1( "{{
0175     //   a|timesince:b }}" ) << dict << QString::fromLatin1( "8 hours" ) <<
0176     //   NoError;
0177 
0178     dict.insert(QStringLiteral("earlier"), now.addDays(-7));
0179     QTest::newRow("filter-timesince07") << QStringLiteral("{{ earlier|timesince }}") << dict << QStringLiteral("1 week") << NoError;
0180 
0181     dict.clear();
0182 
0183     dict.insert(QStringLiteral("now"), now);
0184     dict.insert(QStringLiteral("earlier"), now.addDays(-7));
0185 
0186     QTest::newRow("filter-timesince08") << QStringLiteral("{{ earlier|timesince:now }}") << dict << QStringLiteral("1 week") << NoError;
0187 
0188     dict.clear();
0189 
0190     dict.insert(QStringLiteral("later"), now.addDays(7));
0191 
0192     QTest::newRow("filter-timesince09") << QStringLiteral("{{ later|timesince }}") << dict << QStringLiteral("0 minutes") << NoError;
0193 
0194     dict.clear();
0195 
0196     dict.insert(QStringLiteral("now"), now);
0197     dict.insert(QStringLiteral("later"), now.addDays(7));
0198 
0199     QTest::newRow("filter-timesince10") << QStringLiteral("{{ later|timesince:now }}") << dict << QStringLiteral("0 minutes") << NoError;
0200 
0201     //  Ensures that differing timezones are calculated correctly
0202 
0203     //  {"a": now
0204     //   QTest::newRow( "filter-timesince11" ) << QString::fromLatin1( "{{
0205     //   a|timesince }}" ) << dict << QString::fromLatin1( "0 minutes" ) <<
0206     //   NoError;
0207 
0208     //  {"a": now_tz
0209     //   QTest::newRow( "filter-timesince12" ) << QString::fromLatin1( "{{
0210     //   a|timesince }}" ) << dict << QString::fromLatin1( "0 minutes" ) <<
0211     //   NoError;
0212 
0213     //  {"a": now_tz_i
0214     //   QTest::newRow( "filter-timesince13" ) << QString::fromLatin1( "{{
0215     //   a|timesince }}" ) << dict << QString::fromLatin1( "0 minutes" ) <<
0216     //   NoError;
0217 
0218     //  {"a": now_tz, "b": now_tz_i
0219     //   QTest::newRow( "filter-timesince14" ) << QString::fromLatin1( "{{
0220     //   a|timesince:b }}" ) << dict << QString::fromLatin1( "0 minutes" ) <<
0221     //   NoError;
0222 
0223     //  {"a": now, "b": now_tz_i
0224     //   QTest::newRow( "filter-timesince15" ) << QString::fromLatin1( "{{
0225     //   a|timesince:b }}" ) << dict << QString() << NoError;
0226 
0227     //  {"a": now_tz_i, "b": now
0228     //   QTest::newRow( "filter-timesince16" ) << QString::fromLatin1( "{{
0229     //   a|timesince:b }}" ) << dict << QString() << NoError;
0230 
0231     dict.clear();
0232 
0233     dict.insert(QStringLiteral("a"), now);
0234     dict.insert(QStringLiteral("b"), now);
0235 
0236     QTest::newRow("filter-timesince17") << QStringLiteral("{{ a|timesince:b }}") << dict << QStringLiteral("0 minutes") << NoError;
0237 
0238     dict.clear();
0239 
0240     dict.insert(QStringLiteral("a"), now);
0241     dict.insert(QStringLiteral("b"), now.addDays(1));
0242 
0243     QTest::newRow("filter-timesince18") << QStringLiteral("{{ a|timesince:b }}") << dict << QStringLiteral("1 day") << NoError;
0244 
0245     dict.clear();
0246     QTest::newRow("filter-timesince19") << QStringLiteral("{{xx|timesince}}") << dict << QStringLiteral("") << NoError;
0247     QTest::newRow("filter-timesince20") << QStringLiteral("{{|timesince}}") << dict << QStringLiteral("") << NoError;
0248 
0249     //  Default compare with datetime.now()
0250 
0251     dict.clear();
0252     dict.insert(QStringLiteral("a"), now.addSecs(130));
0253 
0254     QTest::newRow("filter-timeuntil01") << QStringLiteral("{{ a|timeuntil }}") << dict << QStringLiteral("2 minutes") << NoError;
0255 
0256     dict.clear();
0257     dict.insert(QStringLiteral("a"), now.addDays(1).addSecs(10));
0258 
0259     QTest::newRow("filter-timeuntil02") << QStringLiteral("{{ a|timeuntil }}") << dict << QStringLiteral("1 day") << NoError;
0260 
0261     dict.clear();
0262     dict.insert(QStringLiteral("a"), now.addSecs(60 * 60 * 8).addSecs(610));
0263 
0264     QTest::newRow("filter-timeuntil03") << QStringLiteral("{{ a|timeuntil }}") << dict << QStringLiteral("8 hours, 10 minutes") << NoError;
0265 
0266     //  Compare to a given parameter
0267 
0268     dict.clear();
0269     dict.insert(QStringLiteral("a"), now.addDays(-1));
0270     dict.insert(QStringLiteral("b"), now.addDays(-2));
0271 
0272     QTest::newRow("filter-timeuntil04") << QStringLiteral("{{ a|timeuntil:b }}") << dict << QStringLiteral("1 day") << NoError;
0273 
0274     dict.clear();
0275     dict.insert(QStringLiteral("a"), now.addDays(-1));
0276     dict.insert(QStringLiteral("b"), now.addDays(-1).addSecs(-60));
0277 
0278     QTest::newRow("filter-timeuntil05") << QStringLiteral("{{ a|timeuntil:b }}") << dict << QStringLiteral("1 minute") << NoError;
0279 
0280     dict.clear();
0281     dict.insert(QStringLiteral("earlier"), now.addDays(-7));
0282 
0283     QTest::newRow("filter-timeuntil06") << QStringLiteral("{{ earlier|timeuntil }}") << dict << QStringLiteral("0 minutes") << NoError;
0284 
0285     dict.clear();
0286     dict.insert(QStringLiteral("now"), now);
0287     dict.insert(QStringLiteral("earlier"), now.addDays(-7));
0288 
0289     QTest::newRow("filter-timeuntil07") << QStringLiteral("{{ earlier|timeuntil:now }}") << dict << QStringLiteral("0 minutes") << NoError;
0290 
0291     dict.clear();
0292     dict.insert(QStringLiteral("later"), now.addDays(7).addSecs(5));
0293 
0294     QTest::newRow("filter-timeuntil08") << QStringLiteral("{{ later|timeuntil }}") << dict << QStringLiteral("1 week") << NoError;
0295 
0296     dict.clear();
0297     dict.insert(QStringLiteral("now"), now);
0298     dict.insert(QStringLiteral("later"), now.addDays(7));
0299 
0300     QTest::newRow("filter-timeuntil09") << QStringLiteral("{{ later|timeuntil:now }}") << dict << QStringLiteral("1 week") << NoError;
0301 
0302     //  Ensures that differing timezones are calculated correctly
0303     //
0304     //   //  {"a": now_tz_i
0305     //   QTest::newRow( "filter-timeuntil10" ) << QString::fromLatin1( "{{
0306     //   a|timeuntil }}" ) << dict << QString::fromLatin1( "0 minutes" ) <<
0307     //   NoError;
0308     //
0309     //   //  {"a": now_tz_i, "b": now_tz
0310     //   QTest::newRow( "filter-timeuntil11" ) << QString::fromLatin1( "{{
0311     //   a|timeuntil:b }}" ) << dict << QString::fromLatin1( "0 minutes" ) <<
0312     //   NoError;
0313 
0314     dict.clear();
0315     dict.insert(QStringLiteral("a"), now);
0316     dict.insert(QStringLiteral("b"), now);
0317     QTest::newRow("filter-timeuntil12") << QStringLiteral("{{ a|timeuntil:b }}") << dict << QStringLiteral("0 minutes") << NoError;
0318 
0319     dict.clear();
0320     dict.insert(QStringLiteral("a"), now);
0321     dict.insert(QStringLiteral("b"), now.addDays(-1));
0322 
0323     QTest::newRow("filter-timeuntil13") << QStringLiteral("{{ a|timeuntil:b }}") << dict << QStringLiteral("1 day") << NoError;
0324 
0325     dict.clear();
0326     QTest::newRow("filter-timeuntil14") << QStringLiteral("{{xx|timeuntil}}") << dict << QStringLiteral("") << NoError;
0327     QTest::newRow("filter-timeuntil15") << QStringLiteral("{{|timeuntil}}") << dict << QStringLiteral("") << NoError;
0328 
0329     QDateTime d(QDate(2008, 1, 1), {});
0330 
0331     dict.clear();
0332     dict.insert(QStringLiteral("d"), d);
0333 
0334     QTest::newRow("date01") << "{{ d|date:\"MM\" }}" << dict << QStringLiteral("01") << NoError;
0335     QTest::newRow("date02") << QStringLiteral("{{ d|date }}") << dict << d.toString(QStringLiteral("MMM. d, yyyy")) << NoError;
0336 
0337     dict.clear();
0338     dict.insert(QStringLiteral("d"), QStringLiteral("fail_string"));
0339     QTest::newRow("date03") << "{{ d|date:\"MM\" }}" << dict << QString() << NoError;
0340 }
0341 
0342 void TestFilters::testStringFilters_data()
0343 {
0344     QTest::addColumn<QString>("input");
0345     QTest::addColumn<Dict>("dict");
0346     QTest::addColumn<QString>("output");
0347     QTest::addColumn<KTextTemplate::Error>("error");
0348 
0349     Dict dict;
0350 
0351     dict.clear();
0352     dict.insert(QStringLiteral("a"), QStringLiteral("<a>\'"));
0353     dict.insert(QStringLiteral("b"), QVariant::fromValue(markSafe(QStringLiteral("<a>\'"))));
0354 
0355     QTest::newRow("filter-addslash01") << QStringLiteral(
0356         "{% autoescape off %}{{ a|addslashes }} {{ "
0357         "b|addslashes }}{% endautoescape %}")
0358                                        << dict << R"(<a>\' <a>\')" << NoError;
0359 
0360     dict.clear();
0361     dict.insert(QStringLiteral("a"), QStringLiteral("<a>\'"));
0362     dict.insert(QStringLiteral("b"), QVariant::fromValue(markSafe(QStringLiteral("<a>\'"))));
0363 
0364     QTest::newRow("filter-addslash02") << QStringLiteral("{{ a|addslashes }} {{ b|addslashes }}") << dict << R"(&lt;a&gt;\&#39; <a>\')" << NoError;
0365 
0366     dict.clear();
0367     dict.insert(QStringLiteral("a"), QStringLiteral("fred>"));
0368     dict.insert(QStringLiteral("b"), QVariant::fromValue(markSafe(QStringLiteral("fred&gt;"))));
0369 
0370     QTest::newRow("filter-capfirst01") << QStringLiteral(
0371         "{% autoescape off %}{{ a|capfirst }} {{ b|capfirst "
0372         "}}{% endautoescape %}") << dict
0373                                        << QStringLiteral("Fred> Fred&gt;") << NoError;
0374 
0375     dict.clear();
0376     dict.insert(QStringLiteral("a"), QStringLiteral("fred>"));
0377     dict.insert(QStringLiteral("b"), QVariant::fromValue(markSafe(QStringLiteral("fred&gt;"))));
0378 
0379     QTest::newRow("filter-capfirst02") << QStringLiteral("{{ a|capfirst }} {{ b|capfirst }}") << dict << QStringLiteral("Fred&gt; Fred&gt;") << NoError;
0380 
0381     //  Note that applying fix_ampsersands in autoescape mode leads to
0382     //  double escaping.
0383 
0384     dict.clear();
0385     dict.insert(QStringLiteral("a"), QStringLiteral("a&b"));
0386     dict.insert(QStringLiteral("b"), QVariant::fromValue(markSafe(QStringLiteral("a&b"))));
0387 
0388     QTest::newRow("filter-fix_ampersands01") << QStringLiteral(
0389         "{% autoescape off %}{{ a|fix_ampersands }} {{ "
0390         "b|fix_ampersands }}{% endautoescape %}")
0391                                              << dict << QStringLiteral("a&amp;b a&amp;b") << NoError;
0392 
0393     dict.clear();
0394     dict.insert(QStringLiteral("a"), QStringLiteral("a&b"));
0395     dict.insert(QStringLiteral("b"), QVariant::fromValue(markSafe(QStringLiteral("a&b"))));
0396 
0397     QTest::newRow("filter-fix_ampersands02") << QStringLiteral("{{ a|fix_ampersands }} {{ b|fix_ampersands }}") << dict << QStringLiteral("a&amp;amp;b a&amp;b")
0398                                              << NoError;
0399 
0400     dict.clear();
0401     dict.insert(QStringLiteral("a"), QStringLiteral("1.42"));
0402     dict.insert(QStringLiteral("b"), QVariant::fromValue(markSafe(QStringLiteral("1.42"))));
0403 
0404     QTest::newRow("filter-floatformat01") << QStringLiteral(
0405         "{% autoescape off %}{{ a|floatformat }} {{ "
0406         "b|floatformat }}{% endautoescape %}")
0407                                           << dict << QStringLiteral("1.4 1.4") << NoError;
0408 
0409     dict.clear();
0410     dict.insert(QStringLiteral("a"), QStringLiteral("1.42"));
0411     dict.insert(QStringLiteral("b"), QVariant::fromValue(markSafe(QStringLiteral("1.42"))));
0412 
0413     QTest::newRow("filter-floatformat02") << QStringLiteral("{{ a|floatformat }} {{ b|floatformat }}") << dict << QStringLiteral("1.4 1.4") << NoError;
0414 
0415     dict.clear();
0416     dict.insert(QStringLiteral("a"), double(1234.54321));
0417     dict.insert(QStringLiteral("b"), int(1234));
0418 
0419     QTest::newRow("filter-floatformat03") << QStringLiteral("{{ a|floatformat }} {{ b|floatformat }}") << dict << QStringLiteral("1234.5 1234.0") << NoError;
0420     QTest::newRow("filter-floatformat04") << QStringLiteral("{{ a|floatformat:2 }} {{ b|floatformat:2 }}") << dict << QStringLiteral("1234.54 1234.00")
0421                                           << NoError;
0422     QTest::newRow("filter-floatformat04") << QStringLiteral("{{ a|floatformat:0 }} {{ b|floatformat:0 }}") << dict << QStringLiteral("1235 1234") << NoError;
0423 
0424     //  The contents of "linenumbers" is escaped according to the current
0425     //  autoescape setting.
0426 
0427     dict.clear();
0428     dict.insert(QStringLiteral("a"), QStringLiteral("one\n<two>\nthree"));
0429     dict.insert(QStringLiteral("b"), QVariant::fromValue(markSafe(QStringLiteral("one\n&lt;two&gt;\nthree"))));
0430 
0431     QTest::newRow("filter-linenumbers01") << QStringLiteral("{{ a|linenumbers }} {{ b|linenumbers }}") << dict
0432                                           << "1. one\n2. &lt;two&gt;\n3. three 1. one\n2. &lt;two&gt;\n3. three" << NoError;
0433 
0434     dict.clear();
0435     dict.insert(QStringLiteral("a"), QStringLiteral("one\n<two>\nthree"));
0436     dict.insert(QStringLiteral("b"), QVariant::fromValue(markSafe(QStringLiteral("one\n&lt;two&gt;\nthree"))));
0437     QTest::newRow("filter-linenumbers02") << QStringLiteral(
0438         "{% autoescape off %}{{ a|linenumbers }} {{ "
0439         "b|linenumbers }}{% endautoescape %}")
0440                                           << dict << "1. one\n2. <two>\n3. three 1. one\n2. &lt;two&gt;\n3. three" << NoError;
0441 
0442     dict.clear();
0443     dict.insert(QStringLiteral("a"), QStringLiteral("Apple & banana"));
0444     dict.insert(QStringLiteral("b"), QVariant::fromValue(markSafe(QStringLiteral("Apple &amp; banana"))));
0445 
0446     QTest::newRow("filter-lower01") << QStringLiteral("{% autoescape off %}{{ a|lower }} {{ b|lower }}{% endautoescape %}") << dict
0447                                     << QStringLiteral("apple & banana apple &amp; banana") << NoError;
0448 
0449     dict.clear();
0450     dict.insert(QStringLiteral("a"), QStringLiteral("Apple & banana"));
0451     dict.insert(QStringLiteral("b"), QVariant::fromValue(markSafe(QStringLiteral("Apple &amp; banana"))));
0452 
0453     QTest::newRow("filter-lower02") << QStringLiteral("{{ a|lower }} {{ b|lower }}") << dict << QStringLiteral("apple &amp; banana apple &amp; banana")
0454                                     << NoError;
0455 
0456     //  The make_list filter can destroy existing escaping, so the results are
0457     //  escaped.
0458 
0459     dict.clear();
0460     dict.insert(QStringLiteral("a"), markSafe(QStringLiteral("&")));
0461 
0462     QTest::newRow("filter-make_list01") << QStringLiteral("{% autoescape off %}{{ a|make_list }}{% endautoescape %}") << dict << "[u\'&\']" << NoError;
0463     QTest::newRow("filter-make_list02") << QStringLiteral("{{ a|make_list }}") << dict << QStringLiteral("[u&#39;&amp;&#39;]") << NoError;
0464 
0465     QTest::newRow("filter-make_list03") << QStringLiteral(
0466         "{% autoescape off %}{{ a|make_list|stringformat:\"%1\"|safe }}{% "
0467         "endautoescape %}") << dict << QStringLiteral("[u\'&\']")
0468                                         << NoError;
0469     QTest::newRow("filter-make_list04") << QStringLiteral("{{ a|make_list|stringformat:\"%1\"|safe }}") << dict << QStringLiteral("[u\'&\']") << NoError;
0470 
0471     //  Running slugify on a pre-escaped string leads to odd behaviour,
0472     //  but the result is still safe.
0473 
0474     dict.clear();
0475     dict.insert(QStringLiteral("a"), QStringLiteral("a & b"));
0476     dict.insert(QStringLiteral("b"), markSafe(QStringLiteral("a &amp; b")));
0477 
0478     QTest::newRow("filter-slugify01") << QStringLiteral("{% autoescape off %}{{ a|slugify }} {{ b|slugify }}{% endautoescape %}") << dict
0479                                       << QStringLiteral("a-b a-amp-b") << NoError;
0480     QTest::newRow("filter-slugify02") << QStringLiteral("{{ a|slugify }} {{ b|slugify }}") << dict << QStringLiteral("a-b a-amp-b") << NoError;
0481 
0482     dict.clear();
0483     dict.insert(QStringLiteral("a"), QStringLiteral("Schöne Grüße"));
0484 
0485     QTest::newRow("filter-slugify03") << QStringLiteral("{{ a|slugify }}") << dict << QStringLiteral("schone-grue") << NoError;
0486 
0487     dict.clear();
0488     dict.insert(QStringLiteral("a"), QStringLiteral("testing\r\njavascript \'string\" <b>escaping</b>"));
0489     QTest::newRow("escapejs01") << QStringLiteral("{{ a|escapejs }}") << dict
0490                                 << "testing\\u000D\\u000Ajavascript \\u0027string\\u0022 "
0491                                    "\\u003Cb\\u003Eescaping\\u003C/b\\u003E"
0492                                 << NoError;
0493     QTest::newRow("escapejs02") << QStringLiteral("{% autoescape off %}{{ a|escapejs }}{% endautoescape %}") << dict
0494                                 << "testing\\u000D\\u000Ajavascript \\u0027string\\u0022 "
0495                                    "\\u003Cb\\u003Eescaping\\u003C/b\\u003E"
0496                                 << NoError;
0497 
0498     //  Notice that escaping is applied *after* any filters, so the string
0499     //  formatting here only needs to deal with pre-escaped characters.
0500 
0501     dict.clear();
0502     dict.insert(QStringLiteral("a"), QStringLiteral("a<b"));
0503     dict.insert(QStringLiteral("b"), QVariant::fromValue(markSafe(QStringLiteral("a<b"))));
0504 
0505     QTest::newRow("filter-stringformat01") << "{% autoescape off %}.{{ a|stringformat:\"%1\" }}. .{{ "
0506                                               "b|stringformat:\"%2\" }}.{% endautoescape %}"
0507                                            << dict << QStringLiteral(".a<b. .a<b.") << NoError;
0508     QTest::newRow("filter-stringformat02") << R"(.{{ a|stringformat:"%1" }}. .{{ b|stringformat:"%2" }}.)" << dict << QStringLiteral(".a&lt;b. .a<b.")
0509                                            << NoError;
0510     QTest::newRow("filter-stringformat03") << ".{{ a|stringformat:\"foo %1 bar\" }}. .{{ b|stringformat:\"baz %2 "
0511                                               "bat\" }}."
0512                                            << dict << QStringLiteral(".foo a&lt;b bar. .baz a<b bat.") << NoError;
0513 
0514     dict.clear();
0515     dict.insert(QStringLiteral("path"), QStringLiteral("www.kde.org"));
0516     QTest::newRow("filter-stringformat04") << "{% with path|stringformat:\"<a href=\\\"%1\\\">%1</a>\"|safe as "
0517                                               "result %}{{ result }}{% endwith %}"
0518                                            << dict << "<a href=\"www.kde.org\">www.kde.org</a>" << NoError;
0519 
0520     dict.clear();
0521     dict.insert(QStringLiteral("a"), QStringLiteral("JOE\'S CRAB SHACK"));
0522     QTest::newRow("filter-title01") << "{{ a|title }}" << dict << QStringLiteral("Joe&#39;s Crab Shack") << NoError;
0523 
0524     dict.clear();
0525     dict.insert(QStringLiteral("a"), QStringLiteral("555 WEST 53RD STREET"));
0526     QTest::newRow("filter-title02") << "{{ a|title }}" << dict << QStringLiteral("555 West 53rd Street") << NoError;
0527 
0528     dict.clear();
0529     dict.insert(QStringLiteral("a"), QStringLiteral("alpha & bravo"));
0530     dict.insert(QStringLiteral("b"), QVariant::fromValue(markSafe(QStringLiteral("alpha &amp; bravo"))));
0531 
0532     QTest::newRow("filter-truncatewords01") << "{% autoescape off %}{{ a|truncatewords:\"2\" }} {{ "
0533                                                "b|truncatewords:\"2\"}}{% endautoescape %}"
0534                                             << dict << QStringLiteral("alpha & ... alpha &amp; ...") << NoError;
0535 
0536     QTest::newRow("filter-truncatewords02") << R"({{ a|truncatewords:"2" }} {{ b|truncatewords:"2"}})" << dict
0537                                             << QStringLiteral("alpha &amp; ... alpha &amp; ...") << NoError;
0538 
0539     //  The "upper" filter messes up entities (which are case-sensitive),
0540     //  so it's not safe for non-escaping purposes.
0541 
0542     dict.clear();
0543     dict.insert(QStringLiteral("a"), QStringLiteral("a & b"));
0544     dict.insert(QStringLiteral("b"), QVariant::fromValue(markSafe(QStringLiteral("a &amp; b"))));
0545 
0546     QTest::newRow("filter-upper01") << QStringLiteral("{% autoescape off %}{{ a|upper }} {{ b|upper }}{% endautoescape %}") << dict
0547                                     << QStringLiteral("A & B A &AMP; B") << NoError;
0548     QTest::newRow("filter-upper02") << QStringLiteral("{{ a|upper }} {{ b|upper }}") << dict << QStringLiteral("A &amp; B A &amp;AMP; B") << NoError;
0549 
0550     //   //  {"a": "http://example.com/?x=&y=", "b":
0551     //   mark_safe("http://example.com?x=&amp;y=")
0552     //   QTest::newRow( "filter-urlize01") << QString::fromLatin1( "{%
0553     //   autoescape
0554     //   off %}{{ a|urlize }} {{ b|urlize }}{% endautoescape %}" ) << dict <<
0555     //   "<a
0556     //   href=\"http://example.com/?x=&y=\"
0557     //   rel=\"nofollow\">http://example.com/?x=&y=</a> <a
0558     //   href=\"http://example.com?x=&amp;y=\"
0559     //   rel=\"nofollow\">http://example.com?x=&amp;y=</a>" << NoError;
0560     //
0561     //   //  {"a": "http://example.com/?x=&y=", "b":
0562     //   mark_safe("http://example.com?x=&amp;y=")
0563     //   QTest::newRow( "filter-urlize02") << QString::fromLatin1( "{{ a|urlize
0564     //   }}
0565     //   {{ b|urlize }}" ) << dict << "<a href=\"http://example.com/?x=&amp;y=\"
0566     //   rel=\"nofollow\">http://example.com/?x=&amp;y=</a> <a
0567     //   href=\"http://example.com?x=&amp;y=\"
0568     //   rel=\"nofollow\">http://example.com?x=&amp;y=</a>" << NoError;
0569     //
0570     //   //  {"a": mark_safe("a &amp; b")
0571     //   QTest::newRow( "filter-urlize03") << QString::fromLatin1( "{%
0572     //   autoescape
0573     //   off %}{{ a|urlize }}{% endautoescape %}" ) << dict <<
0574     //   QString::fromLatin1( "a &amp; b" ) << NoError;
0575     //
0576     //   //  {"a": mark_safe("a &amp; b")
0577     //   QTest::newRow( "filter-urlize04") << QString::fromLatin1( "{{ a|urlize
0578     //   }}" ) << dict << QString::fromLatin1( "a &amp; b" ) << NoError;
0579     //
0580     //   //  This will lead to a nonsense result, but at least it won't be
0581     //
0582     //   //  exploitable for XSS purposes when auto-escaping is on.
0583     //
0584     //   //  {"a": "<script>alert(\"foo\")</script>"
0585     //   QTest::newRow( "filter-urlize05") << QString::fromLatin1( "{%
0586     //   autoescape
0587     //   off %}{{ a|urlize }}{% endautoescape %}" ) << dict <<
0588     //   "<script>alert(\"foo\")</script>" << NoError;
0589     //
0590     //   //  {"a": "<script>alert(\"foo\")</script>"
0591     //   QTest::newRow( "filter-urlize06") << QString::fromLatin1( "{{ a|urlize
0592     //   }}" ) << dict << QString::fromLatin1(
0593     //   "&lt;script&gt;alert(&#39;foo&#39;)&lt;/script&gt;" ) << NoError;
0594     //
0595     //   //  mailto: testing for urlize
0596     //
0597     //   //  {"a": "Email me at me@example.com"
0598     //   QTest::newRow( "filter-urlize07") << QString::fromLatin1( "{{ a|urlize
0599     //   }}" ) << dict << "Email me at <a
0600     //   href=\"mailto:me@example.com\">me@example.com</a>" << NoError;
0601     //
0602     //   //  {"a": "Email me at <me@example.com>"
0603     //   QTest::newRow( "filter-urlize08") << QString::fromLatin1( "{{ a|urlize
0604     //   }}" ) << dict << "Email me at &lt;<a
0605     //   href=\"mailto:me@example.com\">me@example.com</a>&gt;" << NoError;
0606     //
0607     //   //  {"a": "\"Unsafe\" http://example.com/x=&y=", "b":
0608     //   mark_safe("&quot;Safe&quot; http://example.com?x=&amp;y=")
0609     //   QTest::newRow( "filter-urlizetrunc01") << "{% autoescape off %}{{
0610     //   a|urlizetrunc:\"8\" }} {{ b|urlizetrunc:\"8\" }}{% endautoescape %}" <<
0611     //   dict << "\"Unsafe\" <a href=\"http://example.com/x=&y=\"
0612     //   rel=\"nofollow\">http:...</a> &quot;Safe&quot; <a
0613     //   href=\"http://example.com?x=&amp;y=\" rel=\"nofollow\">http:...</a>" <<
0614     //   NoError;
0615     //
0616     //   //  {"a": "\"Unsafe\" http://example.com/x=&y=", "b":
0617     //   mark_safe("&quot;Safe&quot; http://example.com?x=&amp;y=")
0618     //   QTest::newRow( "filter-urlizetrunc02") << "{{ a|urlizetrunc:\"8\" }} {{
0619     //   b|urlizetrunc:\"8\" }}" << dict << "&quot;Unsafe&quot; <a
0620     //   href=\"http://example.com/x=&amp;y=\" rel=\"nofollow\">http:...</a>
0621     //   &quot;Safe&quot; <a href=\"http://example.com?x=&amp;y=\"
0622     //   rel=\"nofollow\">http:...</a>" << NoError;
0623 
0624     //   //  Ensure iriencode keeps safe strings:
0625     //
0626     //   //  {"url": "?test=1&me=2"
0627     //   QTest::newRow( "filter-iriencode01") << QString::fromLatin1( "{{
0628     //   url|iriencode }}" ) << dict << QString::fromLatin1( "?test=1&amp;me=2"
0629     //   )
0630     //   << NoError;
0631     //
0632     //   //  {"url": "?test=1&me=2"
0633     //   QTest::newRow( "filter-iriencode02") << QString::fromLatin1( "{%
0634     //   autoescape off %}{{ url|iriencode }}{% endautoescape %}" ) << dict <<
0635     //   QString::fromLatin1( "?test=1&me=2" ) << NoError;
0636     //
0637     //   //  {"url": mark_safe("?test=1&me=2")
0638     //   QTest::newRow( "filter-iriencode03") << QString::fromLatin1( "{{
0639     //   url|iriencode }}" ) << dict << QString::fromLatin1( "?test=1&me=2" ) <<
0640     //   NoError;
0641     //
0642     //   //  {"url": mark_safe("?test=1&me=2")
0643     //   QTest::newRow( "filter-iriencode04") << QString::fromLatin1( "{%
0644     //   autoescape off %}{{ url|iriencode }}{% endautoescape %}" ) << dict <<
0645     //   QString::fromLatin1( "?test=1&me=2" ) << NoError;
0646     //
0647     dict.clear();
0648     dict.insert(QStringLiteral("a"), QStringLiteral("a & b"));
0649     dict.insert(QStringLiteral("b"), QVariant::fromValue(markSafe(QStringLiteral("a &amp; b"))));
0650 
0651     QTest::newRow("filter-wordcount01") << QStringLiteral(
0652         "{% autoescape off %}{{ a|wordcount }} {{ b|wordcount "
0653         "}}{% endautoescape %}") << dict << QStringLiteral("3 3")
0654                                         << NoError;
0655 
0656     QTest::newRow("filter-wordcount02") << QStringLiteral("{{ a|wordcount }} {{ b|wordcount }}") << dict << QStringLiteral("3 3") << NoError;
0657 
0658     dict.clear();
0659     dict.insert(QStringLiteral("a"), QStringLiteral("a & b"));
0660     dict.insert(QStringLiteral("b"), QVariant::fromValue(markSafe(QStringLiteral("a & b"))));
0661 
0662     QTest::newRow("filter-wordwrap01") << QStringLiteral(
0663         "{% autoescape off %}{{ a|wordwrap:3 }} {{ "
0664         "b|wordwrap:3 }}{% endautoescape %}")
0665                                        << dict << "a &\nb a &\nb" << NoError;
0666 
0667     QTest::newRow("filter-wordwrap02") << QStringLiteral("{{ a|wordwrap:3 }} {{ b|wordwrap:3 }}") << dict << "a &amp;\nb a &\nb" << NoError;
0668 
0669     dict.clear();
0670     QTest::newRow("filter-wordwrap03") << QStringLiteral("{{xx|wordwrap}}") << dict << "" << NoError;
0671 
0672     QTest::newRow("filter-wordwrap04") << QStringLiteral("{{|wordwrap}}") << dict << "" << NoError;
0673 
0674     dict.clear();
0675     dict.insert(QStringLiteral("a"), QStringLiteral("a&b"));
0676     dict.insert(QStringLiteral("b"), QVariant::fromValue(markSafe(QStringLiteral("a&b"))));
0677 
0678     QTest::newRow("filter-ljust01") << "{% autoescape off %}.{{ a|ljust:\"5\" }}. .{{ b|ljust:\"5\" }}.{% "
0679                                        "endautoescape %}"
0680                                     << dict << QStringLiteral(".a&b  . .a&b  .") << NoError;
0681 
0682     QTest::newRow("filter-ljust02") << R"(.{{ a|ljust:"5" }}. .{{ b|ljust:"5" }}.)" << dict << QStringLiteral(".a&amp;b  . .a&b  .") << NoError;
0683 
0684     QTest::newRow("filter-rjust01") << "{% autoescape off %}.{{ a|rjust:\"5\" }}. .{{ b|rjust:\"5\" }}.{% "
0685                                        "endautoescape %}"
0686                                     << dict << QStringLiteral(".  a&b. .  a&b.") << NoError;
0687 
0688     QTest::newRow("filter-rjust02") << R"(.{{ a|rjust:"5" }}. .{{ b|rjust:"5" }}.)" << dict << QStringLiteral(".  a&amp;b. .  a&b.") << NoError;
0689 
0690     QTest::newRow("filter-center01") << "{% autoescape off %}.{{ a|center:\"5\" }}. .{{ b|center:\"5\" "
0691                                         "}}.{% "
0692                                         "endautoescape %}"
0693                                      << dict << QStringLiteral(". a&b . . a&b .") << NoError;
0694 
0695     QTest::newRow("filter-center02") << R"(.{{ a|center:"5" }}. .{{ b|center:"5" }}.)" << dict << QStringLiteral(". a&amp;b . . a&b .") << NoError;
0696 
0697     dict.clear();
0698     dict.insert(QStringLiteral("a"), QStringLiteral("x&y"));
0699     dict.insert(QStringLiteral("b"), QVariant::fromValue(markSafe(QStringLiteral("x&amp;y"))));
0700 
0701     QTest::newRow("filter-cut01") << "{% autoescape off %}{{ a|cut:\"x\" }} {{ "
0702                                      "b|cut:\"x\" }}{% endautoescape %}"
0703                                   << dict << QStringLiteral("&y &amp;y") << NoError;
0704     QTest::newRow("filter-cut02") << R"({{ a|cut:"x" }} {{ b|cut:"x" }})" << dict << QStringLiteral("&amp;y &amp;y") << NoError;
0705     QTest::newRow("filter-cut03") << "{% autoescape off %}{{ a|cut:\"&\" }} {{ "
0706                                      "b|cut:\"&\" }}{% endautoescape %}"
0707                                   << dict << QStringLiteral("xy xamp;y") << NoError;
0708     QTest::newRow("filter-cut04") << R"({{ a|cut:"&" }} {{ b|cut:"&" }})" << dict << QStringLiteral("xy xamp;y") << NoError;
0709 
0710     //  Passing ";" to cut can break existing HTML entities, so those strings
0711     //  are auto-escaped.
0712 
0713     QTest::newRow("filter-cut05") << "{% autoescape off %}{{ a|cut:\";\" }} {{ "
0714                                      "b|cut:\";\" }}{% endautoescape %}"
0715                                   << dict << QStringLiteral("x&y x&ampy") << NoError;
0716     QTest::newRow("filter-cut06") << R"({{ a|cut:";" }} {{ b|cut:";" }})" << dict << QStringLiteral("x&amp;y x&amp;ampy") << NoError;
0717 
0718     //  The "escape" filter works the same whether autoescape is on or off,
0719     //  but it has no effect on strings already marked as safe.
0720 
0721     dict.clear();
0722     dict.insert(QStringLiteral("a"), QStringLiteral("x&y"));
0723     dict.insert(QStringLiteral("b"), QVariant::fromValue(markSafe(QStringLiteral("x&y"))));
0724 
0725     QTest::newRow("filter-escape01") << QStringLiteral("{{ a|escape }} {{ b|escape }}") << dict << QStringLiteral("x&amp;y x&y") << NoError;
0726     QTest::newRow("filter-escape02") << QStringLiteral("{% autoescape off %}{{ a|escape }} {{ b|escape }}{% endautoescape %}") << dict
0727                                      << QStringLiteral("x&amp;y x&y") << NoError;
0728 
0729     //  It is only applied once, regardless of the number of times it
0730     //  appears in a chain.
0731 
0732     dict.clear();
0733     dict.insert(QStringLiteral("a"), QStringLiteral("x&y"));
0734 
0735     QTest::newRow("filter-escape03") << QStringLiteral("{% autoescape off %}{{ a|escape|escape }}{% endautoescape %}") << dict << QStringLiteral("x&amp;y")
0736                                      << NoError;
0737     QTest::newRow("filter-escape04") << QStringLiteral("{{ a|escape|escape }}") << dict << QStringLiteral("x&amp;y") << NoError;
0738 
0739     //  Force_escape is applied immediately. It can be used to provide
0740     //  double-escaping, for example.
0741 
0742     QTest::newRow("filter-force-escape01") << QStringLiteral("{% autoescape off %}{{ a|force_escape }}{% endautoescape %}") << dict << QStringLiteral("x&amp;y")
0743                                            << NoError;
0744     QTest::newRow("filter-force-escape02") << QStringLiteral("{{ a|force_escape }}") << dict << QStringLiteral("x&amp;y") << NoError;
0745     QTest::newRow("filter-force-escape03") << QStringLiteral(
0746         "{% autoescape off %}{{ a|force_escape|force_escape "
0747         "}}{% endautoescape %}") << dict << QStringLiteral("x&amp;amp;y")
0748                                            << NoError;
0749     QTest::newRow("filter-force-escape04") << QStringLiteral("{{ a|force_escape|force_escape }}") << dict << QStringLiteral("x&amp;amp;y") << NoError;
0750 
0751     //  Because the result of force_escape is "safe", an additional
0752     //  escape filter has no effect.
0753 
0754     QTest::newRow("filter-force-escape05") << QStringLiteral("{% autoescape off %}{{ a|force_escape|escape }}{% endautoescape %}") << dict
0755                                            << QStringLiteral("x&amp;y") << NoError;
0756     QTest::newRow("filter-force-escape06") << QStringLiteral("{{ a|force_escape|escape }}") << dict << QStringLiteral("x&amp;y") << NoError;
0757     QTest::newRow("filter-force-escape07") << QStringLiteral("{% autoescape off %}{{ a|escape|force_escape }}{% endautoescape %}") << dict
0758                                            << QStringLiteral("x&amp;y") << NoError;
0759     QTest::newRow("filter-force-escape08") << QStringLiteral("{{ a|escape|force_escape }}") << dict << QStringLiteral("x&amp;y") << NoError;
0760 
0761     //  The contents in "linebreaks" and "linebreaksbr" are escaped
0762     //  according to the current autoescape setting.
0763 
0764     dict.clear();
0765     dict.insert(QStringLiteral("a"), QStringLiteral("x&\ny"));
0766     dict.insert(QStringLiteral("b"), markSafe(QStringLiteral("x&\ny")));
0767 
0768     QTest::newRow("filter-linebreaks01") << QStringLiteral("{{ a|linebreaks }} {{ b|linebreaks }}") << dict
0769                                          << QStringLiteral("<p>x&amp;<br />y</p> <p>x&<br />y</p>") << NoError;
0770     QTest::newRow("filter-linebreaks02") << QStringLiteral(
0771         "{% autoescape off %}{{ a|linebreaks }} {{ "
0772         "b|linebreaks }}{% endautoescape %}")
0773                                          << dict << QStringLiteral("<p>x&<br />y</p> <p>x&<br />y</p>") << NoError;
0774     QTest::newRow("filter-linebreaksbr01") << QStringLiteral("{{ a|linebreaksbr }} {{ b|linebreaksbr }}") << dict << QStringLiteral("x&amp;<br />y x&<br />y")
0775                                            << NoError;
0776     QTest::newRow("filter-linebreaksbr02") << QStringLiteral(
0777         "{% autoescape off %}{{ a|linebreaksbr }} {{ "
0778         "b|linebreaksbr }}{% endautoescape %}")
0779                                            << dict << QStringLiteral("x&<br />y x&<br />y") << NoError;
0780 
0781     dict.clear();
0782     dict.insert(QStringLiteral("a"), QStringLiteral("<b>hello</b>"));
0783 
0784     QTest::newRow("filter-safe01") << QStringLiteral("{{ a }} -- {{ a|safe }}") << dict << QStringLiteral("&lt;b&gt;hello&lt;/b&gt; -- <b>hello</b>")
0785                                    << NoError;
0786     QTest::newRow("filter-safe02") << QStringLiteral("{% autoescape off %}{{ a }} -- {{ a|safe }}{% endautoescape %}") << dict
0787                                    << QStringLiteral("<b>hello</b> -- <b>hello</b>") << NoError;
0788 
0789     dict.clear();
0790     dict.insert(QStringLiteral("a"), QVariantList{QStringLiteral("&"), QStringLiteral("<")});
0791 
0792     QTest::newRow("filter-safeseq01") << R"({{ a|join:", " }} -- {{ a|safeseq|join:", " }})" << dict << QStringLiteral("&amp;, &lt; -- &, <") << NoError;
0793     QTest::newRow("filter-safeseq02") << "{% autoescape off %}{{ a|join:\", \" "
0794                                          "}} -- {{ a|safeseq|join:\", \" "
0795                                          "}}{% endautoescape %}"
0796                                       << dict << QStringLiteral("&, < -- &, <") << NoError;
0797 
0798     dict.clear();
0799     dict.insert(QStringLiteral("a"), QStringLiteral("<a>x</a> <p><b>y</b></p>"));
0800     dict.insert(QStringLiteral("b"), QVariant::fromValue(markSafe(QStringLiteral("<a>x</a> <p><b>y</b></p>"))));
0801 
0802     QTest::newRow("filter-removetags01") << R"({{ a|removetags:"a b" }} {{ b|removetags:"a b" }})" << dict
0803                                          << QStringLiteral("x &lt;p&gt;y&lt;/p&gt; x <p>y</p>") << NoError;
0804     QTest::newRow("filter-removetags02") << "{% autoescape off %}{{ a|removetags:\"a b\" }} {{ b|removetags:\"a "
0805                                             "b\" }}{% endautoescape %}"
0806                                          << dict << QStringLiteral("x <p>y</p> x <p>y</p>") << NoError;
0807     QTest::newRow("filter-striptags01") << QStringLiteral("{{ a|striptags }} {{ b|striptags }}") << dict << QStringLiteral("x y x y") << NoError;
0808     QTest::newRow("filter-striptags02") << QStringLiteral(
0809         "{% autoescape off %}{{ a|striptags }} {{ b|striptags "
0810         "}}{% endautoescape %}") << dict << QStringLiteral("x y x y")
0811                                         << NoError;
0812 
0813     dict.clear();
0814     dict.insert(QStringLiteral("fs_int_mib"), 1048576);
0815 
0816     QTest::newRow("filter-filesizeformat01") << QStringLiteral("{{ fs_int_mib|filesizeformat }}") << dict << QStringLiteral("1.05 MB") << NoError;
0817 
0818     QTest::newRow("filter-filesizeformat02") << QStringLiteral("{{ fs_int_mib|filesizeformat:\"2\" }}") << dict << QStringLiteral("1.00 MiB") << NoError;
0819 
0820     QTest::newRow("filter-filesizeformat03") << QStringLiteral("{{ fs_int_mib|filesizeformat:\"10,3\" }}") << dict << QStringLiteral("1.049 MB") << NoError;
0821 
0822     QTest::newRow("filter-filesizeformat04") << QStringLiteral("{{ fs_int_mib|filesizeformat:\"10,2,1024\" }}") << dict << QStringLiteral("1.07 GB") << NoError;
0823 
0824     dict.clear();
0825     dict.insert(QStringLiteral("fs_float_mib"), 1024.5);
0826 
0827     QTest::newRow("filter-filesizeformat05") << QStringLiteral("{{ fs_float_mib|filesizeformat:\"10,2,1024\" }}") << dict << QStringLiteral("1.05 MB")
0828                                              << NoError;
0829 
0830     dict.clear();
0831     dict.insert(QStringLiteral("fs_string_mib"), QStringLiteral("1024.5"));
0832 
0833     QTest::newRow("filter-filesizeformat06") << QStringLiteral("{{ fs_string_mib|filesizeformat:\"10,2,1024\" }}") << dict << QStringLiteral("1.05 MB")
0834                                              << NoError;
0835 
0836     dict.clear();
0837     dict.insert(QStringLiteral("fs_bytes"), 999);
0838     dict.insert(QStringLiteral("fs_kb"), 1000);
0839     dict.insert(QStringLiteral("fs_10kb"), 10 * 1000);
0840     dict.insert(QStringLiteral("fs_1000kb"), 1000 * 1000 - 1);
0841     dict.insert(QStringLiteral("fs_mb"), 1000 * 1000);
0842     dict.insert(QStringLiteral("fs_50mb"), 1000 * 1000 * 50);
0843     dict.insert(QStringLiteral("fs_1000mb"), 1000 * 1000 * 1000 - 1);
0844     dict.insert(QStringLiteral("fs_gb"), 1000 * 1000 * 1000);
0845     dict.insert(QStringLiteral("fs_tb"), static_cast<double>(1000.0 * 1000.0 * 1000.0 * 1000.0));
0846     dict.insert(QStringLiteral("fs_pb"), static_cast<double>(1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0));
0847     dict.insert(QStringLiteral("fs_eb"), static_cast<double>(1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 2000.0));
0848     dict.insert(QStringLiteral("fs_zb"), static_cast<double>(1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0));
0849     dict.insert(QStringLiteral("fs_yb"), static_cast<double>(1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0));
0850     dict.insert(QStringLiteral("fs_2000yb"), static_cast<double>(1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 2000.0));
0851     dict.insert(QStringLiteral("fs_0b1"), 0.1);
0852     dict.insert(QStringLiteral("fs_0b2"), QString(QChar(0x03B1)));
0853     dict.insert(QStringLiteral("fs_neg_1"), -100);
0854     dict.insert(QStringLiteral("fs_neg_2"), -1000 * 1000 * 50);
0855 
0856     // fixes tests on MSVC2013
0857     QString fsInput;
0858     fsInput = QStringLiteral(
0859         "{{ fs_bytes|filesizeformat }} {{ fs_kb|filesizeformat }} {{ "
0860         "fs_10kb|filesizeformat }} {{ fs_1000kb|filesizeformat }} ");
0861     fsInput += QStringLiteral(
0862         "{{ fs_mb|filesizeformat }} {{ fs_50mb|filesizeformat }} {{ "
0863         "fs_1000mb|filesizeformat }} {{ fs_gb|filesizeformat }} ");
0864     fsInput += QStringLiteral(
0865         "{{ fs_tb|filesizeformat }} {{ fs_pb|filesizeformat }} {{ "
0866         "fs_eb|filesizeformat }} {{ fs_zb|filesizeformat }} ");
0867     fsInput += QStringLiteral(
0868         "{{ fs_yb|filesizeformat }} {{ fs_2000yb|filesizeformat }} {{ "
0869         "fs_0b1|filesizeformat }} {{ fs_0b2|filesizeformat }} ");
0870     fsInput += QStringLiteral("{{ fs_neg_1|filesizeformat }} {{ fs_neg_2|filesizeformat }}");
0871 
0872     QString fsExpect;
0873     fsExpect = QStringLiteral("999 bytes 1.00 KB 10.00 KB 1000.00 KB ");
0874     fsExpect += QStringLiteral("1.00 MB 50.00 MB 1000.00 MB 1.00 GB ");
0875     fsExpect += QStringLiteral("1.00 TB 1.00 PB 2.00 EB 1.00 ZB ");
0876     fsExpect += QStringLiteral("1.00 YB 2000.00 YB 0 bytes 0 bytes ");
0877     fsExpect += QStringLiteral("-100 bytes -50.00 MB");
0878 
0879     QTest::newRow("filter-filesizeformat07") << fsInput << dict << fsExpect << NoError;
0880 }
0881 
0882 void TestFilters::testListFilters_data()
0883 {
0884     QTest::addColumn<QString>("input");
0885     QTest::addColumn<Dict>("dict");
0886     QTest::addColumn<QString>("output");
0887     QTest::addColumn<KTextTemplate::Error>("error");
0888 
0889     Dict dict;
0890 
0891     dict.insert(QStringLiteral("a"), QVariantList{QStringLiteral("a&b"), QStringLiteral("x")});
0892     dict.insert(QStringLiteral("b"), QVariantList{QVariant::fromValue(markSafe(QStringLiteral("a&b"))), QStringLiteral("x")});
0893 
0894     QTest::newRow("filter-first01") << QStringLiteral("{{ a|first }} {{ b|first }}") << dict << QStringLiteral("a&amp;b a&b") << NoError;
0895     QTest::newRow("filter-first02") << QStringLiteral("{% autoescape off %}{{ a|first }} {{ b|first }}{% endautoescape %}") << dict << QStringLiteral("a&b a&b")
0896                                     << NoError;
0897 
0898     dict.clear();
0899     dict.insert(QStringLiteral("a"), QVariantList{QStringLiteral("x"), QStringLiteral("a&b")});
0900     dict.insert(QStringLiteral("b"), QVariantList{QStringLiteral("x"), QVariant::fromValue(markSafe(QStringLiteral("a&b")))});
0901 
0902     QTest::newRow("filter-last01") << QStringLiteral("{{ a|last }} {{ b|last }}") << dict << QStringLiteral("a&amp;b a&b") << NoError;
0903     QTest::newRow("filter-last02") << QStringLiteral("{% autoescape off %}{{ a|last }} {{ b|last }}{% endautoescape %}") << dict << QStringLiteral("a&b a&b")
0904                                    << NoError;
0905 
0906     dict.clear();
0907     dict.insert(QStringLiteral("a"), QVariantList{QStringLiteral("a&b"), QStringLiteral("a&b")});
0908     dict.insert(QStringLiteral("b"),
0909                 QVariantList() << QVariant::fromValue(markSafe(QStringLiteral("a&b"))) << QVariant::fromValue(markSafe(QStringLiteral("a&b"))));
0910     QTest::newRow("filter-random01") << QStringLiteral("{{ a|random }} {{ b|random }}") << dict << QStringLiteral("a&amp;b a&b") << NoError;
0911     QTest::newRow("filter-random02") << QStringLiteral("{% autoescape off %}{{ a|random }} {{ b|random }}{% endautoescape %}") << dict
0912                                      << QStringLiteral("a&b a&b") << NoError;
0913 
0914     dict.clear();
0915     dict.insert(QStringLiteral("empty_list"), QVariantList());
0916     QTest::newRow("filter-random03") << QStringLiteral("{{empty_list|random}}") << dict << QStringLiteral("") << NoError;
0917     QTest::newRow("filter-random04") << QStringLiteral("{{|random}}") << dict << QStringLiteral("") << NoError;
0918 
0919     dict.clear();
0920     dict.insert(QStringLiteral("a"), QStringLiteral("a&b"));
0921     dict.insert(QStringLiteral("b"), QVariant::fromValue(markSafe(QStringLiteral("a&b"))));
0922 
0923     QTest::newRow("filter-slice01") << R"({{ a|slice:"1:3" }} {{ b|slice:"1:3" }})" << dict << QStringLiteral("&amp;b &b") << NoError;
0924     QTest::newRow("filter-slice02") << "{% autoescape off %}{{ a|slice:\"1:3\" }} {{ b|slice:\"1:3\" }}{% "
0925                                        "endautoescape %}"
0926                                     << dict << QStringLiteral("&b &b") << NoError;
0927 
0928     dict.clear();
0929     QTest::newRow("filter-slice03") << "{{xx|slice}}" << dict << QStringLiteral("") << NoError;
0930     QTest::newRow("filter-slice04") << "{{|slice}}" << dict << QStringLiteral("") << NoError;
0931 
0932     dict.clear();
0933     QVariantList sublist{QStringLiteral("<y")};
0934     dict.insert(QStringLiteral("a"), QVariantList{QStringLiteral("x>"), QVariant(sublist)});
0935 
0936     QTest::newRow("filter-unordered_list01") << QStringLiteral("{{ a|unordered_list }}") << dict << "\t<li>x&gt;\n\t<ul>\n\t\t<li>&lt;y</li>\n\t</ul>\n\t</li>"
0937                                              << NoError;
0938     QTest::newRow("filter-unordered_list02") << QStringLiteral("{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}") << dict
0939                                              << "\t<li>x>\n\t<ul>\n\t\t<li><y</li>\n\t</ul>\n\t</li>" << NoError;
0940 
0941     dict.clear();
0942     sublist = {markSafe(QStringLiteral("<y"))};
0943     dict.insert(QStringLiteral("a"), QVariantList{QStringLiteral("x>"), QVariant(sublist)});
0944 
0945     QTest::newRow("filter-unordered_list03") << QStringLiteral("{{ a|unordered_list }}") << dict << "\t<li>x&gt;\n\t<ul>\n\t\t<li><y</li>\n\t</ul>\n\t</li>"
0946                                              << NoError;
0947     QTest::newRow("filter-unordered_list04") << QStringLiteral("{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}") << dict
0948                                              << "\t<li>x>\n\t<ul>\n\t\t<li><y</li>\n\t</ul>\n\t</li>" << NoError;
0949 
0950     dict.clear();
0951     sublist = {QStringLiteral("<y")};
0952     dict.insert(QStringLiteral("a"), QVariantList{QStringLiteral("x>"), QVariant(sublist)});
0953 
0954     QTest::newRow("filter-unordered_list05") << QStringLiteral("{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}") << dict
0955                                              << "\t<li>x>\n\t<ul>\n\t\t<li><y</li>\n\t</ul>\n\t</li>" << NoError;
0956 
0957     //  length filter.
0958     dict.clear();
0959     dict.insert(QStringLiteral("list"), QVariantList{QStringLiteral("4"), QVariant(), true, QVariantHash()});
0960 
0961     QTest::newRow("length01") << QStringLiteral("{{ list|length }}") << dict << QStringLiteral("4") << NoError;
0962 
0963     dict.clear();
0964     dict.insert(QStringLiteral("list"), QVariantList());
0965 
0966     QTest::newRow("length02") << QStringLiteral("{{ list|length }}") << dict << QStringLiteral("0") << NoError;
0967 
0968     dict.clear();
0969     dict.insert(QStringLiteral("string"), QStringLiteral(""));
0970 
0971     QTest::newRow("length03") << QStringLiteral("{{ string|length }}") << dict << QStringLiteral("0") << NoError;
0972 
0973     dict.clear();
0974     dict.insert(QStringLiteral("string"), QStringLiteral("django"));
0975 
0976     QTest::newRow("length04") << QStringLiteral("{{ string|length }}") << dict << QStringLiteral("6") << NoError;
0977 
0978     //  Invalid uses that should fail silently.
0979 
0980     dict.clear();
0981     dict.insert(QStringLiteral("int"), 7);
0982 
0983     QTest::newRow("length05") << QStringLiteral("{{ int|length }}") << dict << QString() << NoError;
0984 
0985     dict.clear();
0986     dict.insert(QStringLiteral("None"), QVariant());
0987 
0988     QTest::newRow("length06") << QStringLiteral("{{ None|length }}") << dict << QString() << NoError;
0989 
0990     //  length_is filter.
0991 
0992     dict.clear();
0993     dict.insert(QStringLiteral("some_list"), QVariantList{QStringLiteral("4"), QVariant(), true, QVariantHash()});
0994 
0995     QTest::newRow("length_is01") << "{% if some_list|length_is:\"4\" %}Four{% endif %}" << dict << QStringLiteral("Four") << NoError;
0996 
0997     dict.clear();
0998     dict.insert(QStringLiteral("some_list"), QVariantList{QStringLiteral("4"), QVariant(), true, QVariantHash(), 17});
0999 
1000     QTest::newRow("length_is02") << "{% if some_list|length_is:\"4\" %}Four{% else %}Not Four{% endif %}" << dict << QStringLiteral("Not Four") << NoError;
1001 
1002     dict.clear();
1003     dict.insert(QStringLiteral("mystring"), QStringLiteral("word"));
1004 
1005     QTest::newRow("length_is03") << "{% if mystring|length_is:\"4\" %}Four{% endif %}" << dict << QStringLiteral("Four") << NoError;
1006 
1007     dict.clear();
1008     dict.insert(QStringLiteral("mystring"), QStringLiteral("Python"));
1009 
1010     QTest::newRow("length_is04") << "{% if mystring|length_is:\"4\" %}Four{% else %}Not Four{% endif %}" << dict << QStringLiteral("Not Four") << NoError;
1011 
1012     dict.clear();
1013     dict.insert(QStringLiteral("mystring"), QStringLiteral(""));
1014 
1015     QTest::newRow("length_is05") << "{% if mystring|length_is:\"4\" %}Four{% else %}Not Four{% endif %}" << dict << QStringLiteral("Not Four") << NoError;
1016 
1017     dict.clear();
1018     dict.insert(QStringLiteral("var"), QStringLiteral("django"));
1019 
1020     QTest::newRow("length_is06") << QStringLiteral("{% with var|length as my_length %}{{ my_length }}{% endwith %}") << dict << QStringLiteral("6") << NoError;
1021 
1022     //  Boolean return value from length_is should not be coerced to a string
1023 
1024     dict.clear();
1025     QTest::newRow("length_is07") << "{% if \"X\"|length_is:0 %}Length is 0{% "
1026                                     "else %}Length not 0{% endif %}"
1027                                  << dict << QStringLiteral("Length not 0") << NoError;
1028     QTest::newRow("length_is08") << "{% if \"X\"|length_is:1 %}Length is 1{% "
1029                                     "else %}Length not 1{% endif %}"
1030                                  << dict << QStringLiteral("Length is 1") << NoError;
1031 
1032     //  Invalid uses that should fail silently.
1033 
1034     dict.clear();
1035     dict.insert(QStringLiteral("var"), QStringLiteral("django"));
1036 
1037     QTest::newRow("length_is09") << "{{ var|length_is:\"fish\" }}" << dict << QString() << NoError;
1038 
1039     dict.clear();
1040     dict.insert(QStringLiteral("int"), 7);
1041 
1042     QTest::newRow("length_is10") << "{{ int|length_is:\"1\" }}" << dict << QString() << NoError;
1043 
1044     dict.clear();
1045     dict.insert(QStringLiteral("none"), QVariant());
1046 
1047     QTest::newRow("length_is11") << "{{ none|length_is:\"1\" }}" << dict << QString() << NoError;
1048 
1049     dict.clear();
1050     dict.insert(QStringLiteral("a"), QVariantList{QStringLiteral("alpha"), QStringLiteral("beta & me")});
1051 
1052     QTest::newRow("join01") << "{{ a|join:\", \" }}" << dict << QStringLiteral("alpha, beta &amp; me") << NoError;
1053     QTest::newRow("join02") << "{% autoescape off %}{{ a|join:\", \" }}{% endautoescape %}" << dict << QStringLiteral("alpha, beta & me") << NoError;
1054     QTest::newRow("join03") << "{{ a|join:\" &amp; \" }}" << dict << QStringLiteral("alpha &amp; beta &amp; me") << NoError;
1055     QTest::newRow("join04") << "{% autoescape off %}{{ a|join:\" &amp; \" }}{% endautoescape %}" << dict << QStringLiteral("alpha &amp; beta & me") << NoError;
1056 
1057     // Test that joining with unsafe joiners don't result in unsafe strings
1058     // (#11377)
1059     dict.insert(QStringLiteral("var"), QStringLiteral(" & "));
1060     QTest::newRow("join05") << "{{ a|join:var }}" << dict << QStringLiteral("alpha &amp; beta &amp; me") << NoError;
1061     dict.insert(QStringLiteral("var"), KTextTemplate::markSafe(QStringLiteral(" & ")));
1062     QTest::newRow("join06") << "{{ a|join:var }}" << dict << QStringLiteral("alpha & beta &amp; me") << NoError;
1063     dict.insert(QStringLiteral("a"), QVariantList{QStringLiteral("Alpha"), QStringLiteral("Beta & Me")});
1064     dict.insert(QStringLiteral("var"), QStringLiteral(" & "));
1065     QTest::newRow("join07") << "{{ a|join:var|lower }}" << dict << QStringLiteral("alpha &amp; beta &amp; me") << NoError;
1066 
1067     dict.insert(QStringLiteral("a"), QVariantList{QStringLiteral("Alpha"), QStringLiteral("Beta & Me")});
1068     dict.insert(QStringLiteral("var"), KTextTemplate::markSafe(QStringLiteral(" & ")));
1069     QTest::newRow("join08") << "{{ a|join:var|lower }}" << dict << QStringLiteral("alpha & beta &amp; me") << NoError;
1070 
1071     // arguments to filters are intended to be used unescaped.
1072     //   dict.clear();
1073     //   dict.insert( QStringLiteral("a"), QVariantList() <<
1074     //   QString::fromLatin1( "alpha" ) << QString::fromLatin1( "beta & me" ) );
1075     //   dict.insert( QStringLiteral("var"), QStringLiteral(" & ") );
1076     //
1077     //   QTest::newRow( "join05" ) << QString::fromLatin1( "{{ a|join:var }}" )
1078     //   <<
1079     //   dict << QString::fromLatin1( "alpha &amp; beta &amp; me" ) << NoError;
1080     //
1081     //   dict.clear();
1082     //   dict.insert( QStringLiteral("a"), QVariantList() <<
1083     //   QString::fromLatin1( "alpha" ) << QString::fromLatin1( "beta & me" ) );
1084     //   dict.insert( QStringLiteral("var"), QVariant::fromValue( markSafe(
1085     //   QString::fromLatin1( " & " ) ) ) );
1086     //
1087     //   QTest::newRow( "join06" ) << QString::fromLatin1( "{{ a|join:var }}" )
1088     //   <<
1089     //   dict << QString::fromLatin1( "alpha & beta &amp; me" ) << NoError;
1090     //
1091     //   dict.clear();
1092     //   dict.insert( QStringLiteral("a"), QVariantList() <<
1093     //   QString::fromLatin1( "Alpha" ) << QString::fromLatin1( "Beta & me" ) );
1094     //   dict.insert( QStringLiteral("var"), QStringLiteral(" & ") );
1095     //
1096     //   QTest::newRow( "join07" ) << QString::fromLatin1( "{{ a|join:var|lower
1097     //   }}" ) << dict << QString::fromLatin1( "alpha &amp; beta &amp; me" ) <<
1098     //   NoError;
1099     //
1100     //   dict.clear();
1101     //   dict.insert( QStringLiteral("a"), QVariantList() <<
1102     //   QString::fromLatin1( "Alpha" ) << QString::fromLatin1( "Beta & me" ) );
1103     //   dict.insert( QStringLiteral("var"), QVariant::fromValue( markSafe(
1104     //   QString::fromLatin1( " & " ) ) ) );
1105     //
1106     //   QTest::newRow( "join08" ) << QString::fromLatin1( "{{ a|join:var|lower
1107     //   }}" ) << dict << QString::fromLatin1( "alpha & beta &amp; me" ) <<
1108     //   NoError;
1109 
1110     dict.clear();
1111 
1112     QVariantList mapList;
1113     const auto cities = QStringList{QStringLiteral("London"), QStringLiteral("Berlin"), QStringLiteral("Paris"), QStringLiteral("Dublin")};
1114     for (const QString &city : cities) {
1115         QVariantHash map;
1116         map.insert(QStringLiteral("city"), city);
1117         mapList << map;
1118     }
1119 
1120     dict.insert(QStringLiteral("mapList"), mapList);
1121 
1122     QTest::newRow("dictsort01") << "{% with mapList|dictsort:'city' as result %}{% for item in result "
1123                                    "%}{{ item.city }},{% endfor %}{% endwith %}"
1124                                 << dict << "Berlin,Dublin,London,Paris," << NoError;
1125 
1126     {
1127         // Test duplication works
1128         QVariantHash map;
1129         map.insert(QStringLiteral("city"), QStringLiteral("Berlin"));
1130         mapList << map;
1131     }
1132     dict.insert(QStringLiteral("mapList"), mapList);
1133 
1134     QTest::newRow("dictsort02") << "{% with mapList|dictsort:'city' as result %}{% for item in result "
1135                                    "%}{{ item.city }},{% endfor %}{% endwith %}"
1136                                 << dict << "Berlin,Berlin,Dublin,London,Paris," << NoError;
1137 
1138     dict.clear();
1139 
1140     QVariantList listList;
1141 
1142     const auto countries = QStringList{QStringLiteral("England"), QStringLiteral("Germany"), QStringLiteral("France"), QStringLiteral("Ireland")};
1143 
1144     const auto languages = QStringList{QStringLiteral("English"), QStringLiteral("German"), QStringLiteral("French"), QStringLiteral("Irish")};
1145 
1146     for (auto i = 0; i < cities.size(); ++i) {
1147         listList << QVariant(QVariantList{cities.at(i), countries.at(i), languages.at(i)});
1148     }
1149 
1150     dict.insert(QStringLiteral("listList"), listList);
1151 
1152     QTest::newRow("dictsort03") << "{% with listList|dictsort:'0' as result %}{% for item in result "
1153                                    "%}{{ "
1154                                    "item.0 }};{{ item.1 }};{{ item.2 }},{% endfor %}{% endwith %}"
1155                                 << dict
1156                                 << "Berlin;Germany;German,Dublin;Ireland;Irish,London;England;"
1157                                    "English,Paris;France;French,"
1158                                 << NoError;
1159 
1160     QTest::newRow("dictsort04") << "{% with listList|dictsort:'1' as result %}{% for item in result "
1161                                    "%}{{ "
1162                                    "item.0 }};{{ item.1 }};{{ item.2 }},{% endfor %}{% endwith %}"
1163                                 << dict
1164                                 << "London;England;English,Paris;France;French,Berlin;Germany;"
1165                                    "German,Dublin;Ireland;Irish,"
1166                                 << NoError;
1167 }
1168 
1169 void TestFilters::testLogicFilters_data()
1170 {
1171     QTest::addColumn<QString>("input");
1172     QTest::addColumn<Dict>("dict");
1173     QTest::addColumn<QString>("output");
1174     QTest::addColumn<KTextTemplate::Error>("error");
1175 
1176     Dict dict;
1177 
1178     //  Literal string arguments to the default filter are always treated as
1179     //  safe strings, regardless of the auto-escaping state.
1180 
1181     //  Note: we have to use {"a": ""} here, otherwise the invalid template
1182     //  variable string interferes with the test result.
1183 
1184     dict.insert(QStringLiteral("a"), QStringLiteral(""));
1185 
1186     QTest::newRow("filter-default01") << "{{ a|default:\"x<\" }}" << dict << QStringLiteral("x<") << NoError;
1187     QTest::newRow("filter-default02") << "{% autoescape off %}{{ a|default:\"x<\" }}{% endautoescape %}" << dict << QStringLiteral("x<") << NoError;
1188 
1189     dict.clear();
1190     dict.insert(QStringLiteral("a"), QVariant::fromValue(markSafe(QStringLiteral("x>"))));
1191 
1192     QTest::newRow("filter-default03") << "{{ a|default:\"x<\" }}" << dict << QStringLiteral("x>") << NoError;
1193     QTest::newRow("filter-default04") << "{% autoescape off %}{{ a|default:\"x<\" }}{% endautoescape %}" << dict << QStringLiteral("x>") << NoError;
1194 
1195     dict.clear();
1196     dict.insert(QStringLiteral("a"), QVariant());
1197 
1198     QTest::newRow("filter-default_if_none01") << "{{ a|default:\"x<\" }}" << dict << QStringLiteral("x<") << NoError;
1199     QTest::newRow("filter-default_if_none02") << "{% autoescape off %}{{ a|default:\"x<\" }}{% endautoescape %}" << dict << QStringLiteral("x<") << NoError;
1200 }
1201 
1202 void TestFilters::testMiscFilters_data()
1203 {
1204     QTest::addColumn<QString>("input");
1205     QTest::addColumn<Dict>("dict");
1206     QTest::addColumn<QString>("output");
1207     QTest::addColumn<KTextTemplate::Error>("error");
1208 
1209     Dict dict;
1210 
1211     //
1212     //   //  {"a": "<1-800-call-me>", "b": mark_safe("<1-800-call-me>")
1213     //   QTest::newRow( "filter-phone2numeric01") << QString::fromLatin1( "{{
1214     //   a|phone2numeric }} {{ b|phone2numeric }}" ) << dict <<
1215     //   QString::fromLatin1( "&lt;1-800-2255-63&gt; <1-800-2255-63>" ) <<
1216     //   NoError;
1217     //
1218     //   //  {"a": "<1-800-call-me>", "b": mark_safe("<1-800-call-me>")
1219     //   QTest::newRow( "filter-phone2numeric02") << QString::fromLatin1( "{%
1220     //   autoescape off %}{{ a|phone2numeric }} {{ b|phone2numeric }}{%
1221     //   endautoescape %}" ) << dict << QString::fromLatin1( "<1-800-2255-63>
1222     //   <1-800-2255-63>" ) << NoError;
1223     //
1224     //  Chaining a bunch of safeness-preserving filters should not alter
1225     //  the safe status either way.
1226 
1227     dict.insert(QStringLiteral("a"), QStringLiteral("a < b"));
1228     dict.insert(QStringLiteral("b"), QVariant::fromValue(markSafe(QStringLiteral("a < b"))));
1229 
1230     QTest::newRow("chaining01") << R"({{ a|capfirst|center:"7" }}.{{ b|capfirst|center:"7" }})" << dict << QStringLiteral(" A &lt; b . A < b ") << NoError;
1231     QTest::newRow("chaining02") << "{% autoescape off %}{{ a|capfirst|center:\"7\" }}.{{ "
1232                                    "b|capfirst|center:\"7\" }}{% endautoescape %}"
1233                                 << dict << QStringLiteral(" A < b . A < b ") << NoError;
1234 
1235     //  Using a filter that forces a string back to unsafe:
1236 
1237     QTest::newRow("chaining03") << R"({{ a|cut:"b"|capfirst }}.{{ b|cut:"b"|capfirst }})" << dict << QStringLiteral("A &lt; .A < ") << NoError;
1238     QTest::newRow("chaining04") << "{% autoescape off %}{{ a|cut:\"b\"|capfirst }}.{{ "
1239                                    "b|cut:\"b\"|capfirst }}{% endautoescape %}"
1240                                 << dict << QStringLiteral("A < .A < ") << NoError;
1241 
1242     dict.clear();
1243     dict.insert(QStringLiteral("a"), QStringLiteral("a < b"));
1244 
1245     //  Using a filter that forces safeness does not lead to double-escaping
1246 
1247     QTest::newRow("chaining05") << QStringLiteral("{{ a|escape|capfirst }}") << dict << QStringLiteral("A &lt; b") << NoError;
1248     QTest::newRow("chaining06") << QStringLiteral("{% autoescape off %}{{ a|escape|capfirst }}{% endautoescape %}") << dict << QStringLiteral("A &lt; b")
1249                                 << NoError;
1250 
1251     //  Force to safe, then back (also showing why using force_escape too
1252     //  early in a chain can lead to unexpected results).
1253 
1254     QTest::newRow("chaining07") << "{{ a|force_escape|cut:\";\" }}" << dict << QStringLiteral("a &amp;lt b") << NoError;
1255     QTest::newRow("chaining08") << "{% autoescape off %}{{ a|force_escape|cut:\";\" }}{% endautoescape "
1256                                    "%}"
1257                                 << dict << QStringLiteral("a &lt b") << NoError;
1258     QTest::newRow("chaining09") << "{{ a|cut:\";\"|force_escape }}" << dict << QStringLiteral("a &lt; b") << NoError;
1259     QTest::newRow("chaining10") << "{% autoescape off %}{{ a|cut:\";\"|force_escape }}{% endautoescape "
1260                                    "%}"
1261                                 << dict << QStringLiteral("a &lt; b") << NoError;
1262     QTest::newRow("chaining11") << "{{ a|cut:\"b\"|safe }}" << dict << QStringLiteral("a < ") << NoError;
1263     QTest::newRow("chaining12") << "{% autoescape off %}{{ a|cut:\"b\"|safe }}{% endautoescape %}" << dict << QStringLiteral("a < ") << NoError;
1264     QTest::newRow("chaining13") << QStringLiteral("{{ a|safe|force_escape }}") << dict << QStringLiteral("a &lt; b") << NoError;
1265     QTest::newRow("chaining14") << QStringLiteral("{% autoescape off %}{{ a|safe|force_escape }}{% endautoescape %}") << dict << QStringLiteral("a &lt; b")
1266                                 << NoError;
1267 
1268     //   //  Filters decorated with stringfilter still respect is_safe.
1269     //
1270     //   //  {"unsafe": UnsafeClass()
1271     //   QTest::newRow( "autoescape-stringfilter01") << QString::fromLatin1( "{{
1272     //   unsafe|capfirst }}" ) << dict << QString::fromLatin1( "You &amp; me" )
1273     //   <<
1274     //   NoError;
1275     //
1276     //   //  {"unsafe": UnsafeClass()
1277     //   QTest::newRow( "autoescape-stringfilter02") << QString::fromLatin1( "{%
1278     //   autoescape off %}{{ unsafe|capfirst }}{% endautoescape %}" ) << dict <<
1279     //   QString::fromLatin1( "You & me" ) << NoError;
1280     //
1281     //   //  {"safe": SafeClass()
1282     //   QTest::newRow( "autoescape-stringfilter03") << QString::fromLatin1( "{{
1283     //   safe|capfirst }}" ) << dict << QString::fromLatin1( "You &gt; me" ) <<
1284     //   NoError;
1285     //
1286     //   //  {"safe": SafeClass()
1287     //   QTest::newRow( "autoescape-stringfilter04") << QString::fromLatin1( "{%
1288     //   autoescape off %}{{ safe|capfirst }}{% endautoescape %}" ) << dict <<
1289     //   QString::fromLatin1( "You &gt; me" ) << NoError;
1290     //
1291 }
1292 
1293 void TestFilters::testIntegerFilters_data()
1294 {
1295     QTest::addColumn<QString>("input");
1296     QTest::addColumn<Dict>("dict");
1297     QTest::addColumn<QString>("output");
1298     QTest::addColumn<KTextTemplate::Error>("error");
1299 
1300     Dict dict;
1301 
1302     dict.insert(QStringLiteral("i"), 2000);
1303 
1304     QTest::newRow("add01") << QStringLiteral("{{ i|add:5 }}") << dict << QStringLiteral("2005") << NoError;
1305     QTest::newRow("add02") << QStringLiteral("{{ i|add:\"napis\" }}") << dict << QStringLiteral("2000") << NoError;
1306 
1307     dict.clear();
1308     dict.insert(QStringLiteral("i"), QStringLiteral("not_an_int"));
1309 
1310     QTest::newRow("add03") << QStringLiteral("{{ i|add:16 }}") << dict << QStringLiteral("not_an_int") << NoError;
1311     QTest::newRow("add04") << QStringLiteral("{{ i|add:\"16\" }}") << dict << QStringLiteral("not_an_int16") << NoError;
1312 
1313     dict.clear();
1314     dict.insert(QStringLiteral("l1"), QVariantList{1, 2});
1315     dict.insert(QStringLiteral("l2"), QVariantList{3, 4});
1316 
1317     QTest::newRow("add05") << QStringLiteral("{{ l1|add:l2 }}") << dict << QStringLiteral("[1, 2, 3, 4]") << NoError;
1318     // QTest::newRow( "add06" ) << QString::fromLatin1( "{{ t1|add:t2 }}" ) <<
1319     // dict << QString::fromLatin1( "2005" ) << NoError;
1320 
1321     // QTest::newRow( "add07" ) << QString::fromLatin1( "{{ d|add:t }}" ) <<
1322     // dict
1323     // << QString::fromLatin1( "2005" ) << NoError;
1324 
1325     QTest::newRow("add08") << QStringLiteral("{{ 1|add:2 }}") << dict << QStringLiteral("3") << NoError;
1326     dict.clear();
1327     dict.insert(QStringLiteral("l1"), QStringList{QStringLiteral("one"), QStringLiteral("two")});
1328     dict.insert(QStringLiteral("l2"), QStringList{QStringLiteral("three"), QStringLiteral("four")});
1329 
1330     QTest::newRow("add09") << QStringLiteral("{{ l1|add:l2|join:\", \" }}") << dict << QStringLiteral("one, two, three, four") << NoError;
1331 
1332     QTest::newRow("filter-getdigit01") << QStringLiteral("{{ 123|get_digit:1 }}") << dict << QStringLiteral("3") << NoError;
1333     QTest::newRow("filter-getdigit02") << QStringLiteral("{{ 123|get_digit:2 }}") << dict << QStringLiteral("2") << NoError;
1334     QTest::newRow("filter-getdigit03") << QStringLiteral("{{ 123|get_digit:3 }}") << dict << QStringLiteral("1") << NoError;
1335     QTest::newRow("filter-getdigit04") << QStringLiteral("{{ 123|get_digit:4 }}") << dict << QStringLiteral("123") << NoError;
1336 }
1337 
1338 QTEST_MAIN(TestFilters)
1339 #include "testfilters.moc"
1340 
1341 #endif