File indexing completed on 2024-11-17 03:42:07
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 BUILTINSTEST_H 0011 #define BUILTINSTEST_H 0012 0013 #include <QDebug> 0014 #include <QFileInfo> 0015 #include <QTest> 0016 0017 #include "cachingloaderdecorator.h" 0018 #include "context.h" 0019 #include "engine.h" 0020 #include "filterexpression.h" 0021 #include "ktexttemplate_paths.h" 0022 #include "template.h" 0023 #include "util.h" 0024 #include <metaenumvariable_p.h> 0025 0026 using Dict = QHash<QString, QVariant>; 0027 0028 Q_DECLARE_METATYPE(KTextTemplate::Error) 0029 0030 using namespace KTextTemplate; 0031 0032 /** 0033 For use with tests. 0034 */ 0035 class OtherClass : public QObject 0036 { 0037 Q_OBJECT 0038 Q_PROPERTY(QString method READ method) 0039 Q_PROPERTY(Animals animals READ animals) 0040 public: 0041 enum Animals { Lions, Tigers, Bears }; 0042 Q_ENUMS(Animals) 0043 0044 OtherClass(QObject *parent = {}) 0045 : QObject(parent) 0046 { 0047 } 0048 OtherClass(Animals animals, QObject *parent = {}) 0049 : QObject(parent) 0050 , m_animals(animals) 0051 { 0052 } 0053 0054 Animals animals() const 0055 { 0056 return m_animals; 0057 } 0058 0059 QString method() const 0060 { 0061 return QStringLiteral("OtherClass::method"); 0062 } 0063 0064 private: 0065 Animals m_animals = Tigers; 0066 }; 0067 0068 /** 0069 For use with tests. 0070 */ 0071 class SomeClass : public QObject 0072 { 0073 Q_OBJECT 0074 Q_PROPERTY(QString method READ method) 0075 Q_PROPERTY(QVariant otherClass READ otherClass) 0076 0077 public: 0078 enum FirstEnum { Employee, Employer, Manager }; 0079 0080 enum SecondEnum { Voter = 2, Consumer = 4, Citizen = 8 }; 0081 0082 Q_ENUMS(FirstEnum SecondEnum) 0083 0084 SomeClass(QObject *parent = {}) 0085 : QObject(parent) 0086 , m_other(new OtherClass(this)) 0087 { 0088 } 0089 0090 QString method() const 0091 { 0092 return QStringLiteral("SomeClass::method"); 0093 } 0094 0095 QVariant otherClass() const 0096 { 0097 return QVariant::fromValue(m_other); 0098 } 0099 0100 QString nonAccessibleMethod(const QString &str) 0101 { 0102 return str; 0103 } 0104 0105 private: 0106 QObject *m_other; 0107 }; 0108 0109 /** 0110 For use with tests. 0111 */ 0112 class GadgetClass 0113 { 0114 Q_GADGET 0115 Q_PROPERTY(PersonName personName READ personName) 0116 public: 0117 enum PersonName { Mike = 0, Natalie, Oliver }; 0118 Q_ENUM(PersonName) 0119 0120 GadgetClass() = default; 0121 GadgetClass(PersonName pn) 0122 : m_personName(pn) 0123 { 0124 } 0125 0126 PersonName personName() const 0127 { 0128 return m_personName; 0129 } 0130 0131 private: 0132 PersonName m_personName = Oliver; 0133 }; 0134 Q_DECLARE_METATYPE(GadgetClass) 0135 0136 class NoEscapeOutputStream : public OutputStream 0137 { 0138 public: 0139 NoEscapeOutputStream() 0140 { 0141 } 0142 0143 NoEscapeOutputStream(QTextStream *stream) 0144 : OutputStream(stream) 0145 { 0146 } 0147 0148 QSharedPointer<OutputStream> clone(QTextStream *stream) const override 0149 { 0150 return QSharedPointer<NoEscapeOutputStream>::create(stream); 0151 } 0152 0153 QString escape(const QString &input) const override 0154 { 0155 return input; 0156 } 0157 }; 0158 0159 class JSOutputStream : public OutputStream 0160 { 0161 public: 0162 JSOutputStream() 0163 { 0164 } 0165 0166 JSOutputStream(QTextStream *stream) 0167 : OutputStream(stream) 0168 { 0169 } 0170 0171 QSharedPointer<OutputStream> clone(QTextStream *stream) const override 0172 { 0173 return QSharedPointer<JSOutputStream>::create(stream); 0174 } 0175 0176 QString escape(const QString &input) const override 0177 { 0178 QList<std::pair<QString, QString>> jsEscapes; 0179 jsEscapes << std::pair<QString, QString>(QChar::fromLatin1('\\'), QStringLiteral("\\u005C")) 0180 << std::pair<QString, QString>(QChar::fromLatin1('\''), QStringLiteral("\\u0027")) 0181 << std::pair<QString, QString>(QChar::fromLatin1('\"'), QStringLiteral("\\u0022")) 0182 << std::pair<QString, QString>(QChar::fromLatin1('>'), QStringLiteral("\\u003E")) 0183 << std::pair<QString, QString>(QChar::fromLatin1('<'), QStringLiteral("\\u003C")) 0184 << std::pair<QString, QString>(QChar::fromLatin1('&'), QStringLiteral("\\u0026")) 0185 << std::pair<QString, QString>(QChar::fromLatin1('='), QStringLiteral("\\u003D")) 0186 << std::pair<QString, QString>(QChar::fromLatin1('-'), QStringLiteral("\\u002D")) 0187 << std::pair<QString, QString>(QChar::fromLatin1(';'), QStringLiteral("\\u003B")) 0188 << std::pair<QString, QString>(QChar(0x2028), QStringLiteral("\\u2028")) 0189 << std::pair<QString, QString>(QChar(0x2029), QStringLiteral("\\u2029")); 0190 0191 for (auto i = 0; i < 32; ++i) { 0192 jsEscapes << std::pair<QString, QString>(QChar(i), QStringLiteral("\\u00") + QStringLiteral("%1").arg(i, 2, 16, QLatin1Char('0')).toUpper()); 0193 } 0194 0195 auto retString = input; 0196 for (const auto &escape : std::as_const(jsEscapes)) { 0197 retString = retString.replace(escape.first, escape.second); 0198 } 0199 return retString; 0200 } 0201 }; 0202 0203 class TestBuiltinSyntax : public QObject 0204 { 0205 Q_OBJECT 0206 0207 private Q_SLOTS: 0208 void initTestCase(); 0209 0210 void testObjects(); 0211 0212 void testTruthiness_data(); 0213 void testTruthiness(); 0214 0215 void testRenderAfterError(); 0216 0217 void testBasicSyntax_data(); 0218 void testBasicSyntax() 0219 { 0220 doTest(); 0221 } 0222 0223 void testEnums_data(); 0224 void testEnums() 0225 { 0226 doTest(); 0227 } 0228 0229 void testListIndex_data(); 0230 void testListIndex() 0231 { 0232 doTest(); 0233 } 0234 0235 void testFilterSyntax_data(); 0236 void testFilterSyntax() 0237 { 0238 doTest(); 0239 } 0240 0241 void testCommentSyntax_data(); 0242 void testCommentSyntax() 0243 { 0244 doTest(); 0245 } 0246 0247 void testMultiline_data(); 0248 void testMultiline() 0249 { 0250 doTest(); 0251 } 0252 0253 void testEscaping_data(); 0254 void testEscaping() 0255 { 0256 doTest(); 0257 } 0258 0259 void testTypeAccessors_data(); 0260 void testTypeAccessors() 0261 { 0262 doTest(); 0263 } 0264 void testTypeAccessorsUnordered_data(); 0265 void testTypeAccessorsUnordered(); 0266 0267 void testMultipleStates(); 0268 void testAlternativeEscaping(); 0269 0270 void testTemplatePathSafety_data(); 0271 void testTemplatePathSafety(); 0272 0273 void testMediaPathSafety_data(); 0274 void testMediaPathSafety(); 0275 0276 void testDynamicProperties_data(); 0277 void testDynamicProperties() 0278 { 0279 doTest(); 0280 } 0281 0282 void testGarbageInput_data(); 0283 void testGarbageInput(); 0284 0285 void testInsignificantWhitespace_data(); 0286 void testInsignificantWhitespace(); 0287 0288 void cleanupTestCase(); 0289 0290 private: 0291 Engine *m_engine; 0292 0293 QSharedPointer<InMemoryTemplateLoader> m_loader; 0294 0295 Engine *getEngine(); 0296 0297 void doTest(); 0298 }; 0299 0300 void TestBuiltinSyntax::testObjects() 0301 { 0302 { 0303 auto loader = QSharedPointer<KTextTemplate::FileSystemTemplateLoader>::create(); 0304 loader->setTemplateDirs({QStringLiteral("/path/one"), QStringLiteral("/path/two")}); 0305 0306 auto cache = QSharedPointer<KTextTemplate::CachingLoaderDecorator>::create(loader); 0307 } 0308 0309 Context c1, c2; 0310 c1 = c1; 0311 c1 = c2; 0312 Context c3(c1); 0313 Q_UNUSED(c3); 0314 0315 FilterExpression f1, f2; 0316 f1 = f1; 0317 f1 = f2; 0318 FilterExpression f3(f1); 0319 Q_UNUSED(f3); 0320 0321 Variable v1; 0322 v1 = v1; 0323 v1 = f1.variable(); 0324 Variable v3(v1); 0325 Q_UNUSED(v3); 0326 QVERIFY(!v1.isTrue(&c1)); 0327 QVERIFY(!v1.isLocalized()); 0328 0329 c1.setMutating(true); 0330 QVERIFY(c1.isMutating()); 0331 0332 SafeString s1, s2; 0333 s1 = s1; 0334 s2 = s1; 0335 SafeString s3(s1); 0336 Q_UNUSED(s3); 0337 0338 QMetaType{qMetaTypeId<MetaEnumVariable>()}.create(nullptr); 0339 } 0340 0341 void TestBuiltinSyntax::testTruthiness_data() 0342 { 0343 QTest::addColumn<QVariant>("input"); 0344 QTest::addColumn<bool>("expected"); 0345 0346 QTest::newRow("truthtest-01") << QVariant() << false; 0347 QTest::newRow("truthtest-02") << QVariant(false) << false; 0348 QTest::newRow("truthtest-03") << QVariant(true) << true; 0349 0350 QTest::newRow("truthtest-04") << QVariant(0) << false; 0351 QTest::newRow("truthtest-05") << QVariant(1) << true; 0352 0353 { 0354 auto falseV = QVariant::fromValue<int>(0); 0355 QTest::newRow("truthtest-06") << falseV << false; 0356 auto trueV = QVariant::fromValue<int>(1); 0357 QTest::newRow("truthtest-07") << trueV << true; 0358 } 0359 { 0360 auto falseV = QVariant::fromValue<uint>(0); 0361 QTest::newRow("truthtest-08") << falseV << false; 0362 auto trueV = QVariant::fromValue<uint>(1); 0363 QTest::newRow("truthtest-09") << trueV << true; 0364 } 0365 { 0366 auto falseV = QVariant::fromValue<qlonglong>(0); 0367 QTest::newRow("truthtest-10") << falseV << false; 0368 auto trueV = QVariant::fromValue<qlonglong>(1); 0369 QTest::newRow("truthtest-11") << trueV << true; 0370 } 0371 { 0372 auto falseV = QVariant::fromValue<qulonglong>(0); 0373 QTest::newRow("truthtest-12") << falseV << false; 0374 auto trueV = QVariant::fromValue<qulonglong>(1); 0375 QTest::newRow("truthtest-13") << trueV << true; 0376 } 0377 { 0378 auto falseV = QVariant::fromValue<double>(0); 0379 QTest::newRow("truthtest-14") << falseV << false; 0380 auto trueV = QVariant::fromValue<double>(1); 0381 QTest::newRow("truthtest-15") << trueV << true; 0382 } 0383 { 0384 auto falseV = QVariant::fromValue<float>(0); 0385 QTest::newRow("truthtest-16") << falseV << false; 0386 auto trueV = QVariant::fromValue<float>(1); 0387 QTest::newRow("truthtest-17") << trueV << true; 0388 } 0389 { 0390 auto falseV = QVariant::fromValue<char>(0); 0391 QTest::newRow("truthtest-18") << falseV << false; 0392 auto trueV = QVariant::fromValue<char>(1); 0393 QTest::newRow("truthtest-19") << trueV << true; 0394 } 0395 0396 QTest::newRow("truthtest-20") << QVariant::fromValue(QString()) << false; 0397 QTest::newRow("truthtest-21") << QVariant::fromValue(QStringLiteral("")) << false; 0398 QTest::newRow("truthtest-22") << QVariant::fromValue(QStringLiteral("false")) << true; 0399 QTest::newRow("truthtest-23") << QVariant::fromValue(QStringLiteral("true")) << true; 0400 QTest::newRow("truthtest-24") << QVariant::fromValue(QStringLiteral("anystring")) << true; 0401 0402 { 0403 QVariantList l; 0404 QTest::newRow("truthtest-25") << QVariant::fromValue(l) << false; 0405 l.append(1); 0406 QTest::newRow("truthtest-26") << QVariant::fromValue(l) << true; 0407 } 0408 { 0409 QVariantHash h; 0410 QTest::newRow("truthtest-27") << QVariant::fromValue(h) << false; 0411 h.insert(QStringLiteral("value"), 1); 0412 QTest::newRow("truthtest-28") << QVariant::fromValue(h) << true; 0413 } 0414 0415 { 0416 QTest::newRow("truthtest-29") << QVariant::fromValue<QObject *>(nullptr) << false; 0417 auto plainO = new QObject(this); 0418 QTest::newRow("truthtest-30") << QVariant::fromValue(plainO) << true; 0419 auto trueO = new QObject(this); 0420 trueO->setProperty("__true__", true); 0421 QTest::newRow("truthtest-31") << QVariant::fromValue(trueO) << true; 0422 auto falseO = new QObject(this); 0423 falseO->setProperty("__true__", false); 0424 QTest::newRow("truthtest-32") << QVariant::fromValue(falseO) << false; 0425 } 0426 } 0427 0428 void TestBuiltinSyntax::testTruthiness() 0429 { 0430 QFETCH(QVariant, input); 0431 QFETCH(bool, expected); 0432 0433 QVERIFY(variantIsTrue(input) == expected); 0434 } 0435 0436 void TestBuiltinSyntax::testRenderAfterError() 0437 { 0438 Engine engine; 0439 engine.setPluginPaths({QStringLiteral(KTEXTTEMPLATE_PLUGIN_PATH)}); 0440 0441 QSharedPointer<InMemoryTemplateLoader> loader(new InMemoryTemplateLoader); 0442 loader->setTemplate(QStringLiteral("template1"), QStringLiteral("This template has an error {{ va>r }}")); 0443 loader->setTemplate(QStringLiteral("template2"), QStringLiteral("Ok")); 0444 loader->setTemplate(QStringLiteral("main"), QStringLiteral("{% include template_var %}")); 0445 0446 engine.addTemplateLoader(loader); 0447 0448 Context c; 0449 Template t; 0450 0451 t = engine.loadByName(QStringLiteral("main")); 0452 0453 c.insert(QStringLiteral("template_var"), QLatin1String("template1")); 0454 auto output = t->render(&c); 0455 QCOMPARE(output, QString()); 0456 QCOMPARE(t->error(), TagSyntaxError); 0457 0458 c.insert(QStringLiteral("template_var"), QLatin1String("template2")); 0459 QCOMPARE(t->render(&c), QLatin1String("Ok")); 0460 QCOMPARE(t->error(), NoError); 0461 } 0462 0463 void TestBuiltinSyntax::initTestCase() 0464 { 0465 m_engine = getEngine(); 0466 m_loader = QSharedPointer<InMemoryTemplateLoader>(new InMemoryTemplateLoader()); 0467 m_engine->addTemplateLoader(m_loader); 0468 QVERIFY(m_engine->templateLoaders().contains(m_loader)); 0469 } 0470 0471 Engine *TestBuiltinSyntax::getEngine() 0472 { 0473 auto engine = new Engine(this); 0474 engine->setPluginPaths({QStringLiteral(KTEXTTEMPLATE_PLUGIN_PATH)}); 0475 return engine; 0476 } 0477 0478 void TestBuiltinSyntax::cleanupTestCase() 0479 { 0480 delete m_engine; 0481 } 0482 0483 void TestBuiltinSyntax::doTest() 0484 { 0485 QFETCH(QString, input); 0486 QFETCH(Dict, dict); 0487 QFETCH(QString, output); 0488 QFETCH(KTextTemplate::Error, error); 0489 0490 auto t = m_engine->newTemplate(input, QLatin1String(QTest::currentDataTag())); 0491 0492 if (t->error() != NoError) { 0493 if (t->error() != error) 0494 qDebug() << t->errorString(); 0495 QCOMPARE(t->error(), error); 0496 return; 0497 } 0498 0499 Context context(dict); 0500 0501 auto result = t->render(&context); 0502 if (t->error() != NoError) { 0503 if (t->error() != error) 0504 qDebug() << t->errorString(); 0505 QCOMPARE(t->error(), error); 0506 return; 0507 } 0508 0509 QCOMPARE(t->error(), NoError); 0510 0511 // Didn't catch any errors, so make sure I didn't expect any. 0512 QCOMPARE(NoError, error); 0513 0514 QCOMPARE(result, output); 0515 } 0516 0517 void TestBuiltinSyntax::testBasicSyntax_data() 0518 { 0519 QTest::addColumn<QString>("input"); 0520 QTest::addColumn<Dict>("dict"); 0521 QTest::addColumn<QString>("output"); 0522 QTest::addColumn<KTextTemplate::Error>("error"); 0523 0524 Dict dict; 0525 0526 QTest::newRow("basic-syntax00") << QString() << dict << QString() << NoError; 0527 0528 // Plain text should go through the template parser untouched 0529 QTest::newRow("basic-syntax01") << QStringLiteral("something cool") << dict << QStringLiteral("something cool") << NoError; 0530 0531 // Variables should be replaced with their value in the current 0532 // context 0533 dict.insert(QStringLiteral("headline"), QStringLiteral("Success")); 0534 QTest::newRow("basic-syntax02") << QStringLiteral("{{ headline }}") << dict << QStringLiteral("Success") << NoError; 0535 0536 dict.clear(); 0537 dict.insert(QStringLiteral("first"), 1); 0538 dict.insert(QStringLiteral("second"), 2); 0539 0540 // More than one replacement variable is allowed in a template 0541 QTest::newRow("basic-syntax03") << QStringLiteral("{{ first }} --- {{ second }}") << dict << QStringLiteral("1 --- 2") << NoError; 0542 0543 dict.clear(); 0544 // Fail silently when a variable is not found in the current context 0545 QTest::newRow("basic-syntax04") << QStringLiteral("as{{ missing }}df") << dict << QStringLiteral("asdf") << NoError; 0546 0547 // A variable may not contain more than one word 0548 QTest::newRow("basic-syntax06") << QStringLiteral("{{ multi word variable }}") << dict << QString() << TagSyntaxError; 0549 // Raise TemplateSyntaxError for empty variable tags 0550 QTest::newRow("basic-syntax07") << QStringLiteral("{{ }}") << dict << QString() << EmptyVariableError; 0551 QTest::newRow("basic-syntax08") << QStringLiteral("{{ }}") << dict << QString() << EmptyVariableError; 0552 0553 // Attribute syntax allows a template to call an object's attribute 0554 0555 auto someClass = new SomeClass(this); 0556 dict.insert(QStringLiteral("var"), QVariant::fromValue(someClass)); 0557 0558 QTest::newRow("basic-syntax09") << QStringLiteral("{{ var.method }}") << dict << QStringLiteral("SomeClass::method") << NoError; 0559 0560 // Multiple levels of attribute access are allowed 0561 QTest::newRow("basic-syntax10") << QStringLiteral("{{ var.otherClass.method }}") << dict << QStringLiteral("OtherClass::method") << NoError; 0562 0563 // Fail silently when a variable's attribute isn't found 0564 QTest::newRow("basic-syntax11") << QStringLiteral("{{ var.blech }}") << dict << QString() << NoError; 0565 0566 // TODO: Needed? 0567 // Raise TemplateSyntaxError when trying to access a variable beginning with 0568 // an underscore 0569 // #C# {"var": SomeClass()} 0570 dict.clear(); 0571 QVariantHash hash; 0572 hash.insert(QStringLiteral("__dict__"), QStringLiteral("foo")); 0573 dict.insert(QStringLiteral("var"), hash); 0574 QTest::newRow("basic-syntax12") << QStringLiteral("{{ var.__dict__ }}") << dict << QString() << TagSyntaxError; 0575 0576 dict.clear(); 0577 // Raise TemplateSyntaxError when trying to access a variable containing an 0578 // illegal character 0579 QTest::newRow("basic-syntax13") << QStringLiteral("{{ va>r }}") << dict << QString() << TagSyntaxError; 0580 QTest::newRow("basic-syntax14") << QStringLiteral("{{ (var.r) }}") << dict << QString() << TagSyntaxError; 0581 QTest::newRow("basic-syntax15") << QStringLiteral("{{ sp%am }}") << dict << QString() << TagSyntaxError; 0582 QTest::newRow("basic-syntax16") << QStringLiteral("{{ eggs! }}") << dict << QString() << TagSyntaxError; 0583 QTest::newRow("basic-syntax17") << QStringLiteral("{{ moo? }}") << dict << QString() << TagSyntaxError; 0584 QTest::newRow("basic-syntax-error01") << QStringLiteral("{{ moo:arg }}") << dict << QString() << TagSyntaxError; 0585 QTest::newRow("basic-syntax-error02") << QStringLiteral("{{ moo|cut:'foo':'bar' }}") << dict << QString() << TagSyntaxError; 0586 0587 // Attribute syntax allows a template to call a dictionary key's value 0588 0589 hash.clear(); 0590 hash.insert(QStringLiteral("bar"), QStringLiteral("baz")); 0591 dict.insert(QStringLiteral("foo"), hash); 0592 QTest::newRow("basic-syntax18") << QStringLiteral("{{ foo.bar }}") << dict << QStringLiteral("baz") << NoError; 0593 0594 // Fail silently when a variable's dictionary key isn't found 0595 QTest::newRow("basic-syntax19") << QStringLiteral("{{ foo.spam }}") << dict << QString() << NoError; 0596 0597 dict.clear(); 0598 dict.insert(QStringLiteral("var"), QVariant::fromValue(someClass)); 0599 // Fail silently when attempting to access an unavailable method 0600 QTest::newRow("basic-syntax20") << QStringLiteral("{{ var.nonAccessibleMethod }}") << dict << QString() << NoError; 0601 0602 dict.clear(); 0603 // Don't get confused when parsing something that is almost, but not 0604 // quite, a template tag. 0605 QTest::newRow("basic-syntax21") << QStringLiteral("a {{ moo %} b") << dict << QStringLiteral("a {{ moo %} b") << NoError; 0606 QTest::newRow("basic-syntax22") << QStringLiteral("{{ moo #}") << dict << QStringLiteral("{{ moo #}") << NoError; 0607 0608 dict.insert(QStringLiteral("cow"), QStringLiteral("cow")); 0609 // Will try to treat "moo #} {{ cow" as the variable. Not ideal, but 0610 // costly to work around, so this triggers an error. 0611 QTest::newRow("basic-syntax23") << QStringLiteral("{{ moo #} {{ cow }}") << dict << QString() << TagSyntaxError; 0612 0613 dict.clear(); 0614 // Embedded newlines make it not-a-tag. 0615 QTest::newRow("basic-syntax24") << "{{ moo\n }}" << dict << "{{ moo\n }}" << NoError; 0616 // Literal strings are permitted inside variables, mostly for i18n 0617 // purposes. 0618 QTest::newRow("basic-syntax25") << "{{ \"fred\" }}" << dict << QStringLiteral("fred") << NoError; 0619 QTest::newRow("basic-syntax26") << R"({{ "\"fred\"" }})" << dict << "\"fred\"" << NoError; 0620 QTest::newRow("basic-syntax27") << R"({{ _("\"fred\"") }})" << dict << ""fred"" << NoError; 0621 0622 dict.clear(); 0623 hash.clear(); 0624 QVariantHash innerHash; 0625 innerHash.insert(QStringLiteral("3"), QStringLiteral("d")); 0626 hash.insert(QStringLiteral("2"), innerHash); 0627 dict.insert(QStringLiteral("1"), hash); 0628 0629 QTest::newRow("basic-syntax28") << QStringLiteral("{{ 1.2.3 }}") << dict << QStringLiteral("d") << NoError; 0630 0631 dict.clear(); 0632 hash.clear(); 0633 QVariantList list{QStringLiteral("a"), QStringLiteral("b"), QStringLiteral("c"), QStringLiteral("d")}; 0634 hash.insert(QStringLiteral("2"), list); 0635 dict.insert(QStringLiteral("1"), hash); 0636 QTest::newRow("basic-syntax29") << QStringLiteral("{{ 1.2.3 }}") << dict << QStringLiteral("d") << NoError; 0637 0638 dict.clear(); 0639 list.clear(); 0640 QVariantList innerList{QStringLiteral("x"), QStringLiteral("x"), QStringLiteral("x"), QStringLiteral("x")}; 0641 list.append(QVariant(innerList)); 0642 innerList = {QStringLiteral("y"), QStringLiteral("y"), QStringLiteral("y"), QStringLiteral("y")}; 0643 list.append(QVariant(innerList)); 0644 innerList = {QStringLiteral("a"), QStringLiteral("b"), QStringLiteral("c"), QStringLiteral("d")}; 0645 list.append(QVariant(innerList)); 0646 dict.insert(QStringLiteral("1"), list); 0647 0648 QTest::newRow("basic-syntax30") << QStringLiteral("{{ 1.2.3 }}") << dict << QStringLiteral("d") << NoError; 0649 0650 dict.clear(); 0651 list.clear(); 0652 innerList = {QStringLiteral("x"), QStringLiteral("x"), QStringLiteral("x"), QStringLiteral("x")}; 0653 list.append(QVariant(innerList)); 0654 innerList = {QStringLiteral("y"), QStringLiteral("y"), QStringLiteral("y"), QStringLiteral("y")}; 0655 list.append(QVariant(innerList)); 0656 innerList = {QStringLiteral("a"), QStringLiteral("b"), QStringLiteral("c"), QStringLiteral("d")}; 0657 list.append(QVariant(innerList)); 0658 dict.insert(QStringLiteral("1"), list); 0659 0660 QTest::newRow("basic-syntax31") << QStringLiteral("{{ 1.2.3 }}") << dict << QStringLiteral("d") << NoError; 0661 0662 dict.clear(); 0663 list.clear(); 0664 hash.clear(); 0665 hash.insert(QStringLiteral("x"), QStringLiteral("x")); 0666 list.append(hash); 0667 hash.clear(); 0668 hash.insert(QStringLiteral("y"), QStringLiteral("y")); 0669 list.append(hash); 0670 hash.clear(); 0671 hash.insert(QStringLiteral("3"), QStringLiteral("d")); 0672 list.append(hash); 0673 0674 dict.insert(QStringLiteral("1"), list); 0675 0676 QTest::newRow("basic-syntax32") << QStringLiteral("{{ 1.2.3 }}") << dict << QStringLiteral("d") << NoError; 0677 0678 dict.clear(); 0679 0680 dict.insert(QStringLiteral("1"), QStringLiteral("abc")); 0681 QTest::newRow("basic-syntax33") << QStringLiteral("{{ 1 }}") << dict << QStringLiteral("1") << NoError; 0682 QTest::newRow("basic-syntax34") << QStringLiteral("{{ 1.2 }}") << dict << QStringLiteral("1.2") << NoError; 0683 0684 dict.clear(); 0685 0686 dict.insert(QStringLiteral("abc"), QStringLiteral("def")); 0687 0688 QTest::newRow("basic-syntax35") << QStringLiteral("{{ abc._something }} {{ abc._something|upper }}") << dict << QString() << TagSyntaxError; 0689 0690 QTest::newRow("basic-syntax36") << "{{ \"fred }}" << dict << QString() << TagSyntaxError; 0691 QTest::newRow("basic-syntax37") << "{{ \'fred }}" << dict << QString() << TagSyntaxError; 0692 QTest::newRow("basic-syntax38") << "{{ \"fred\' }}" << dict << QString() << TagSyntaxError; 0693 QTest::newRow("basic-syntax39") << "{{ \'fred\" }}" << dict << QString() << TagSyntaxError; 0694 QTest::newRow("basic-syntax40") << "{{ _(\'fred }}" << dict << QString() << TagSyntaxError; 0695 QTest::newRow("basic-syntax41") << "{{ abc|removetags:_(\'fred }}" << dict << QString() << TagSyntaxError; 0696 } 0697 0698 void TestBuiltinSyntax::testEnums_data() 0699 { 0700 QTest::addColumn<QString>("input"); 0701 QTest::addColumn<Dict>("dict"); 0702 QTest::addColumn<QString>("output"); 0703 QTest::addColumn<KTextTemplate::Error>("error"); 0704 0705 Dict dict; 0706 0707 auto otherClass = new OtherClass(this); 0708 dict.insert(QStringLiteral("var"), QVariant::fromValue(otherClass)); 0709 0710 QTest::newRow("class-enums01") << QStringLiteral("{{ var.Lions }}") << dict << QStringLiteral("0") << NoError; 0711 QTest::newRow("class-enums02") << QStringLiteral("{{ var.Tigers }}") << dict << QStringLiteral("1") << NoError; 0712 QTest::newRow("class-enums03") << QStringLiteral("{{ var.Bears }}") << dict << QStringLiteral("2") << NoError; 0713 QTest::newRow("class-enums04") << QStringLiteral("{{ var.Hamsters }}") << dict << QString() << NoError; 0714 QTest::newRow("class-enums05") << QStringLiteral("{{ var.Tigers.name }}") << dict << QStringLiteral("Animals") << NoError; 0715 QTest::newRow("class-enums06") << QStringLiteral("{{ var.Tigers.scope }}") << dict << QStringLiteral("OtherClass") << NoError; 0716 QTest::newRow("class-enums07") << QStringLiteral("{{ var.Tigers.value }}") << dict << QStringLiteral("1") << NoError; 0717 QTest::newRow("class-enums08") << QStringLiteral("{{ var.Tigers.key }}") << dict << QStringLiteral("Tigers") << NoError; 0718 QTest::newRow("class-enums09") << QStringLiteral("{{ var.animals }}") << dict << QStringLiteral("1") << NoError; 0719 QTest::newRow("class-enums10") << QStringLiteral("{{ var.animals.name }}") << dict << QStringLiteral("Animals") << NoError; 0720 QTest::newRow("class-enums11") << QStringLiteral("{{ var.animals.scope }}") << dict << QStringLiteral("OtherClass") << NoError; 0721 QTest::newRow("class-enums12") << QStringLiteral("{{ var.animals.value }}") << dict << QStringLiteral("1") << NoError; 0722 QTest::newRow("class-enums13") << QStringLiteral("{{ var.animals.key }}") << dict << QStringLiteral("Tigers") << NoError; 0723 QTest::newRow("class-enums14") << QStringLiteral("{{ var.Animals.0 }}") << dict << QStringLiteral("0") << NoError; 0724 QTest::newRow("class-enums15") << QStringLiteral("{{ var.Animals.2 }}") << dict << QStringLiteral("2") << NoError; 0725 QTest::newRow("class-enums16") << QStringLiteral("{{ var.Animals.3 }}") << dict << QString() << NoError; 0726 QTest::newRow("class-enums17") << QStringLiteral("{{ var.Animals.0.name }}") << dict << QStringLiteral("Animals") << NoError; 0727 QTest::newRow("class-enums18") << QStringLiteral("{{ var.Animals.0.scope }}") << dict << QStringLiteral("OtherClass") << NoError; 0728 QTest::newRow("class-enums19") << QStringLiteral("{{ var.Animals.0.value }}") << dict << QStringLiteral("0") << NoError; 0729 QTest::newRow("class-enums20") << QStringLiteral("{{ var.Animals.0.key }}") << dict << QStringLiteral("Lions") << NoError; 0730 QTest::newRow("class-enums21") << QStringLiteral("{{ var.Animals.2.key }}") << dict << QStringLiteral("Bears") << NoError; 0731 QTest::newRow("class-enums22") << QStringLiteral("{{ var.Tigers.samba }}") << dict << QString() << NoError; 0732 QTest::newRow("class-enums23") << QStringLiteral( 0733 "{% with var.animals as result %}{{ result.key }},{{ " 0734 "result }},{{ result.scope }}{% endwith %}") 0735 << dict << QStringLiteral("Tigers,1,OtherClass") << NoError; 0736 QTest::newRow("class-enums24") << QStringLiteral( 0737 "{% with var.Animals.2 as result %}{{ result.key }},{{ " 0738 "result }},{{ result.scope }}{% endwith %}") 0739 << dict << QStringLiteral("Bears,2,OtherClass") << NoError; 0740 QTest::newRow("class-enums25") << QStringLiteral( 0741 "{% with var.Bears as result %}{{ result.key }},{{ " 0742 "result }},{{ result.scope }}{% endwith %}") 0743 << dict << QStringLiteral("Bears,2,OtherClass") << NoError; 0744 QTest::newRow("class-enums26") << QStringLiteral( 0745 "{% with var.Animals as result %}{{ result.0.key }},{{ " 0746 "result.1.key }},{{ result.2.key }}{% endwith %}") 0747 << dict << QStringLiteral("Lions,Tigers,Bears") << NoError; 0748 0749 dict.clear(); 0750 0751 auto someClass = new SomeClass(this); 0752 dict.insert(QStringLiteral("var"), QVariant::fromValue(someClass)); 0753 0754 QTest::newRow("class-enums27") << QStringLiteral("{{ var.Employee }}") << dict << QStringLiteral("0") << NoError; 0755 QTest::newRow("class-enums28") << QStringLiteral("{{ var.Employer }}") << dict << QStringLiteral("1") << NoError; 0756 QTest::newRow("class-enums29") << QStringLiteral("{{ var.Manager }}") << dict << QStringLiteral("2") << NoError; 0757 QTest::newRow("class-enums30") << QStringLiteral("{{ var.Voter }}") << dict << QStringLiteral("2") << NoError; 0758 QTest::newRow("class-enums31") << QStringLiteral("{{ var.Consumer }}") << dict << QStringLiteral("4") << NoError; 0759 QTest::newRow("class-enums32") << QStringLiteral("{{ var.Citizen }}") << dict << QStringLiteral("8") << NoError; 0760 QTest::newRow("class-enums33") << QStringLiteral("{{ var.FirstEnum }}") << dict << QString() << NoError; 0761 QTest::newRow("class-enums34") << QStringLiteral("{{ var.SecondEnum }}") << dict << QString() << NoError; 0762 0763 QTest::newRow("class-enums35") << QString::fromLatin1( 0764 "{% with var.SecondEnum as result %}" 0765 "{{ result.0 }},{{ result.1 }},{{ result.2 }}," 0766 "{{ result.0.key }},{{ result.1.key }},{{ result.2.key }}," 0767 "{{ result }},{{ result.scope }}" 0768 "{% endwith %}") << dict << QStringLiteral("2,4,8,Voter,Consumer,Citizen,,SomeClass") 0769 << NoError; 0770 0771 QTest::newRow("class-enums36") << QStringLiteral("{% ifequal var.Employee 2 %}{% endifequal %}") << dict << QString() << NoError; 0772 0773 dict.insert(QStringLiteral("var"), QVariant::fromValue(otherClass)); 0774 0775 QTest::newRow("enums-loops01") << QString::fromLatin1( 0776 "{% for enum in var.Animals %}{% ifequal enum var.Tigers %}" 0777 "<b>{{ enum.key }}</b>{% else %}{{ enum.key }}{% endifequal %}," 0778 "{% empty %}No content{% endfor %}") 0779 << dict << QStringLiteral("Lions,<b>Tigers</b>,Bears,") << NoError; 0780 0781 QTest::newRow("enums-loops02") << QString::fromLatin1( 0782 "{% for enum in var.Tigers %}" 0783 "{% ifequal enum result %}<b>{{ enum.key }}</b>" 0784 "{% else %}{{ enum.key }}{% endifequal %}," 0785 "{% empty %}No content" 0786 "{% endfor %}") << dict << QStringLiteral("No content") 0787 << NoError; 0788 0789 QTest::newRow("enums-loops03") << QString::fromLatin1( 0790 "{% with var.animals as result %}" 0791 "{% for enum in var.Animals %}" 0792 "{% ifequal enum result %}<b>{{ enum.key }}</b>" 0793 "{% else %}{{ enum.key }}{% endifequal %}," 0794 "{% empty %}No content" 0795 "{% endfor %}" 0796 "{% endwith %}") << dict << QStringLiteral("Lions,<b>Tigers</b>,Bears,") 0797 << NoError; 0798 0799 QTest::newRow("enums-keycount01") << QStringLiteral("{{ var.Animals.keyCount }}") << dict << QStringLiteral("3") << NoError; 0800 QTest::newRow("enums-keycount02") << QStringLiteral("{{ var.Lions.keyCount }}") << dict << QStringLiteral("3") << NoError; 0801 QTest::newRow("enums-keycount02") << QStringLiteral("{{ var.animals.keyCount }}") << dict << QStringLiteral("3") << NoError; 0802 0803 auto otherClass2 = new OtherClass(OtherClass::Lions, this); 0804 dict.insert(QStringLiteral("var2"), QVariant::fromValue(otherClass2)); 0805 0806 auto otherClass3 = new OtherClass(this); 0807 dict.insert(QStringLiteral("var3"), QVariant::fromValue(otherClass3)); 0808 0809 QTest::newRow("enums-compare01") << QStringLiteral("{% if var.animals == var3.animals %}true{% else %}false{% endif %}") << dict << QStringLiteral("true") 0810 << NoError; 0811 0812 QTest::newRow("enums-compare02") << QStringLiteral("{% if var.animals == var2.animals %}true{% else %}false{% endif %}") << dict << QStringLiteral("false") 0813 << NoError; 0814 0815 QTest::newRow("enums-compare03") << QStringLiteral("{% if var.animals >= var3.animals %}true{% else %}false{% endif %}") << dict << QStringLiteral("true") 0816 << NoError; 0817 0818 QTest::newRow("enums-compare04") << QStringLiteral("{% if var.animals >= var2.animals %}true{% else %}false{% endif %}") << dict << QStringLiteral("true") 0819 << NoError; 0820 0821 QTest::newRow("enums-compare05") << QStringLiteral("{% if var.animals > var3.animals %}true{% else %}false{% endif %}") << dict << QStringLiteral("false") 0822 << NoError; 0823 0824 QTest::newRow("enums-compare06") << QStringLiteral("{% if var.animals > var2.animals %}true{% else %}false{% endif %}") << dict << QStringLiteral("true") 0825 << NoError; 0826 0827 QTest::newRow("enums-compare07") << QStringLiteral("{% if var.animals <= var3.animals %}true{% else %}false{% endif %}") << dict << QStringLiteral("true") 0828 << NoError; 0829 0830 QTest::newRow("enums-compare08") << QStringLiteral("{% if var.animals <= var2.animals %}true{% else %}false{% endif %}") << dict << QStringLiteral("false") 0831 << NoError; 0832 0833 QTest::newRow("enums-compare09") << QStringLiteral("{% if var.animals < var3.animals %}true{% else %}false{% endif %}") << dict << QStringLiteral("false") 0834 << NoError; 0835 0836 QTest::newRow("enums-compare10") << QStringLiteral("{% if var.animals < var2.animals %}true{% else %}false{% endif %}") << dict << QStringLiteral("false") 0837 << NoError; 0838 0839 QTest::newRow("qt-enums01") << QStringLiteral("{{ Qt.AlignRight }}") << dict << QStringLiteral("2") << NoError; 0840 QTest::newRow("qt-enums02") << QStringLiteral("{{ Qt.AlignRight.scope }}") << dict << QStringLiteral("Qt") << NoError; 0841 QTest::newRow("qt-enums03") << QStringLiteral("{{ Qt.AlignRight.name }}") << dict << QStringLiteral("Alignment") << NoError; 0842 QTest::newRow("qt-enums04") << QStringLiteral("{{ Qt.AlignRight.value }}") << dict << QStringLiteral("2") << NoError; 0843 QTest::newRow("qt-enums05") << QStringLiteral("{{ Qt.AlignRight.key }}") << dict << QStringLiteral("AlignRight") << NoError; 0844 QTest::newRow("qt-enums06") << QStringLiteral("{{ Qt.Alignment.2.key }}") << dict << QStringLiteral("AlignRight") << NoError; 0845 QTest::newRow("qt-enums07") << QStringLiteral("{{ Qt.DoesNotExist }}") << dict << QString() << NoError; 0846 QTest::newRow("qt-enums08") << QStringLiteral("{{ Qt }}") << dict << QString() << NoError; 0847 0848 dict.clear(); 0849 0850 GadgetClass gadgetClasss; 0851 dict.insert(QStringLiteral("var"), QVariant::fromValue(gadgetClasss)); 0852 0853 QTest::newRow("gadget-enums01") << QStringLiteral("{{ var.Mike }}") << dict << QStringLiteral("0") << NoError; 0854 QTest::newRow("gadget-enums02") << QStringLiteral("{{ var.Natalie }}") << dict << QStringLiteral("1") << NoError; 0855 QTest::newRow("gadget-enums03") << QStringLiteral("{{ var.Oliver }}") << dict << QStringLiteral("2") << NoError; 0856 QTest::newRow("gadget-enums04") << QStringLiteral("{{ var.Patricia }}") << dict << QString() << NoError; 0857 QTest::newRow("gadget-enums05") << QStringLiteral("{{ var.Natalie.name }}") << dict << QStringLiteral("PersonName") << NoError; 0858 QTest::newRow("gadget-enums06") << QStringLiteral("{{ var.Natalie.scope }}") << dict << QStringLiteral("GadgetClass") << NoError; 0859 QTest::newRow("gadget-enums07") << QStringLiteral("{{ var.Natalie.value }}") << dict << QStringLiteral("1") << NoError; 0860 QTest::newRow("gadget-enums08") << QStringLiteral("{{ var.Natalie.key }}") << dict << QStringLiteral("Natalie") << NoError; 0861 QTest::newRow("gadget-enums09") << QStringLiteral("{{ var.personName }}") << dict << QStringLiteral("2") << NoError; 0862 QTest::newRow("gadget-enums10") << QStringLiteral("{{ var.personName.name }}") << dict << QStringLiteral("PersonName") << NoError; 0863 QTest::newRow("gadget-enums11") << QStringLiteral("{{ var.personName.scope }}") << dict << QStringLiteral("GadgetClass") << NoError; 0864 QTest::newRow("gadget-enums12") << QStringLiteral("{{ var.personName.value }}") << dict << QStringLiteral("2") << NoError; 0865 QTest::newRow("gadget-enums13") << QStringLiteral("{{ var.personName.key }}") << dict << QStringLiteral("Oliver") << NoError; 0866 QTest::newRow("gadget-enums14") << QStringLiteral("{{ var.PersonName.0 }}") << dict << QStringLiteral("0") << NoError; 0867 QTest::newRow("gadget-enums15") << QStringLiteral("{{ var.PersonName.2 }}") << dict << QStringLiteral("2") << NoError; 0868 QTest::newRow("gadget-enums16") << QStringLiteral("{{ var.PersonName.3 }}") << dict << QString() << NoError; 0869 QTest::newRow("gadget-enums17") << QStringLiteral("{{ var.PersonName.0.name }}") << dict << QStringLiteral("PersonName") << NoError; 0870 QTest::newRow("gadget-enums18") << QStringLiteral("{{ var.PersonName.0.scope }}") << dict << QStringLiteral("GadgetClass") << NoError; 0871 QTest::newRow("gadget-enums19") << QStringLiteral("{{ var.PersonName.0.value }}") << dict << QStringLiteral("0") << NoError; 0872 QTest::newRow("gadget-enums20") << QStringLiteral("{{ var.PersonName.0.key }}") << dict << QStringLiteral("Mike") << NoError; 0873 QTest::newRow("gadget-enums21") << QStringLiteral("{{ var.PersonName.2.key }}") << dict << QStringLiteral("Oliver") << NoError; 0874 QTest::newRow("gadget-enums22") << QStringLiteral("{{ var.PersonName.samba }}") << dict << QString() << NoError; 0875 QTest::newRow("gadget-enums23") << QStringLiteral( 0876 "{% with var.personName as result %}{{ result.key }},{{ " 0877 "result }},{{ result.scope }}{% endwith %}") 0878 << dict << QStringLiteral("Oliver,2,GadgetClass") << NoError; 0879 QTest::newRow("gadget-enums24") << QStringLiteral( 0880 "{% with var.PersonName.2 as result %}{{ result.key }},{{ " 0881 "result }},{{ result.scope }}{% endwith %}") 0882 << dict << QStringLiteral("Oliver,2,GadgetClass") << NoError; 0883 QTest::newRow("gadget-enums25") << QStringLiteral( 0884 "{% with var.Oliver as result %}{{ result.key }},{{ " 0885 "result }},{{ result.scope }}{% endwith %}") 0886 << dict << QStringLiteral("Oliver,2,GadgetClass") << NoError; 0887 QTest::newRow("gadget-enums26") << QStringLiteral( 0888 "{% with var.PersonName as result %}{{ result.0.key }},{{ " 0889 "result.1.key }},{{ result.2.key }}{% endwith %}") 0890 << dict << QStringLiteral("Mike,Natalie,Oliver") << NoError; 0891 0892 QTest::newRow("gadget-enums-loops01") << QString::fromLatin1( 0893 "{% for enum in var.PersonName %}{% ifequal enum var.Natalie %}" 0894 "<b>{{ enum.key }}</b>{% else %}{{ enum.key }}{% endifequal %}," 0895 "{% empty %}No content{% endfor %}") 0896 << dict << QStringLiteral("Mike,<b>Natalie</b>,Oliver,") << NoError; 0897 0898 QTest::newRow("gadget-enums-loops02") << QString::fromLatin1( 0899 "{% for enum in var.Tigers %}" 0900 "{% ifequal enum result %}<b>{{ enum.key }}</b>" 0901 "{% else %}{{ enum.key }}{% endifequal %}," 0902 "{% empty %}No content" 0903 "{% endfor %}") << dict << QStringLiteral("No content") 0904 << NoError; 0905 0906 QTest::newRow("gadget-enums-loops03") << QString::fromLatin1( 0907 "{% with var.personName as result %}" 0908 "{% for enum in var.PersonName %}" 0909 "{% ifequal enum result %}<b>{{ enum.key }}</b>" 0910 "{% else %}{{ enum.key }}{% endifequal %}," 0911 "{% empty %}No content" 0912 "{% endfor %}" 0913 "{% endwith %}") << dict << QStringLiteral("Mike,Natalie,<b>Oliver</b>,") 0914 << NoError; 0915 0916 QTest::newRow("gadget-enums-keycount01") << QStringLiteral("{{ var.PersonName.keyCount }}") << dict << QStringLiteral("3") << NoError; 0917 QTest::newRow("gadget-enums-keycount02") << QStringLiteral("{{ var.personName.keyCount }}") << dict << QStringLiteral("3") << NoError; 0918 0919 GadgetClass gadgetClass2(GadgetClass::Natalie); 0920 dict.insert(QStringLiteral("var2"), QVariant::fromValue(gadgetClass2)); 0921 0922 GadgetClass gadgetClass3; 0923 dict.insert(QStringLiteral("var3"), QVariant::fromValue(gadgetClass3)); 0924 0925 QTest::newRow("gadget-enums-compare01") << QStringLiteral( 0926 "{% if var.personName == var3.personName %}true{% else " 0927 "%}false{% endif %}") << dict << QStringLiteral("true") 0928 << NoError; 0929 0930 QTest::newRow("gadget-enums-compare02") << QStringLiteral( 0931 "{% if var.personName == var2.personName %}true{% else " 0932 "%}false{% endif %}") << dict << QStringLiteral("false") 0933 << NoError; 0934 0935 QTest::newRow("gadget-enums-compare03") << QStringLiteral( 0936 "{% if var.personName >= var3.personName %}true{% else " 0937 "%}false{% endif %}") << dict << QStringLiteral("true") 0938 << NoError; 0939 0940 QTest::newRow("gadget-enums-compare04") << QStringLiteral( 0941 "{% if var.personName >= var2.personName %}true{% else " 0942 "%}false{% endif %}") << dict << QStringLiteral("true") 0943 << NoError; 0944 0945 QTest::newRow("gadget-enums-compare05") << QStringLiteral("{% if var.personName > var3.personName %}true{% else %}false{% endif %}") << dict 0946 << QStringLiteral("false") << NoError; 0947 0948 QTest::newRow("gadget-enums-compare06") << QStringLiteral("{% if var.personName > var2.personName %}true{% else %}false{% endif %}") << dict 0949 << QStringLiteral("true") << NoError; 0950 0951 QTest::newRow("gadget-enums-compare07") << QStringLiteral( 0952 "{% if var.personName <= var3.personName %}true{% else " 0953 "%}false{% endif %}") << dict << QStringLiteral("true") 0954 << NoError; 0955 0956 QTest::newRow("gadget-enums-compare08") << QStringLiteral( 0957 "{% if var.personName <= var2.personName %}true{% else " 0958 "%}false{% endif %}") << dict << QStringLiteral("false") 0959 << NoError; 0960 0961 QTest::newRow("gadget-enums-compare09") << QStringLiteral("{% if var.personName < var3.personName %}true{% else %}false{% endif %}") << dict 0962 << QStringLiteral("false") << NoError; 0963 0964 QTest::newRow("gadget-enums-compare10") << QStringLiteral("{% if var.personName < var2.personName %}true{% else %}false{% endif %}") << dict 0965 << QStringLiteral("false") << NoError; 0966 } 0967 0968 void TestBuiltinSyntax::testListIndex_data() 0969 { 0970 QTest::addColumn<QString>("input"); 0971 QTest::addColumn<Dict>("dict"); 0972 QTest::addColumn<QString>("output"); 0973 QTest::addColumn<KTextTemplate::Error>("error"); 0974 0975 Dict dict; 0976 0977 QVariantList l{QStringLiteral("first item"), QStringLiteral("second item")}; 0978 0979 dict.insert(QStringLiteral("var"), l); 0980 0981 // List-index syntax allows a template to access a certain item of a 0982 // subscriptable object. 0983 QTest::newRow("list-index01") << QStringLiteral("{{ var.1 }}") << dict << QStringLiteral("second item") << NoError; 0984 // Fail silently when the list index is out of range. 0985 QTest::newRow("list-index02") << QStringLiteral("{{ var.5 }}") << dict << QString() << NoError; 0986 0987 dict.clear(); 0988 dict.insert(QStringLiteral("var"), QVariant()); 0989 0990 // Fail silently when the variable is not a subscriptable object. 0991 QTest::newRow("list-index03") << QStringLiteral("{{ var.1 }}") << dict << QString() << NoError; 0992 0993 dict.clear(); 0994 dict.insert(QStringLiteral("var"), QVariantHash()); 0995 // Fail silently when variable is a dict without the specified key. 0996 QTest::newRow("list-index04") << QStringLiteral("{{ var.1 }}") << dict << QString() << NoError; 0997 0998 dict.clear(); 0999 1000 QVariantHash hash; 1001 hash.insert(QStringLiteral("1"), QStringLiteral("hello")); 1002 dict.insert(QStringLiteral("var"), hash); 1003 // Dictionary lookup wins out when dict's key is a string. 1004 QTest::newRow("list-index05") << QStringLiteral("{{ var.1 }}") << dict << QStringLiteral("hello") << NoError; 1005 1006 // QVariantHash can only use strings as keys, so list-index06 and 1007 // list-index07 1008 // are not valid. 1009 1010 dict.clear(); 1011 1012 QStringList sl; 1013 sl.append(QStringLiteral("hello")); 1014 sl.append(QStringLiteral("world")); 1015 dict.insert(QStringLiteral("var"), sl); 1016 // QStringList lookup 1017 QTest::newRow("list-index08") << QStringLiteral("{{ var.0 }}, {{ var.1 }}!") << dict << QStringLiteral("hello, world!") << NoError; 1018 } 1019 1020 void TestBuiltinSyntax::testFilterSyntax_data() 1021 { 1022 QTest::addColumn<QString>("input"); 1023 QTest::addColumn<Dict>("dict"); 1024 QTest::addColumn<QString>("output"); 1025 QTest::addColumn<KTextTemplate::Error>("error"); 1026 1027 Dict dict; 1028 1029 // Basic filter usage 1030 dict.insert(QStringLiteral("var"), QStringLiteral("Django is the greatest!")); 1031 QTest::newRow("filter-syntax01") << QStringLiteral("{{ var|upper }}") << dict << QStringLiteral("DJANGO IS THE GREATEST!") << NoError; 1032 1033 // Chained filters 1034 QTest::newRow("filter-syntax02") << QStringLiteral("{{ var|upper|lower }}") << dict << QStringLiteral("django is the greatest!") << NoError; 1035 1036 // Raise TemplateSyntaxError for space between a variable and filter pipe 1037 dict.clear(); 1038 QTest::newRow("filter-syntax03") << QStringLiteral("{{ var |upper }}") << dict << QString() << TagSyntaxError; 1039 1040 // Raise TemplateSyntaxError for space after a filter pipe 1041 QTest::newRow("filter-syntax04") << QStringLiteral("{{ var| upper }}") << dict << QString() << TagSyntaxError; 1042 1043 // Raise TemplateSyntaxError for a nonexistent filter 1044 QTest::newRow("filter-syntax05") << QStringLiteral("{{ var|does_not_exist }}") << dict << QString() << UnknownFilterError; 1045 1046 // Raise TemplateSyntaxError when trying to access a filter containing an 1047 // illegal character 1048 QTest::newRow("filter-syntax06") << QStringLiteral("{{ var|fil(ter) }}") << dict << QString() << UnknownFilterError; 1049 1050 // Raise TemplateSyntaxError for invalid block tags 1051 QTest::newRow("filter-syntax07") << QStringLiteral("{% nothing_to_see_here %}") << dict << QString() << InvalidBlockTagError; 1052 // Raise TemplateSyntaxError for empty block tags 1053 QTest::newRow("filter-syntax08") << QStringLiteral("{% %}") << dict << QString() << EmptyBlockTagError; 1054 1055 // Chained filters, with an argument to the first one 1056 dict.insert(QStringLiteral("var"), QStringLiteral("<b><i>Yes</i></b>")); 1057 QTest::newRow("filter-syntax09") << "{{ var|removetags:\"b i\"|upper|lower }}" << dict << QStringLiteral("yes") << NoError; 1058 // Literal string as argument is always "safe" from auto-escaping.. 1059 dict.clear(); 1060 dict.insert(QStringLiteral("var"), QVariant()); 1061 QTest::newRow("filter-syntax10") << R"({{ var|default_if_none:" endquote\" hah" }})" << dict << " endquote\" hah" << NoError; 1062 // Variable as argument 1063 dict.insert(QStringLiteral("var2"), QStringLiteral("happy")); 1064 QTest::newRow("filter-syntax11") << QStringLiteral("{{ var|default_if_none:var2 }}") << dict << QStringLiteral("happy") << NoError; 1065 // Default argument testing 1066 dict.clear(); 1067 dict.insert(QStringLiteral("var"), true); 1068 QTest::newRow("filter-syntax12") << "{{ var|yesno:\"yup,nup,mup\" }} {{ var|yesno }}" << dict << QStringLiteral("yup yes") << NoError; 1069 1070 // Fail silently for methods that raise an exception with a 1071 // "silent_variable_failure" attribute 1072 // dict.clear(); 1073 // QObject *someClass = new SomeClass(this); 1074 // dict.insert( QStringLiteral("var"), QVariant::fromValue(someClass)); 1075 // QTest::newRow("filter-syntax13") << QString::fromLatin1( "1{{ 1076 // var.method3 1077 // }}2" ) << dict << QString::fromLatin1( "12" ) << NoError; 1078 // // In methods that raise an exception without a 1079 // // "silent_variable_attribute" set to True, the exception propagates 1080 // // #C# SomeOtherException) 1081 // QTest::newRow("filter-syntax14") << QString::fromLatin1( "var" ) << 1082 // dict 1083 // << QString() << TagSyntaxError; 1084 1085 // Escaped backslash in argument 1086 dict.clear(); 1087 dict.insert(QStringLiteral("var"), QVariant()); 1088 QTest::newRow("filter-syntax15") << R"({{ var|default_if_none:"foo\bar" }})" << dict << "foo\\bar" << NoError; 1089 // Escaped backslash using known escape char 1090 QTest::newRow("filter-syntax16") << R"({{ var|default_if_none:"foo\now" }})" << dict << "foo\\now" << NoError; 1091 // Empty strings can be passed as arguments to filters 1092 dict.clear(); 1093 dict.insert(QStringLiteral("var"), QVariantList{QStringLiteral("a"), QStringLiteral("b"), QStringLiteral("c")}); 1094 QTest::newRow("filter-syntax17") << "{{ var|join:\"\" }}" << dict << QStringLiteral("abc") << NoError; 1095 1096 // Make sure that any unicode strings are converted to bytestrings 1097 // in the final output. 1098 // FAIL'filter-syntax18': (r'{{ var }}', {'var': UTF8Class()}, 1099 // u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111'), 1100 1101 // Numbers as filter arguments should work 1102 dict.clear(); 1103 dict.insert(QStringLiteral("var"), QStringLiteral("hello world")); 1104 QTest::newRow("filter-syntax19") << QStringLiteral("{{ var|truncatewords:1 }}") << dict << QStringLiteral("hello ...") << NoError; 1105 // filters should accept empty string constants 1106 dict.clear(); 1107 QTest::newRow("filter-syntax20") << R"({{ ""|default_if_none:"was none" }})" << dict << QString() << NoError; 1108 1109 QTest::newRow("filter-syntax21") << "{{ \"\"|default_if_none:|truncatewords }}" << dict << QString() << EmptyVariableError; 1110 } 1111 1112 void TestBuiltinSyntax::testCommentSyntax_data() 1113 { 1114 QTest::addColumn<QString>("input"); 1115 QTest::addColumn<Dict>("dict"); 1116 QTest::addColumn<QString>("output"); 1117 QTest::addColumn<KTextTemplate::Error>("error"); 1118 1119 Dict dict; 1120 1121 QTest::newRow("comment-syntax01") << QStringLiteral("{# this is hidden #}hello") << dict << QStringLiteral("hello") << NoError; 1122 QTest::newRow("comment-syntax02") << QStringLiteral("{# this is hidden #}hello{# foo #}") << dict << QStringLiteral("hello") << NoError; 1123 // Comments can contain invalid stuff. 1124 QTest::newRow("comment-syntax03") << QStringLiteral("foo{# {% if %} #}") << dict << QStringLiteral("foo") << NoError; 1125 QTest::newRow("comment-syntax04") << QStringLiteral("foo{# {% endblock %} #}") << dict << QStringLiteral("foo") << NoError; 1126 QTest::newRow("comment-syntax05") << QStringLiteral("foo{# {% somerandomtag %} #}") << dict << QStringLiteral("foo") << NoError; 1127 QTest::newRow("comment-syntax06") << QStringLiteral("foo{# {% #}") << dict << QStringLiteral("foo") << NoError; 1128 QTest::newRow("comment-syntax07") << QStringLiteral("foo{# %} #}") << dict << QStringLiteral("foo") << NoError; 1129 QTest::newRow("comment-syntax08") << QStringLiteral("foo{# %} #}bar") << dict << QStringLiteral("foobar") << NoError; 1130 QTest::newRow("comment-syntax09") << QStringLiteral("foo{# {{ #}") << dict << QStringLiteral("foo") << NoError; 1131 QTest::newRow("comment-syntax10") << QStringLiteral("foo{# }} #}") << dict << QStringLiteral("foo") << NoError; 1132 QTest::newRow("comment-syntax11") << QStringLiteral("foo{# { #}") << dict << QStringLiteral("foo") << NoError; 1133 QTest::newRow("comment-syntax12") << QStringLiteral("foo{# } #}") << dict << QStringLiteral("foo") << NoError; 1134 } 1135 1136 void TestBuiltinSyntax::testMultiline_data() 1137 { 1138 QTest::addColumn<QString>("input"); 1139 QTest::addColumn<Dict>("dict"); 1140 QTest::addColumn<QString>("output"); 1141 QTest::addColumn<KTextTemplate::Error>("error"); 1142 1143 Dict dict; 1144 1145 QTest::newRow("multiline01") << "Hello,\nboys.\nHow\nare\nyou\ngentlemen?" << dict << "Hello,\nboys.\nHow\nare\nyou\ngentlemen?" << NoError; 1146 } 1147 1148 void TestBuiltinSyntax::testEscaping_data() 1149 { 1150 QTest::addColumn<QString>("input"); 1151 QTest::addColumn<Dict>("dict"); 1152 QTest::addColumn<QString>("output"); 1153 QTest::addColumn<KTextTemplate::Error>("error"); 1154 1155 Dict dict; 1156 1157 // html escaping is not to be confused with for example url escaping. 1158 dict.insert(QStringLiteral("var"), QStringLiteral("< > & \" \' # = % $")); 1159 QTest::newRow("escape01") << QStringLiteral("{{ var }}") << dict << "< > & " ' # = % $" << NoError; 1160 1161 dict.clear(); 1162 dict.insert(QStringLiteral("var"), QStringLiteral("this & that")); 1163 QTest::newRow("escape02") << QStringLiteral("{{ var }}") << dict << QStringLiteral("this & that") << NoError; 1164 1165 // Strings are compared unescaped. 1166 QTest::newRow("escape03") << "{% ifequal var \"this & that\" %}yes{% endifequal %}" << dict << QStringLiteral("yes") << NoError; 1167 1168 // Arguments to filters are 'safe' and manipulate their input unescaped. 1169 QTest::newRow("escape04") << "{{ var|cut:\"&\" }}" << dict << QStringLiteral("this that") << NoError; 1170 1171 dict.insert(QStringLiteral("varList"), QVariantList{QStringLiteral("Tom"), QStringLiteral("Dick"), QStringLiteral("Harry")}); 1172 QTest::newRow("escape05") << "{{ varList|join:\" & \" }}" << dict << QStringLiteral("Tom & Dick & Harry") << NoError; 1173 1174 // Unlike variable args. 1175 dict.insert(QStringLiteral("amp"), QStringLiteral(" & ")); 1176 QTest::newRow("escape06") << QStringLiteral("{{ varList|join:amp }}") << dict << QStringLiteral("Tom & Dick & Harry") << NoError; 1177 1178 // Literal strings are safe. 1179 QTest::newRow("escape07") << "{{ \"this & that\" }}" << dict << QStringLiteral("this & that") << NoError; 1180 1181 // Iterating outputs safe characters. 1182 dict.clear(); 1183 QVariantList list{QStringLiteral("K"), QStringLiteral("&"), QStringLiteral("R")}; 1184 dict.insert(QStringLiteral("list"), list); 1185 QTest::newRow("escape08") << QStringLiteral("{% for letter in list %}{{ letter }},{% endfor %}") << dict << QStringLiteral("K,&,R,") << NoError; 1186 1187 dict.clear(); 1188 // escape requirement survives lookup. 1189 QVariantHash hash; 1190 hash.insert(QStringLiteral("key"), QStringLiteral("this & that")); 1191 dict.insert(QStringLiteral("var"), hash); 1192 QTest::newRow("escape09") << QStringLiteral("{{ var.key }}") << dict << QStringLiteral("this & that") << NoError; 1193 1194 dict.clear(); 1195 } 1196 1197 void TestBuiltinSyntax::testMultipleStates() 1198 { 1199 auto engine1 = getEngine(); 1200 1201 auto loader1 = QSharedPointer<InMemoryTemplateLoader>(new InMemoryTemplateLoader()); 1202 1203 loader1->setTemplate(QStringLiteral("template1"), QStringLiteral("Template 1")); 1204 engine1->addTemplateLoader(loader1); 1205 1206 auto t1 = engine1->newTemplate(QStringLiteral("{% include \"template1\" %}"), QStringLiteral("\"template1\"")); 1207 1208 auto engine2 = getEngine(); 1209 1210 auto loader2 = QSharedPointer<InMemoryTemplateLoader>(new InMemoryTemplateLoader()); 1211 1212 loader2->setTemplate(QStringLiteral("template2"), QStringLiteral("Template 2")); 1213 1214 engine2->addTemplateLoader(loader2); 1215 1216 auto t2 = engine2->newTemplate(QStringLiteral("{% include \"template2\" %}"), QStringLiteral("\"template2\"")); 1217 1218 auto engine3 = getEngine(); 1219 1220 auto loader3 = QSharedPointer<InMemoryTemplateLoader>(new InMemoryTemplateLoader()); 1221 1222 loader3->setTemplate(QStringLiteral("template3"), QStringLiteral("Template 3")); 1223 1224 engine3->addTemplateLoader(loader3); 1225 1226 auto t3 = engine3->newTemplate(QStringLiteral("{% include var %}"), QStringLiteral("var")); 1227 1228 QVariantHash h; 1229 h.insert(QStringLiteral("var"), QStringLiteral("template3")); 1230 Context c(h); 1231 t1->render(&c); 1232 1233 auto expected1 = QStringLiteral("Template 1"); 1234 auto expected2 = QStringLiteral("Template 2"); 1235 auto expected3 = QStringLiteral("Template 3"); 1236 QCOMPARE(t1->render(&c), expected1); 1237 QCOMPARE(t2->render(&c), expected2); 1238 QCOMPARE(t3->render(&c), expected3); 1239 } 1240 1241 void TestBuiltinSyntax::testAlternativeEscaping() 1242 { 1243 auto engine1 = getEngine(); 1244 1245 auto t1 = engine1->newTemplate(QStringLiteral("{{ var }} {% spaceless %}{{ var }}{% endspaceless %}"), QStringLiteral("\"template1\"")); 1246 1247 auto input = QStringLiteral("< > \r\n & \" \' # = % $"); 1248 1249 QVariantHash h; 1250 h.insert(QStringLiteral("var"), input); 1251 Context c(h); 1252 1253 QString output; 1254 QTextStream ts(&output); 1255 1256 NoEscapeOutputStream noEscapeOs(&ts); 1257 1258 t1->render(&noEscapeOs, &c); 1259 1260 QCOMPARE(output, QString(input + QLatin1String(" ") + input)); 1261 output.clear(); 1262 1263 JSOutputStream jsOs(&ts); 1264 1265 t1->render(&jsOs, &c); 1266 1267 QString jsOutput(QStringLiteral("\\u003C \\u003E \\u000D\\u000A \\u0026 \\u0022 \\u0027 # \\u003D % $")); 1268 1269 jsOutput = jsOutput + QLatin1String(" ") + jsOutput; 1270 1271 QCOMPARE(output, jsOutput); 1272 } 1273 1274 void TestBuiltinSyntax::testTemplatePathSafety_data() 1275 { 1276 QTest::addColumn<QString>("inputPath"); 1277 QTest::addColumn<QString>("output"); 1278 1279 QTest::newRow("template-path-safety01") << QStringLiteral("visible_file") << QStringLiteral("visible_file"); 1280 QTest::newRow("template-path-safety02") << QStringLiteral("../invisible_file") << QString(); 1281 } 1282 1283 void TestBuiltinSyntax::testTemplatePathSafety() 1284 { 1285 QFETCH(QString, inputPath); 1286 QFETCH(QString, output); 1287 1288 auto loader = new FileSystemTemplateLoader(); 1289 1290 loader->setTemplateDirs({QStringLiteral(".")}); 1291 1292 QFile f(inputPath); 1293 auto opened = f.open(QFile::WriteOnly | QFile::Text); 1294 QVERIFY(opened); 1295 f.write(inputPath.toUtf8()); 1296 f.close(); 1297 1298 auto t = loader->loadByName(inputPath, m_engine); 1299 Context c; 1300 if (output.isEmpty()) 1301 QVERIFY(!t); 1302 else 1303 QCOMPARE(t->render(&c), inputPath); 1304 1305 delete loader; 1306 f.remove(); 1307 } 1308 1309 void TestBuiltinSyntax::testMediaPathSafety_data() 1310 { 1311 QTest::addColumn<QString>("inputPath"); 1312 QTest::addColumn<QString>("output"); 1313 1314 QTest::newRow("media-path-safety01") << QStringLiteral("visible_file") << QStringLiteral("./visible_file"); 1315 QTest::newRow("media-path-safety02") << QStringLiteral("../invisible_file") << QString(); 1316 } 1317 1318 void TestBuiltinSyntax::testMediaPathSafety() 1319 { 1320 QFETCH(QString, inputPath); 1321 QFETCH(QString, output); 1322 1323 auto loader = new FileSystemTemplateLoader(); 1324 1325 loader->setTemplateDirs({QStringLiteral(".")}); 1326 1327 QFile f(inputPath); 1328 auto opened = f.open(QFile::WriteOnly | QFile::Text); 1329 QVERIFY(opened); 1330 f.write(inputPath.toUtf8()); 1331 f.close(); 1332 1333 auto uri = loader->getMediaUri(inputPath); 1334 if (output.isEmpty()) 1335 QVERIFY(uri.second.isEmpty()); 1336 else 1337 QCOMPARE(QFileInfo(uri.first + uri.second).absoluteFilePath(), QFileInfo(output).absoluteFilePath()); 1338 1339 delete loader; 1340 f.remove(); 1341 } 1342 1343 void TestBuiltinSyntax::testTypeAccessorsUnordered() 1344 { 1345 QFETCH(QString, input); 1346 QFETCH(Dict, dict); 1347 QFETCH(QStringList, output); 1348 QFETCH(KTextTemplate::Error, error); 1349 1350 auto t = m_engine->newTemplate(input, QLatin1String(QTest::currentDataTag())); 1351 1352 Context context(dict); 1353 1354 auto result = t->render(&context); 1355 if (t->error() != NoError) { 1356 if (t->error() != error) 1357 qDebug() << t->errorString(); 1358 QCOMPARE(t->error(), error); 1359 return; 1360 } 1361 1362 QCOMPARE(t->error(), NoError); 1363 1364 // Didn't catch any errors, so make sure I didn't expect any. 1365 QCOMPARE(NoError, error); 1366 1367 for (const QString &s : output) { 1368 QVERIFY(result.contains(s)); 1369 } 1370 1371 QCOMPARE(result.length(), output.join(QString()).length()); 1372 } 1373 1374 void TestBuiltinSyntax::testTypeAccessorsUnordered_data() 1375 { 1376 QTest::addColumn<QString>("input"); 1377 QTest::addColumn<Dict>("dict"); 1378 QTest::addColumn<QStringList>("output"); 1379 QTest::addColumn<KTextTemplate::Error>("error"); 1380 1381 Dict dict; 1382 1383 QVariantHash itemsHash; 1384 itemsHash.insert(QStringLiteral("one"), 1); 1385 itemsHash.insert(QStringLiteral("two"), 2); 1386 itemsHash.insert(QStringLiteral("three"), 3); 1387 1388 dict.insert(QStringLiteral("hash"), itemsHash); 1389 1390 QTest::newRow("type-accessors-hash-unordered01") << QStringLiteral( 1391 "{% for key,value in hash.items %}{{ key }}:{{ value " 1392 "}};{% endfor %}") << dict << QStringList{QStringLiteral("one:1;"), QStringLiteral("two:2;"), QStringLiteral("three:3;")} 1393 << NoError; 1394 QTest::newRow("type-accessors-hash-unordered02") << QStringLiteral("{% for key in hash.keys %}{{ key }};{% endfor %}") << dict 1395 << QStringList{QStringLiteral("one;"), QStringLiteral("two;"), QStringLiteral("three;")} << NoError; 1396 QTest::newRow("type-accessors-hash-unordered03") << QStringLiteral("{% for value in hash.values %}{{ value }};{% endfor %}") << dict 1397 << QStringList{QStringLiteral("1;"), QStringLiteral("2;"), QStringLiteral("3;")} << NoError; 1398 } 1399 1400 void TestBuiltinSyntax::testTypeAccessors_data() 1401 { 1402 QTest::addColumn<QString>("input"); 1403 QTest::addColumn<Dict>("dict"); 1404 QTest::addColumn<QString>("output"); 1405 QTest::addColumn<KTextTemplate::Error>("error"); 1406 1407 Dict dict; 1408 1409 QVariantHash itemsHash; 1410 itemsHash.insert(QStringLiteral("one"), 1); 1411 itemsHash.insert(QStringLiteral("two"), 2); 1412 itemsHash.insert(QStringLiteral("three"), 3); 1413 1414 dict.insert(QStringLiteral("hash"), itemsHash); 1415 1416 QTest::newRow("type-accessors-hash01") << QStringLiteral("{{ hash.items|length }}") << dict << QStringLiteral("3") << NoError; 1417 QTest::newRow("type-accessors-hash02") << QStringLiteral("{{ hash.keys|length }}") << dict << QStringLiteral("3") << NoError; 1418 QTest::newRow("type-accessors-hash03") << QStringLiteral("{{ hash.values|length }}") << dict << QStringLiteral("3") << NoError; 1419 1420 dict.clear(); 1421 dict.insert(QStringLiteral("str1"), QStringLiteral("my string")); 1422 dict.insert(QStringLiteral("str2"), QStringLiteral("mystring")); 1423 1424 QTest::newRow("type-accessors-string01") << QStringLiteral("{{ str1.capitalize }}") << dict << QStringLiteral("My string") << NoError; 1425 QTest::newRow("type-accessors-string02") << QStringLiteral("{{ str2.capitalize }}") << dict << QStringLiteral("Mystring") << NoError; 1426 1427 dict.clear(); 1428 dict.insert(QStringLiteral("str1"), QStringLiteral("de24335fre")); 1429 dict.insert(QStringLiteral("str2"), QStringLiteral("de435f3.-5r")); 1430 1431 QTest::newRow("type-accessors-string03") << QStringLiteral("{{ str1.isalnum }}") << dict << QStringLiteral("True") << NoError; 1432 QTest::newRow("type-accessors-string04") << QStringLiteral("{{ str2.isalnum }}") << dict << QStringLiteral("False") << NoError; 1433 1434 dict.clear(); 1435 dict.insert(QStringLiteral("str1"), QStringLiteral("24335")); 1436 dict.insert(QStringLiteral("str2"), QStringLiteral("de435f35r")); 1437 dict.insert(QStringLiteral("str3"), QStringLiteral("de435f3.-5r")); 1438 1439 QTest::newRow("type-accessors-string05") << QStringLiteral("{{ str1.isdigit }}") << dict << QStringLiteral("True") << NoError; 1440 QTest::newRow("type-accessors-string06") << QStringLiteral("{{ str2.isdigit }}") << dict << QStringLiteral("False") << NoError; 1441 QTest::newRow("type-accessors-string07") << QStringLiteral("{{ str3.isdigit }}") << dict << QStringLiteral("False") << NoError; 1442 1443 dict.clear(); 1444 dict.insert(QStringLiteral("str"), QStringLiteral("MyString")); 1445 dict.insert(QStringLiteral("lowerStr"), QStringLiteral("mystring")); 1446 1447 QTest::newRow("type-accessors-string08") << QStringLiteral("{{ str.islower }}") << dict << QStringLiteral("False") << NoError; 1448 QTest::newRow("type-accessors-string09") << QStringLiteral("{{ lowerStr.islower }}") << dict << QStringLiteral("True") << NoError; 1449 1450 dict.clear(); 1451 dict.insert(QStringLiteral("str1"), QStringLiteral(" ")); 1452 dict.insert(QStringLiteral("str2"), QStringLiteral(" r ")); 1453 dict.insert(QStringLiteral("str3"), QStringLiteral(" \t\nr ")); 1454 dict.insert(QStringLiteral("str4"), QStringLiteral(" \t\n ")); 1455 1456 QTest::newRow("type-accessors-string10") << QStringLiteral("{{ str1.isspace }}") << dict << QStringLiteral("True") << NoError; 1457 QTest::newRow("type-accessors-string11") << QStringLiteral("{{ str2.isspace }}") << dict << QStringLiteral("False") << NoError; 1458 QTest::newRow("type-accessors-string12") << QStringLiteral("{{ str3.isspace }}") << dict << QStringLiteral("False") << NoError; 1459 QTest::newRow("type-accessors-string13") << QStringLiteral("{{ str4.isspace }}") << dict << QStringLiteral("True") << NoError; 1460 1461 dict.clear(); 1462 dict.insert(QStringLiteral("str1"), QStringLiteral("My String")); 1463 dict.insert(QStringLiteral("str2"), QStringLiteral("Mystring")); 1464 dict.insert(QStringLiteral("str3"), QStringLiteral("My string")); 1465 dict.insert(QStringLiteral("str4"), QStringLiteral("my string")); 1466 1467 QTest::newRow("type-accessors-string14") << QStringLiteral("{{ str1.istitle }}") << dict << QStringLiteral("True") << NoError; 1468 QTest::newRow("type-accessors-string15") << QStringLiteral("{{ str2.istitle }}") << dict << QStringLiteral("True") << NoError; 1469 QTest::newRow("type-accessors-string16") << QStringLiteral("{{ str3.istitle }}") << dict << QStringLiteral("False") << NoError; 1470 QTest::newRow("type-accessors-string17") << QStringLiteral("{{ str4.istitle }}") << dict << QStringLiteral("False") << NoError; 1471 1472 dict.clear(); 1473 dict.insert(QStringLiteral("str"), QStringLiteral("MyString")); 1474 dict.insert(QStringLiteral("upperStr"), QStringLiteral("MYSTRING")); 1475 1476 QTest::newRow("type-accessors-string18") << QStringLiteral("{{ str.isupper }}") << dict << QStringLiteral("False") << NoError; 1477 QTest::newRow("type-accessors-string19") << QStringLiteral("{{ upperStr.isupper }}") << dict << QStringLiteral("True") << NoError; 1478 1479 dict.clear(); 1480 dict.insert(QStringLiteral("str1"), QStringLiteral("My String")); 1481 dict.insert(QStringLiteral("str2"), QStringLiteral("MYSTRING")); 1482 dict.insert(QStringLiteral("str3"), QStringLiteral("MY STRING")); 1483 1484 QTest::newRow("type-accessors-string20") << QStringLiteral("{{ str1.lower }}") << dict << QStringLiteral("my string") << NoError; 1485 QTest::newRow("type-accessors-string21") << QStringLiteral("{{ str2.lower }}") << dict << QStringLiteral("mystring") << NoError; 1486 QTest::newRow("type-accessors-string22") << QStringLiteral("{{ str3.lower }}") << dict << QStringLiteral("my string") << NoError; 1487 1488 dict.clear(); 1489 dict.insert(QStringLiteral("str"), QStringLiteral("one\ntwo three\nfour")); 1490 1491 QTest::newRow("type-accessors-string23") << QStringLiteral("{% for line in str.splitlines %}{{ line }};{% endfor %}") << dict 1492 << QStringLiteral("one;two three;four;") << NoError; 1493 1494 dict.clear(); 1495 dict.insert(QStringLiteral("str1"), QStringLiteral(" one")); 1496 dict.insert(QStringLiteral("str2"), QStringLiteral(" one ")); 1497 dict.insert(QStringLiteral("str3"), QStringLiteral("one ")); 1498 dict.insert(QStringLiteral("str4"), QStringLiteral(" ")); 1499 dict.insert(QStringLiteral("str5"), QStringLiteral("")); 1500 1501 QTest::newRow("type-accessors-string24") << QStringLiteral("{{ str1.strip }}") << dict << QStringLiteral("one") << NoError; 1502 QTest::newRow("type-accessors-string25") << QStringLiteral("{{ str2.strip }}") << dict << QStringLiteral("one") << NoError; 1503 QTest::newRow("type-accessors-string26") << QStringLiteral("{{ str3.strip }}") << dict << QStringLiteral("one") << NoError; 1504 QTest::newRow("type-accessors-string27") << QStringLiteral("{{ str4.strip }}") << dict << QString() << NoError; 1505 QTest::newRow("type-accessors-string28") << QStringLiteral("{{ str5.strip }}") << dict << QString() << NoError; 1506 1507 dict.clear(); 1508 dict.insert(QStringLiteral("str1"), QStringLiteral("My String")); 1509 dict.insert(QStringLiteral("str2"), QStringLiteral("mY sTRING")); 1510 dict.insert(QStringLiteral("str3"), QStringLiteral("My StrInG")); 1511 dict.insert(QStringLiteral("str4"), QStringLiteral("my string")); 1512 dict.insert(QStringLiteral("str5"), QStringLiteral("MY STRING")); 1513 1514 // Yes, this really is a python built-in. 1515 QTest::newRow("type-accessors-string29") << QStringLiteral("{{ str1.swapcase }}") << dict << QStringLiteral("mY sTRING") << NoError; 1516 QTest::newRow("type-accessors-string30") << QStringLiteral("{{ str2.swapcase }}") << dict << QStringLiteral("My String") << NoError; 1517 QTest::newRow("type-accessors-string31") << QStringLiteral("{{ str3.swapcase }}") << dict << QStringLiteral("mY sTRiNg") << NoError; 1518 QTest::newRow("type-accessors-string32") << QStringLiteral("{{ str4.swapcase }}") << dict << QStringLiteral("MY STRING") << NoError; 1519 QTest::newRow("type-accessors-string33") << QStringLiteral("{{ str5.swapcase }}") << dict << QStringLiteral("my string") << NoError; 1520 1521 dict.clear(); 1522 dict.insert(QStringLiteral("str1"), QStringLiteral("My String")); 1523 dict.insert(QStringLiteral("str2"), QStringLiteral("mystring")); 1524 dict.insert(QStringLiteral("str3"), QStringLiteral("my string")); 1525 dict.insert(QStringLiteral("str4"), QStringLiteral("my String")); 1526 dict.insert(QStringLiteral("str5"), QStringLiteral("My string")); 1527 dict.insert(QStringLiteral("str6"), QStringLiteral("123")); 1528 dict.insert(QStringLiteral("str7"), QString()); 1529 1530 QTest::newRow("type-accessors-string34") << QStringLiteral("{{ str1.title }}") << dict << QStringLiteral("My String") << NoError; 1531 QTest::newRow("type-accessors-string35") << QStringLiteral("{{ str2.title }}") << dict << QStringLiteral("Mystring") << NoError; 1532 QTest::newRow("type-accessors-string36") << QStringLiteral("{{ str3.title }}") << dict << QStringLiteral("My String") << NoError; 1533 QTest::newRow("type-accessors-string37") << QStringLiteral("{{ str4.title }}") << dict << QStringLiteral("My String") << NoError; 1534 QTest::newRow("type-accessors-string38") << QStringLiteral("{{ str5.title }}") << dict << QStringLiteral("My String") << NoError; 1535 1536 QTest::newRow("type-accessors-string39") << QStringLiteral("{{ str1.upper }}") << dict << QStringLiteral("MY STRING") << NoError; 1537 QTest::newRow("type-accessors-string40") << QStringLiteral("{{ str2.upper }}") << dict << QStringLiteral("MYSTRING") << NoError; 1538 QTest::newRow("type-accessors-string41") << QStringLiteral("{{ str3.upper }}") << dict << QStringLiteral("MY STRING") << NoError; 1539 QTest::newRow("type-accessors-string42") << QStringLiteral("{{ str3.dne }}") << dict << QString() << NoError; 1540 QTest::newRow("type-accessors-string43") << QStringLiteral("{{ str2.isalpha }}") << dict << QStringLiteral("True") << NoError; 1541 QTest::newRow("type-accessors-string44") << QStringLiteral("{{ str3.isalpha }}") << dict << QStringLiteral("False") << NoError; 1542 QTest::newRow("type-accessors-string45") << QStringLiteral("{{ str6.isalpha }}") << dict << QStringLiteral("False") << NoError; 1543 QTest::newRow("type-accessors-string46") << QStringLiteral("{{ str7.isalpha }}") << dict << QStringLiteral("False") << NoError; 1544 1545 dict.clear(); 1546 1547 #define SON(obj) obj->setObjectName(QStringLiteral(#obj)) 1548 1549 auto obj1 = new QObject(this); 1550 SON(obj1); 1551 auto obj2 = new QObject(this); 1552 SON(obj2); 1553 obj2->setParent(obj1); 1554 auto obj3 = new QObject(this); 1555 obj3->setParent(obj2); 1556 SON(obj3); 1557 auto obj4 = new QObject(this); 1558 obj4->setParent(obj2); 1559 SON(obj4); 1560 1561 dict.insert(QStringLiteral("object"), QVariant::fromValue(obj1)); 1562 1563 QTest::newRow("type-accessors-qobject01") << QStringLiteral("{{ object.objectName }}") << dict << QStringLiteral("obj1") << NoError; 1564 1565 const QLatin1String objectDumper( 1566 "<li>{{ object.objectName }}</li>" 1567 "{% if object.children %}" 1568 "<ul>" 1569 "{% for object in object.children %}" 1570 "{% include 'objectdumper.html' %}" 1571 "{% endfor %}" 1572 "</ul>" 1573 "{% endif %}"); 1574 1575 m_loader->setTemplate(QStringLiteral("objectdumper.html"), objectDumper); 1576 1577 QTest::newRow("type-accessors-qobject02") << QStringLiteral("<ul>{% include 'objectdumper.html' %}</ul>") << dict 1578 << QString::fromLatin1( 1579 "<ul>" 1580 "<li>obj1</li>" 1581 "<ul>" 1582 "<li>obj2</li>" 1583 "<ul>" 1584 "<li>obj3</li>" 1585 "<li>obj4</li>" 1586 "</ul>" 1587 "</ul>" 1588 "</ul>") 1589 << NoError; 1590 } 1591 1592 void TestBuiltinSyntax::testDynamicProperties_data() 1593 { 1594 QTest::addColumn<QString>("input"); 1595 QTest::addColumn<Dict>("dict"); 1596 QTest::addColumn<QString>("output"); 1597 QTest::addColumn<KTextTemplate::Error>("error"); 1598 1599 Dict dict; 1600 1601 auto obj = new QObject(this); 1602 obj->setProperty("prop", 7); 1603 dict.insert(QStringLiteral("var"), QVariant::fromValue(static_cast<QObject *>(obj))); 1604 1605 QTest::newRow("dynamic-properties01") << QStringLiteral("{{ var.prop }}") << dict << QStringLiteral("7") << NoError; 1606 } 1607 1608 void TestBuiltinSyntax::testGarbageInput() 1609 { 1610 QFETCH(QString, input); 1611 1612 auto t = m_engine->newTemplate(input, QLatin1String(QTest::currentDataTag())); 1613 1614 Dict dict; 1615 1616 Context context(dict); 1617 1618 auto result = t->render(&context); 1619 1620 QCOMPARE(t->error(), NoError); 1621 1622 QCOMPARE(result, input); 1623 } 1624 1625 void TestBuiltinSyntax::testGarbageInput_data() 1626 { 1627 QTest::addColumn<QString>("input"); 1628 1629 Dict dict; 1630 1631 QTest::newRow("garbage-input01") << QStringLiteral("content %}"); 1632 QTest::newRow("garbage-input02") << QStringLiteral(" content %}"); 1633 QTest::newRow("garbage-input03") << QStringLiteral("content #}"); 1634 QTest::newRow("garbage-input04") << QStringLiteral(" content #}"); 1635 QTest::newRow("garbage-input05") << QStringLiteral("content }}"); 1636 QTest::newRow("garbage-input06") << QStringLiteral(" content }}"); 1637 QTest::newRow("garbage-input07") << QStringLiteral("% content %}"); 1638 QTest::newRow("garbage-input08") << QStringLiteral("# content #}"); 1639 QTest::newRow("garbage-input09") << QStringLiteral("{ content }}"); 1640 QTest::newRow("garbage-input10") << QStringLiteral("{% content }"); 1641 QTest::newRow("garbage-input11") << QStringLiteral("{% content %"); 1642 QTest::newRow("garbage-input12") << QStringLiteral("{# content }"); 1643 QTest::newRow("garbage-input13") << QStringLiteral("{# content #"); 1644 QTest::newRow("garbage-input14") << QStringLiteral("{{ content }"); 1645 QTest::newRow("garbage-input15") << QStringLiteral("{{ content }"); 1646 QTest::newRow("garbage-input16") << QStringLiteral("{{ content %}"); 1647 QTest::newRow("garbage-input17") << QStringLiteral("{% content }}"); 1648 QTest::newRow("garbage-input18") << QStringLiteral("{{ content #}"); 1649 QTest::newRow("garbage-input19") << QStringLiteral("{# content }}"); 1650 QTest::newRow("garbage-input20") << QStringLiteral("{{ con #} tent #}"); 1651 QTest::newRow("garbage-input21") << QStringLiteral("{{ con %} tent #}"); 1652 QTest::newRow("garbage-input22") << QStringLiteral("{{ con #} tent %}"); 1653 QTest::newRow("garbage-input23") << QStringLiteral("{{ con %} tent %}"); 1654 QTest::newRow("garbage-input24") << QStringLiteral("{% con #} tent #}"); 1655 QTest::newRow("garbage-input25") << QStringLiteral("{% con }} tent #}"); 1656 QTest::newRow("garbage-input26") << QStringLiteral("{% con #} tent }}"); 1657 QTest::newRow("garbage-input27") << QStringLiteral("{% con }} tent }}"); 1658 QTest::newRow("garbage-input28") << QStringLiteral("{# con %} tent %}"); 1659 QTest::newRow("garbage-input29") << QStringLiteral("{# con }} tent %}"); 1660 QTest::newRow("garbage-input30") << QStringLiteral("{# con %} tent }}"); 1661 QTest::newRow("garbage-input31") << QStringLiteral("{# con }} tent }}"); 1662 QTest::newRow("garbage-input32") << QStringLiteral("{# con {# tent }}"); 1663 QTest::newRow("garbage-input33") << QStringLiteral("{# con {% tent }}"); 1664 QTest::newRow("garbage-input34") << QStringLiteral("{% con {% tent }}"); 1665 QTest::newRow("garbage-input35") << QStringLiteral("{ { content }}"); 1666 QTest::newRow("garbage-input36") << QStringLiteral("{ % content %}"); 1667 QTest::newRow("garbage-input37") << QStringLiteral("{ # content #}"); 1668 QTest::newRow("garbage-input38") << QStringLiteral("{\n{ content }}"); 1669 QTest::newRow("garbage-input39") << QStringLiteral("{\n# content #}"); 1670 QTest::newRow("garbage-input40") << QStringLiteral("{\n% content %}"); 1671 QTest::newRow("garbage-input41") << QStringLiteral("{{\n content }}"); 1672 QTest::newRow("garbage-input42") << QStringLiteral("{#\n content #}"); 1673 QTest::newRow("garbage-input43") << QStringLiteral("{%\n content %}"); 1674 QTest::newRow("garbage-input44") << QStringLiteral("{{ content \n}}"); 1675 QTest::newRow("garbage-input45") << QStringLiteral("{# content \n#}"); 1676 QTest::newRow("garbage-input46") << QStringLiteral("{% content \n%}"); 1677 QTest::newRow("garbage-input47") << QStringLiteral("{{ content }\n}"); 1678 QTest::newRow("garbage-input48") << QStringLiteral("{# content #\n}"); 1679 QTest::newRow("garbage-input49") << QStringLiteral("{% content %\n}"); 1680 QTest::newRow("garbage-input50") << QStringLiteral("{{ content } }"); 1681 QTest::newRow("garbage-input51") << QStringLiteral("{% content % }"); 1682 QTest::newRow("garbage-input52") << QStringLiteral("{# content # }"); 1683 QTest::newRow("garbage-input53") << QStringLiteral("{ { content } }"); 1684 QTest::newRow("garbage-input54") << QStringLiteral("{ % content % }"); 1685 QTest::newRow("garbage-input55") << QStringLiteral("{ # content # }"); 1686 QTest::newRow("garbage-input56") << QStringLiteral("{{ content }%"); 1687 QTest::newRow("garbage-input57") << QStringLiteral("{# content #%"); 1688 QTest::newRow("garbage-input58") << QStringLiteral("{% content %%"); 1689 QTest::newRow("garbage-input59") << QStringLiteral("{{ content }A"); 1690 QTest::newRow("garbage-input60") << QStringLiteral("{# content #A"); 1691 QTest::newRow("garbage-input61") << QStringLiteral("{% content %A"); 1692 QTest::newRow("garbage-input62") << QStringLiteral("{{ content A}"); 1693 QTest::newRow("garbage-input63") << QStringLiteral("{# content A#"); 1694 QTest::newRow("garbage-input64") << QStringLiteral("{% content A%"); 1695 QTest::newRow("garbage-input65") << QStringLiteral("{# content A}"); 1696 QTest::newRow("garbage-input66") << QStringLiteral("{% content A}"); 1697 QTest::newRow("garbage-input67") << QStringLiteral("A{ content }}"); 1698 QTest::newRow("garbage-input68") << QStringLiteral("A# content #}"); 1699 QTest::newRow("garbage-input69") << QStringLiteral("A% content %}"); 1700 QTest::newRow("garbage-input60") << QStringLiteral("{A content }}"); 1701 QTest::newRow("garbage-input71") << QStringLiteral("{A content #}"); 1702 QTest::newRow("garbage-input72") << QStringLiteral("{A content %}"); 1703 QTest::newRow("garbage-input73") << QStringLiteral("{A content #}"); 1704 QTest::newRow("garbage-input74") << QStringLiteral("{A content %}"); 1705 QTest::newRow("garbage-input75") << QStringLiteral("{A content A}"); 1706 QTest::newRow("garbage-input76") << QStringLiteral("}} content }}"); 1707 QTest::newRow("garbage-input77") << QStringLiteral("}} content {{"); 1708 QTest::newRow("garbage-input78") << QStringLiteral("#} content #}"); 1709 QTest::newRow("garbage-input79") << QStringLiteral("#} content {#"); 1710 QTest::newRow("garbage-input80") << QStringLiteral("%} content %}"); 1711 QTest::newRow("garbage-input81") << QStringLiteral("%} content {%"); 1712 QTest::newRow("garbage-input82") << QStringLiteral("#{ content }#"); 1713 QTest::newRow("garbage-input83") << QStringLiteral("%{ content }%"); 1714 } 1715 1716 void TestBuiltinSyntax::testInsignificantWhitespace() 1717 { 1718 QFETCH(QString, input); 1719 QFETCH(Dict, dict); 1720 QFETCH(QString, stripped_output); 1721 QFETCH(QString, unstripped_output); 1722 1723 Context context(dict); 1724 1725 QVERIFY(!m_engine->smartTrimEnabled()); 1726 m_engine->setSmartTrimEnabled(true); 1727 QVERIFY(m_engine->smartTrimEnabled()); 1728 1729 { 1730 auto t = m_engine->newTemplate(input, QLatin1String(QTest::currentDataTag())); 1731 1732 auto result = t->render(&context); 1733 1734 QCOMPARE(t->error(), NoError); 1735 1736 QCOMPARE(result, stripped_output); 1737 } 1738 m_engine->setSmartTrimEnabled(false); 1739 { 1740 auto t = m_engine->newTemplate(input, QLatin1String(QTest::currentDataTag())); 1741 1742 auto result = t->render(&context); 1743 1744 QCOMPARE(t->error(), NoError); 1745 1746 QCOMPARE(result, unstripped_output); 1747 } 1748 } 1749 1750 void TestBuiltinSyntax::testInsignificantWhitespace_data() 1751 { 1752 QTest::addColumn<QString>("input"); 1753 QTest::addColumn<Dict>("dict"); 1754 QTest::addColumn<QString>("stripped_output"); 1755 QTest::addColumn<QString>("unstripped_output"); 1756 1757 Dict dict; 1758 1759 QTest::newRow("insignificant-whitespace01") << QStringLiteral("\n {% templatetag openblock %}\n") << dict << QStringLiteral("{%\n") 1760 << QStringLiteral("\n {%\n"); 1761 1762 QTest::newRow("insignificant-whitespace02") << QStringLiteral("\n{% templatetag openblock %}\n") << dict << QStringLiteral("{%\n") 1763 << QStringLiteral("\n{%\n"); 1764 1765 QTest::newRow("insignificant-whitespace03") << QStringLiteral("{% templatetag openblock %}\n") << dict << QStringLiteral("{%\n") << QStringLiteral("{%\n"); 1766 1767 QTest::newRow("insignificant-whitespace04") << QStringLiteral("\n\t \t {% templatetag openblock %}\n") << dict << QStringLiteral("{%\n") 1768 << QStringLiteral("\n\t \t {%\n"); 1769 1770 // Leading whitespace with text before single template tag 1771 QTest::newRow("insignificant-whitespace05") << QStringLiteral("\n some\ttext {% templatetag openblock %}\n") << dict << QStringLiteral("\n some\ttext {%\n") 1772 << QStringLiteral("\n some\ttext {%\n"); 1773 1774 // Leading line with text before single template tag 1775 QTest::newRow("insignificant-whitespace06") << QStringLiteral("\n some\ttext\n {% templatetag openblock %}\n") << dict 1776 << QStringLiteral("\n some\ttext{%\n") << QStringLiteral("\n some\ttext\n {%\n"); 1777 QTest::newRow("insignificant-whitespace07") << QStringLiteral("\n some\ttext \n \t {% templatetag openblock %}\n") << dict 1778 << QStringLiteral("\n some\ttext {%\n") << QStringLiteral("\n some\ttext \n \t {%\n"); 1779 1780 // whitespace leading /before/ the newline is not stripped. 1781 QTest::newRow("insignificant-whitespace08") << QStringLiteral("\n some\ttext \t \n {% templatetag openblock %}\n") << dict 1782 << QStringLiteral("\n some\ttext \t {%\n") << QStringLiteral("\n some\ttext \t \n {%\n"); 1783 1784 // Multiple text lines before tag 1785 QTest::newRow("insignificant-whitespace09") << QStringLiteral("\n some\ntext \t \n {% templatetag openblock %}\n") << dict 1786 << QStringLiteral("\n some\ntext \t {%\n") << QStringLiteral("\n some\ntext \t \n {%\n"); 1787 QTest::newRow("insignificant-whitespace10") << QStringLiteral("\n some \t \n \t text \t \n {% templatetag openblock %}\n") << dict 1788 << QStringLiteral("\n some \t \n \t text \t {%\n") << QStringLiteral("\n some \t \n \t text \t \n {%\n"); 1789 1790 // Leading whitespace before tag, some text after 1791 QTest::newRow("insignificant-whitespace11") << QStringLiteral("\n \t {% templatetag openblock %} some text\n") << dict 1792 << QStringLiteral("\n \t {% some text\n") << QStringLiteral("\n \t {% some text\n"); 1793 1794 // Leading whitespace before tag, some text with trailing whitespace after 1795 QTest::newRow("insignificant-whitespace12") << QStringLiteral("\n \t {% templatetag openblock %} some text \t \n") << dict 1796 << QStringLiteral("\n \t {% some text \t \n") << QStringLiteral("\n \t {% some text \t \n"); 1797 1798 // Whitespace after tag is not removed 1799 QTest::newRow("insignificant-whitespace13") << QStringLiteral("\n \t {% templatetag openblock %} \t \n \t some text \t \n") << dict 1800 << QStringLiteral("{% \t \n \t some text \t \n") << QStringLiteral("\n \t {% \t \n \t some text \t \n"); 1801 1802 // Multiple lines of leading whitespace. Only one leading newline is removed 1803 QTest::newRow("insignificant-whitespace14") << QStringLiteral("\n\n\n{% templatetag openblock %}\n some text\n") << dict 1804 << QStringLiteral("\n\n{%\n some text\n") << QStringLiteral("\n\n\n{%\n some text\n"); 1805 1806 // Trailing whitespace after tag 1807 QTest::newRow("insignificant-whitespace15") << QStringLiteral("\n\n\n{% templatetag openblock %}\t \t \t\n some text\n") << dict 1808 << QStringLiteral("\n\n{%\t \t \t\n some text\n") << QStringLiteral("\n\n\n{%\t \t \t\n some text\n"); 1809 1810 // Removable newline followed by leading whitespace 1811 QTest::newRow("insignificant-whitespace16") << QStringLiteral("\n\n\n\t \t \t{% templatetag openblock %}\n some text\n") << dict 1812 << QStringLiteral("\n\n{%\n some text\n") << QStringLiteral("\n\n\n\t \t \t{%\n some text\n"); 1813 1814 // Removable leading whitespace and trailing whitespace 1815 QTest::newRow("insignificant-whitespace17") << QStringLiteral("\n\n\n\t \t \t{% templatetag openblock %}\t \t \t\n some text\n") << dict 1816 << QStringLiteral("\n\n{%\t \t \t\n some text\n") << QStringLiteral("\n\n\n\t \t \t{%\t \t \t\n some text\n"); 1817 1818 // Multiple lines of trailing whitespace. No trailing newline is removed. 1819 QTest::newRow("insignificant-whitespace18") << QStringLiteral("\n{% templatetag openblock %}\n\n\n some text\n") << dict 1820 << QStringLiteral("{%\n\n\n some text\n") << QStringLiteral("\n{%\n\n\n some text\n"); 1821 QTest::newRow("insignificant-whitespace19") << QStringLiteral("\n{% templatetag openblock %}\t \n\n\n some text\n") << dict 1822 << QStringLiteral("{%\t \n\n\n some text\n") << QStringLiteral("\n{%\t \n\n\n some text\n"); 1823 1824 // Consecutive trimmed lines with tags strips one newline each 1825 QTest::newRow("insignificant-whitespace20") << QStringLiteral( 1826 "\n{% templatetag openblock %}\n{% templatetag openblock %}\n{% " 1827 "templatetag openblock %}\n some text\n") 1828 << dict << QStringLiteral("{%{%{%\n some text\n") << QStringLiteral("\n{%\n{%\n{%\n some text\n"); 1829 1830 // Consecutive trimmed lines with tags strips one newline each. Intermediate 1831 // newlines are preserved 1832 QTest::newRow("insignificant-whitespace21") << QStringLiteral( 1833 "\n\n{% templatetag openblock %}\n\n{% templatetag openblock " 1834 "%}\n\n{% templatetag openblock %}\n\n some text\n") 1835 << dict << QStringLiteral("\n{%\n{%\n{%\n\n some text\n") 1836 << QStringLiteral("\n\n{%\n\n{%\n\n{%\n\n some text\n"); 1837 1838 // Consecutive trimmed lines with tags strips one newline each. Leading 1839 // whitespace is stripped but trailing is not 1840 QTest::newRow("insignificant-whitespace22") << QStringLiteral( 1841 "\n\n\t {% templatetag openblock %}\t \n\n\t {% " 1842 "templatetag openblock %}\t \n\n\t {% templatetag " 1843 "openblock %}\t \n some text\n") << dict << QStringLiteral("\n{%\t \n{%\t \n{%\t \n some text\n") 1844 << QStringLiteral("\n\n\t {%\t \n\n\t {%\t \n\n\t {%\t \n some text\n"); 1845 1846 // Consecutive trimmed lines with tags strips one newline each. Intermediate 1847 // whitespace is stripped 1848 QTest::newRow("insignificant-whitespace23") << QStringLiteral( 1849 "\n\t {% templatetag openblock %}\t \n\t {% templatetag openblock " 1850 "%}\t \n\t {% templatetag openblock %}\t \n some text\n") 1851 << dict << QStringLiteral("{%\t {%\t {%\t \n some text\n") 1852 << QStringLiteral("\n\t {%\t \n\t {%\t \n\t {%\t \n some text\n"); 1853 1854 // Intermediate whitespace on one line is preserved 1855 // Consecutive tags on one line do not have intermediate whitespace or 1856 // leading 1857 // whitespace stripped 1858 QTest::newRow("insignificant-whitespace24") << QStringLiteral( 1859 "\n\t {% templatetag openblock %}\t \t {% templatetag openblock " 1860 "%}\t \t {% templatetag openblock %}\t \n some text\n") 1861 << dict << QStringLiteral("\n\t {%\t \t {%\t \t {%\t \n some text\n") 1862 << QStringLiteral("\n\t {%\t \t {%\t \t {%\t \n some text\n"); 1863 1864 // Still, only one leading newline is removed. 1865 QTest::newRow("insignificant-whitespace25") << QStringLiteral( 1866 "\n\n {% templatetag openblock %}\n \t {% templatetag openblock " 1867 "%}\n \t {% templatetag openblock %}\n some text\n") 1868 << dict << QStringLiteral("\n{%{%{%\n some text\n") << QStringLiteral("\n\n {%\n \t {%\n \t {%\n some text\n"); 1869 1870 // Lines with {# comments #} have the same stripping behavior 1871 QTest::newRow("insignificant-whitespace26") << QStringLiteral( 1872 "\n\n {% templatetag openblock %}\n \t {# some comment " 1873 "#}\n some text\n") << dict << QStringLiteral("\n{%\n some text\n") 1874 << QStringLiteral("\n\n {%\n \t \n some text\n"); 1875 1876 // Only {# comments #} 1877 QTest::newRow("insignificant-whitespace27") << QStringLiteral("\n\n {# a comment #}\n \t {# some comment #}\n some text\n") << dict 1878 << QStringLiteral("\n\n some text\n") << QStringLiteral("\n\n \n \t \n some text\n"); 1879 1880 // Consecutive newlines with tags and comments 1881 QTest::newRow("insignificant-whitespace28") << QStringLiteral( 1882 "\n\t {% templatetag openblock %}\t \n\t {# some comment #}\t " 1883 "\n\t {% templatetag openblock %}\t \n some text\n") 1884 << dict << QStringLiteral("{%\t \t {%\t \n some text\n") 1885 << QStringLiteral("\n\t {%\t \n\t \t \n\t {%\t \n some text\n"); 1886 1887 dict.insert(QStringLiteral("spam"), QStringLiteral("ham")); 1888 // Lines with only {{ values }} have the same stripping behavior 1889 QTest::newRow("insignificant-whitespace29") << QStringLiteral( 1890 "\n {% templatetag openblock %}\t\n \t {{ spam }}\t \n " 1891 "\t {% templatetag openblock %}\t \n some text\n") 1892 << dict << QStringLiteral("{%\tham\t {%\t \n some text\n") 1893 << QStringLiteral("\n {%\t\n \t ham\t \n \t {%\t \n some text\n"); 1894 QTest::newRow("insignificant-whitespace30") << QStringLiteral( 1895 "\n\n {% templatetag openblock %}\t\n\n \t {{ spam }}\t \n\n \t " 1896 "{% templatetag openblock %}\t \n some text\n") 1897 << dict << QStringLiteral("\n{%\t\nham\t \n{%\t \n some text\n") 1898 << QStringLiteral("\n\n {%\t\n\n \t ham\t \n\n \t {%\t \n some text\n"); 1899 1900 // Leading whitespace not stripped when followed by anything. See 1901 // templatetag-whitespace24 1902 QTest::newRow("insignificant-whitespace31") << QStringLiteral( 1903 "\n {% templatetag openblock %}\t \t {{ spam }}\t \t " 1904 "{% templatetag openblock %}\t \n some text\n") 1905 << dict << QStringLiteral("\n {%\t \t ham\t \t {%\t \n some text\n") 1906 << QStringLiteral("\n {%\t \t ham\t \t {%\t \n some text\n"); 1907 1908 // {{ value }} {% tag %} {{ value }} this time 1909 QTest::newRow("insignificant-whitespace32") << QStringLiteral( 1910 "\n {{ spam }}\t\n \t {% templatetag openblock %}\t \n " 1911 "\t {{ spam }}\t \n some text\n") << dict 1912 << QStringLiteral("ham\t{%\t ham\t \n some text\n") 1913 << QStringLiteral("\n ham\t\n \t {%\t \n \t ham\t \n some text\n"); 1914 1915 // Invalid stuff is still invalid 1916 // Newlines inside begin-end tokens, even in {# comments #}, make it not a 1917 // tag. 1918 QTest::newRow("insignificant-whitespace33") << QStringLiteral("\n\n {# \n{% templatetag openblock #}\t \n some text\n") << dict 1919 << QStringLiteral("\n\n {# \n{% templatetag openblock #}\t \n some text\n") 1920 << QStringLiteral("\n\n {# \n{% templatetag openblock #}\t \n some text\n"); 1921 1922 // Complete comment matching tags on one line are processed 1923 QTest::newRow("insignificant-whitespace34") << QStringLiteral("\n\n {# \n{# templatetag openblock #}\t \n some text\n") << dict 1924 << QStringLiteral("\n\n {# \t \n some text\n") << QStringLiteral("\n\n {# \n\t \n some text\n"); 1925 QTest::newRow("insignificant-whitespace35") << QStringLiteral("\n\n {# \n{# templatetag openblock\n #}\t \n some text\n") << dict 1926 << QStringLiteral("\n\n {# \n{# templatetag openblock\n #}\t \n some text\n") 1927 << QStringLiteral("\n\n {# \n{# templatetag openblock\n #}\t \n some text\n"); 1928 QTest::newRow("insignificant-whitespace36") << QStringLiteral("\n\n {# \n{{ some comment #}\t \n some text\n") << dict 1929 << QStringLiteral("\n\n {# \n{{ some comment #}\t \n some text\n") 1930 << QStringLiteral("\n\n {# \n{{ some comment #}\t \n some text\n"); 1931 QTest::newRow("insignificant-whitespace37") << QStringLiteral("\n\n {# \n \t {% templatetag openblock #}\t \n some text\n") << dict 1932 << QStringLiteral("\n\n {# \n \t {% templatetag openblock #}\t \n some text\n") 1933 << QStringLiteral("\n\n {# \n \t {% templatetag openblock #}\t \n some text\n"); 1934 QTest::newRow("insignificant-whitespace38") << QStringLiteral("\n\n {# templatetag openblock #\n}\t \n some text\n") << dict 1935 << QStringLiteral("\n\n {# templatetag openblock #\n}\t \n some text\n") 1936 << QStringLiteral("\n\n {# templatetag openblock #\n}\t \n some text\n"); 1937 QTest::newRow("insignificant-whitespace39") << QStringLiteral("\n\n {% templatetag openblock %\n}\t \n some text\n") << dict 1938 << QStringLiteral("\n\n {% templatetag openblock %\n}\t \n some text\n") 1939 << QStringLiteral("\n\n {% templatetag openblock %\n}\t \n some text\n"); 1940 QTest::newRow("insignificant-whitespace40") << QStringLiteral("\n\n {{ templatetag openblock }\n}\t \n some text\n") << dict 1941 << QStringLiteral("\n\n {{ templatetag openblock }\n}\t \n some text\n") 1942 << QStringLiteral("\n\n {{ templatetag openblock }\n}\t \n some text\n"); 1943 QTest::newRow("insignificant-whitespace41") << QStringLiteral("\n\n {\n# {# templatetag openblock #}\t \n some text\n") << dict 1944 << QStringLiteral("\n\n {\n# \t \n some text\n") << QStringLiteral("\n\n {\n# \t \n some text\n"); 1945 QTest::newRow("insignificant-whitespace42") << QStringLiteral("\n\n {\n {# templatetag openblock #}\t \n some text\n") << dict 1946 << QStringLiteral("\n\n {\t \n some text\n") << QStringLiteral("\n\n {\n \t \n some text\n"); 1947 QTest::newRow("insignificant-whitespace43") << QStringLiteral("\n{{# foo #};{# bar #}\n") << dict << QStringLiteral("\n{;\n") << QStringLiteral("\n{;\n"); 1948 1949 QTest::newRow("insignificant-whitespace44") << QStringLiteral("\n{{ foo }} ") << dict << QString() << QStringLiteral("\n "); 1950 } 1951 1952 QTEST_MAIN(TestBuiltinSyntax) 1953 #include "testbuiltins.moc" 1954 1955 #endif