Cutelee  6.1.0
testdefaulttags.cpp
1 /*
2  This file is part of the Cutelee template system.
3 
4  Copyright (c) 2009,2010 Stephen Kelly <steveire@gmail.com>
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Lesser General Public
8  License as published by the Free Software Foundation; either version
9  2.1 of the Licence, or (at your option) any later version.
10 
11  This library is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  Lesser General Public License for more details.
15 
16  You should have received a copy of the GNU Lesser General Public
17  License along with this library. If not, see <http://www.gnu.org/licenses/>.
18 
19 */
20 
21 #ifndef DEFAULTTAGSTEST_H
22 #define DEFAULTTAGSTEST_H
23 
24 #include <QtCore/QDebug>
25 #include <QtTest/QTest>
26 
27 #include "context.h"
28 #include "coverageobject.h"
29 #include "engine.h"
30 #include "cutelee_paths.h"
31 #include "metatype.h"
32 #include "template.h"
33 #include "util.h"
34 
38 
39 Q_DECLARE_METATYPE(Cutelee::Error)
40 
42 {
43 public:
45  {
46  m_existingMedia << QStringLiteral("existing_image.png")
47  << QStringLiteral("another_existing_image.png")
48  << QStringLiteral("this&that.png");
49  }
50 
51  std::pair<QString, QString> getMediaUri(const QString &fileName) const override
52  {
53  if (m_existingMedia.contains(fileName))
54  return {QStringLiteral("/path/to/"), fileName};
55  return {};
56  }
57 
58 private:
59  QStringList m_existingMedia;
60 };
61 
62 class Zoo : public QObject
63 {
64  Q_OBJECT
65 public:
66  Zoo(QObject *parent = {}) : QObject(parent) {}
67 
68  enum Animals { Lions, Tigers, Bears };
69  Q_ENUM(Animals)
70 };
71 
72 using namespace Cutelee;
73 
75 {
76  Q_OBJECT
77 
78 private Q_SLOTS:
79  void initTestCase();
80  void cleanupTestCase();
81 
82  void testCommentTag_data();
83  void testCommentTag() { doTest(); }
84 
85  void testFirstOfTag_data();
86  void testFirstOfTag() { doTest(); }
87 
88  void testIfTag_data();
89  void testIfTag() { doTest(); }
90 
91  void testForTag_data();
92  void testForTag() { doTest(); }
93 
94  void testIfEqualTag_data();
95  void testIfEqualTag() { doTest(); }
96 
97  void testIfNotEqualTag_data();
98  void testIfNotEqualTag() { doTest(); }
99 
100  void testTemplateTagTag_data();
101  void testTemplateTagTag() { doTest(); }
102 
103  void testWithTag_data();
104  void testWithTag() { doTest(); }
105 
106  void testCycleTag_data();
107  void testCycleTag() { doTest(); }
108 
109  void testWidthRatioTag_data();
110  void testWidthRatioTag() { doTest(); }
111 
112  void testFilterTag_data();
113  void testFilterTag() { doTest(); }
114 
115  void testNowTag_data();
116  void testNowTag() { doTest(); }
117 
118  void testSpacelessTag_data();
119  void testSpacelessTag() { doTest(); }
120 
121  void testRegroupTag_data();
122  void testRegroupTag() { doTest(); }
123 
124  void testIfChangedTag_data();
125  void testIfChangedTag() { doTest(); }
126 
127  void testAutoescapeTag_data();
128  void testAutoescapeTag() { doTest(); }
129 
130  void testMediaFinderTag_data();
131  void testMediaFinderTag() { doTest(); }
132 
133  void testRangeTag_data();
134  void testRangeTag() { doTest(); }
135 
136  void testDebugTag_data();
137  void testDebugTag() { doTest(); }
138 
139  void testLoadTag_data();
140  void testLoadTag() { doTest(); }
141 
142  void testUrlTypes_data();
143  void testUrlTypes();
144 
145  void testRelativePaths_data();
146  void testRelativePaths();
147 
148 private:
149  void doTest();
150 
151  Engine *m_engine;
152 };
153 
154 void TestDefaultTags::initTestCase()
155 {
156  m_engine = new Engine(this);
157  m_engine->setPluginPaths({QStringLiteral(CUTELEE_PLUGIN_PATH)});
158 
159  auto loader1 = std::shared_ptr<FakeTemplateLoader>(new FakeTemplateLoader());
160 
161  m_engine->addTemplateLoader(loader1);
162 }
163 
164 void TestDefaultTags::cleanupTestCase() { delete m_engine; }
165 
166 void TestDefaultTags::doTest()
167 {
168  QFETCH(QString, input);
169  QFETCH(Dict, dict);
170  QFETCH(QString, output);
171  QFETCH(Cutelee::Error, error);
172 
173  auto t = m_engine->newTemplate(input, QLatin1String(QTest::currentDataTag()));
174 
175  if (t->error() != NoError) {
176  if (t->error() != error)
177  qDebug() << t->errorString();
178  QCOMPARE(t->error(), error);
179  return;
180  }
181 
182  Context context(dict);
183 
184  auto result = t->render(&context);
185 
186  if (t->error() != NoError) {
187  if (t->error() != error)
188  qDebug() << t->errorString();
189  QCOMPARE(t->error(), error);
190  return;
191  }
192 
193  // Didn't catch any errors, so make sure I didn't expect any.
194  QCOMPARE(NoError, error);
195 
196  QCOMPARE(t->error(), NoError);
197 
198  QCOMPARE(result, output);
199 
200  if (!result.isEmpty()
201  && QString::fromLatin1(QTest::currentTestFunction())
202  == QStringLiteral("testMediaFinderTag")) {
203  QVERIFY(!context.externalMedia().isEmpty());
204  }
205 }
206 
207 void TestDefaultTags::testCommentTag_data()
208 {
209  QTest::addColumn<QString>("input");
210  QTest::addColumn<Dict>("dict");
211  QTest::addColumn<QString>("output");
212  QTest::addColumn<Cutelee::Error>("error");
213 
214  Dict dict;
215 
216  QTest::newRow("comment-tag01")
217  << QStringLiteral("{% comment %}this is hidden{% endcomment %}hello")
218  << dict << QStringLiteral("hello") << NoError;
219 
220  QTest::newRow("comment-tag02")
221  << QStringLiteral("{% comment %}this is hidden{% endcomment %}hello{% "
222  "comment %}foo{% endcomment %}")
223  << dict << QStringLiteral("hello") << NoError;
224  // Comment tag can contain invalid stuff.
225  QTest::newRow("comment-tag03")
226  << QStringLiteral("foo{% comment %} {% if %} {% endcomment %}") << dict
227  << QStringLiteral("foo") << NoError;
228  QTest::newRow("comment-tag04")
229  << QStringLiteral("foo{% comment %} {% endblock %} {% endcomment %}")
230  << dict << QStringLiteral("foo") << NoError;
231  QTest::newRow("comment-tag05")
232  << QStringLiteral("foo{% comment %} {% somerandomtag %} {% endcomment %}")
233  << dict << QStringLiteral("foo") << NoError;
234  QTest::newRow("comment-tag06") << QStringLiteral("{% comment %}yes") << dict
235  << QString() << UnclosedBlockTagError;
236 }
237 
238 void TestDefaultTags::testFirstOfTag_data()
239 {
240  QTest::addColumn<QString>("input");
241  QTest::addColumn<Dict>("dict");
242  QTest::addColumn<QString>("output");
243  QTest::addColumn<Cutelee::Error>("error");
244 
245  Dict dict;
246  dict.insert(QStringLiteral("a"), 0);
247  dict.insert(QStringLiteral("b"), 0);
248  dict.insert(QStringLiteral("c"), 0);
249  QTest::newRow("firstof01")
250  << QStringLiteral("{% firstof a b c %}") << dict << QString() << NoError;
251 
252  dict.clear();
253  dict.insert(QStringLiteral("a"), 1);
254  dict.insert(QStringLiteral("b"), 0);
255  dict.insert(QStringLiteral("c"), 0);
256  QTest::newRow("firstof02") << QStringLiteral("{% firstof a b c %}") << dict
257  << QStringLiteral("1") << NoError;
258 
259  dict.clear();
260  dict.insert(QStringLiteral("a"), 0);
261  dict.insert(QStringLiteral("b"), 2);
262  dict.insert(QStringLiteral("c"), 0);
263  QTest::newRow("firstof03") << QStringLiteral("{% firstof a b c %}") << dict
264  << QStringLiteral("2") << NoError;
265 
266  dict.clear();
267  dict.insert(QStringLiteral("a"), 0);
268  dict.insert(QStringLiteral("b"), 0);
269  dict.insert(QStringLiteral("c"), 3);
270  QTest::newRow("firstof04") << QStringLiteral("{% firstof a b c %}") << dict
271  << QStringLiteral("3") << NoError;
272 
273  dict.clear();
274  dict.insert(QStringLiteral("a"), 1);
275  dict.insert(QStringLiteral("b"), 2);
276  dict.insert(QStringLiteral("c"), 3);
277  QTest::newRow("firstof05") << QStringLiteral("{% firstof a b c %}") << dict
278  << QStringLiteral("1") << NoError;
279 
280  dict.clear();
281  dict.insert(QStringLiteral("b"), 0);
282  dict.insert(QStringLiteral("c"), 3);
283  QTest::newRow("firstof06") << QStringLiteral("{% firstof a b c %}") << dict
284  << QStringLiteral("3") << NoError;
285 
286  dict.clear();
287  dict.insert(QStringLiteral("a"), 0);
288  QTest::newRow("firstof07")
289  << "{% firstof a b \"c\" %}" << dict << QStringLiteral("c") << NoError;
290 
291  dict.clear();
292  dict.insert(QStringLiteral("a"), 0);
293  dict.insert(QStringLiteral("b"), 0);
294  QTest::newRow("firstof08") << "{% firstof a b \"c and d\" %}" << dict
295  << QStringLiteral("c and d") << NoError;
296  QTest::newRow("firstof09") << QStringLiteral("{% firstof %}") << dict
297  << QStringLiteral("a") << TagSyntaxError;
298 }
299 
300 class BadIfObject : public QObject
301 {
302  Q_OBJECT
303  Q_PROPERTY(bool isTrue READ isTrue CONSTANT)
304  Q_PROPERTY(bool isFalse READ isFalse CONSTANT)
305  Q_PROPERTY(bool isBad READ isBad CONSTANT)
306 public:
307  BadIfObject(QObject *parent = {}) : QObject(parent), mIsBadCalled(false) {}
308 
309  bool isTrue() const { return true; }
310 
311  bool isFalse() const { return false; }
312 
313  bool isBad() const
314  {
315  mIsBadCalled = true;
316  return true;
317  }
318 
319  bool isBadCalled() { return mIsBadCalled; }
320 
321 private:
322  mutable bool mIsBadCalled;
323 };
324 
325 void TestDefaultTags::testIfTag_data()
326 {
327  QTest::addColumn<QString>("input");
328  QTest::addColumn<Dict>("dict");
329  QTest::addColumn<QString>("output");
330  QTest::addColumn<Cutelee::Error>("error");
331 
332  Dict dict;
333 
334  dict.insert(QStringLiteral("foo"), true);
335 
336  QTest::newRow("if-tag01")
337  << QStringLiteral("{% if foo %}yes{% else %}no{% endif %}") << dict
338  << QStringLiteral("yes") << NoError;
339 
340  dict.clear();
341  dict.insert(QStringLiteral("foo"), false);
342  QTest::newRow("if-tag02")
343  << QStringLiteral("{% if foo %}yes{% else %}no{% endif %}") << dict
344  << QStringLiteral("no") << NoError;
345 
346  dict.clear();
347  QTest::newRow("if-tag03")
348  << QStringLiteral("{% if foo %}yes{% else %}no{% endif %}") << dict
349  << QStringLiteral("no") << NoError;
350 
351  dict.clear();
352  dict.insert(QStringLiteral("foo"), true);
353  QTest::newRow("if-tag04")
354  << QStringLiteral("{% if foo %}foo{% elif bar %}bar{% endif %}") << dict
355  << QStringLiteral("foo") << NoError;
356 
357  dict.clear();
358  dict.insert(QStringLiteral("bar"), true);
359  QTest::newRow("if-tag05")
360  << QStringLiteral("{% if foo %}foo{% elif bar %}bar{% endif %}") << dict
361  << QStringLiteral("bar") << NoError;
362 
363  dict.clear();
364  QTest::newRow("if-tag06")
365  << QStringLiteral("{% if foo %}foo{% elif bar %}bar{% endif %}") << dict
366  << QString() << NoError;
367 
368  dict.clear();
369  dict.insert(QStringLiteral("foo"), true);
370  QTest::newRow("if-tag07") << QStringLiteral(
371  "{% if foo %}foo{% elif bar %}bar{% else %}nothing{% endif %}")
372  << dict << QStringLiteral("foo") << NoError;
373 
374  dict.clear();
375  dict.insert(QStringLiteral("bar"), true);
376  QTest::newRow("if-tag08") << QStringLiteral(
377  "{% if foo %}foo{% elif bar %}bar{% else %}nothing{% endif %}")
378  << dict << QStringLiteral("bar") << NoError;
379 
380  dict.clear();
381  QTest::newRow("if-tag09") << QStringLiteral(
382  "{% if foo %}foo{% elif bar %}bar{% else %}nothing{% endif %}")
383  << dict << QStringLiteral("nothing") << NoError;
384 
385  dict.clear();
386  dict.insert(QStringLiteral("foo"), true);
387  QTest::newRow("if-tag10")
388  << QStringLiteral("{% if foo %}foo{% elif bar %}bar{% elif baz %}baz{% "
389  "else %}nothing{% endif %}")
390  << dict << QStringLiteral("foo") << NoError;
391 
392  dict.clear();
393  dict.insert(QStringLiteral("bar"), true);
394  QTest::newRow("if-tag11")
395  << QStringLiteral("{% if foo %}foo{% elif bar %}bar{% elif baz %}baz{% "
396  "else %}nothing{% endif %}")
397  << dict << QStringLiteral("bar") << NoError;
398 
399  dict.clear();
400  dict.insert(QStringLiteral("baz"), true);
401  QTest::newRow("if-tag12")
402  << QStringLiteral("{% if foo %}foo{% elif bar %}bar{% elif baz %}baz{% "
403  "else %}nothing{% endif %}")
404  << dict << QStringLiteral("baz") << NoError;
405 
406  dict.clear();
407  QTest::newRow("if-tag13")
408  << QStringLiteral("{% if foo %}foo{% elif bar %}bar{% elif baz %}baz{% "
409  "else %}nothing{% endif %}")
410  << dict << QStringLiteral("nothing") << NoError;
411 
412  // Filters
413 
414  dict.clear();
415  dict.insert(QStringLiteral("foo"), QStringLiteral("abcde"));
416  QTest::newRow("if-tag-filter01")
417  << QStringLiteral("{% if foo|length == 5 %}yes{% else %}{{ foo|length }}{% endif %}")
418  << dict << QStringLiteral("yes") << NoError;
419 
420  dict.clear();
421  QTest::newRow("if-tag-filter02") << QStringLiteral(
422  "{% if foo|upper == \'ABC\' %}yes{% else %}no{% endif %}")
423  << dict << QStringLiteral("no") << NoError;
424 
425  // Equality
426 
427  dict.clear();
428  QTest::newRow("if-tag-eq01")
429  << QStringLiteral("{% if foo == bar %}yes{% else %}no{% endif %}") << dict
430  << QStringLiteral("yes") << NoError;
431 
432  dict.clear();
433  dict.insert(QStringLiteral("foo"), 1);
434  QTest::newRow("if-tag-eq02")
435  << QStringLiteral("{% if foo == bar %}yes{% else %}no{% endif %}") << dict
436  << QStringLiteral("no") << NoError;
437 
438  dict.clear();
439  dict.insert(QStringLiteral("foo"), 1);
440  dict.insert(QStringLiteral("bar"), 1);
441  QTest::newRow("if-tag-eq03")
442  << QStringLiteral("{% if foo == bar %}yes{% else %}no{% endif %}") << dict
443  << QStringLiteral("yes") << NoError;
444 
445  dict.clear();
446  dict.insert(QStringLiteral("foo"), 1);
447  dict.insert(QStringLiteral("bar"), 2);
448  QTest::newRow("if-tag-eq04")
449  << QStringLiteral("{% if foo == bar %}yes{% else %}no{% endif %}") << dict
450  << QStringLiteral("no") << NoError;
451 
452  dict.clear();
453  QTest::newRow("if-tag-eq05")
454  << QStringLiteral("{% if foo == \'\' %}yes{% else %}no{% endif %}")
455  << dict << QStringLiteral("no") << NoError;
456 
457  dict.clear();
458  dict.insert(QStringLiteral("foostring"), QStringLiteral("foo"));
459  QTest::newRow("if-tag-eq06") << QStringLiteral(
460  "{% if foostring == \'foo\' %}yes{% else %}no{% endif %}")
461  << dict << QStringLiteral("yes") << NoError;
462 
463  dict.clear();
464  QTest::newRow("if-tag-eq07")
465  << QStringLiteral("{% if \"foo\" == \"foo\" %}yes{% else %}no{% endif %}")
466  << dict << QStringLiteral("yes") << NoError;
467  dict.clear();
468  dict.insert(QStringLiteral("foo"), QStringLiteral("bar"));
469  QTest::newRow("if-tag-eq08")
470  << QStringLiteral("{% if foo == \"bar\" %}yes{% else %}no{% endif %}")
471  << dict << QStringLiteral("yes") << NoError;
472 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
473  dict.clear();
474  dict.insert(QStringLiteral("zoo"), QVariant::fromValue(new Zoo(this)));
475  dict.insert(QStringLiteral("tigersEnum"),
476  QVariant::fromValue<int>(Zoo::Tigers));
477  QTest::newRow("if-tag-eq07") << QStringLiteral(
478  "{% if tigersEnum == zoo.Tigers %}yes{% else %}no{% endif %}")
479  << dict << QStringLiteral("yes") << NoError;
480 #endif
481 
482  // Comparison
483 
484  dict.clear();
485  QTest::newRow("if-tag-gt-01")
486  << QStringLiteral("{% if 2 > 1 %}yes{% else %}no{% endif %}") << dict
487  << QStringLiteral("yes") << NoError;
488 
489  dict.clear();
490  QTest::newRow("if-tag-gt-02")
491  << QStringLiteral("{% if 1 > 1 %}yes{% else %}no{% endif %}") << dict
492  << QStringLiteral("no") << NoError;
493 
494  dict.clear();
495  QTest::newRow("if-tag-gte-01")
496  << QStringLiteral("{% if 1 >= 1 %}yes{% else %}no{% endif %}") << dict
497  << QStringLiteral("yes") << NoError;
498 
499  dict.clear();
500  QTest::newRow("if-tag-gte-02")
501  << QStringLiteral("{% if 1 >= 2 %}yes{% else %}no{% endif %}") << dict
502  << QStringLiteral("no") << NoError;
503 
504  dict.clear();
505  QTest::newRow("if-tag-lt-01")
506  << QStringLiteral("{% if 1 < 2 %}yes{% else %}no{% endif %}") << dict
507  << QStringLiteral("yes") << NoError;
508 
509  dict.clear();
510  QTest::newRow("if-tag-lt-02")
511  << QStringLiteral("{% if 1 < 1 %}yes{% else %}no{% endif %}") << dict
512  << QStringLiteral("no") << NoError;
513 
514  dict.clear();
515  QTest::newRow("if-tag-lte-01")
516  << QStringLiteral("{% if 1 <= 1 %}yes{% else %}no{% endif %}") << dict
517  << QStringLiteral("yes") << NoError;
518 
519  dict.clear();
520  QTest::newRow("if-tag-lte-02")
521  << QStringLiteral("{% if 2 <= 1 %}yes{% else %}no{% endif %}") << dict
522  << QStringLiteral("no") << NoError;
523 
524  // Contains
525 
526  dict.clear();
527  dict.insert(QStringLiteral("x"), QVariantList{1});
528  QTest::newRow("if-tag-in-01")
529  << QStringLiteral("{% if 1 in x %}yes{% else %}no{% endif %}") << dict
530  << QStringLiteral("yes") << NoError;
531 
532  dict.clear();
533  dict.insert(QStringLiteral("x"), QVariantList{1});
534  QTest::newRow("if-tag-in-02")
535  << QStringLiteral("{% if 2 in x %}yes{% else %}no{% endif %}") << dict
536  << QStringLiteral("no") << NoError;
537 
538  dict.clear();
539  dict.insert(QStringLiteral("x"), QVariantList{1});
540  QTest::newRow("if-tag-not-in-01")
541  << QStringLiteral("{% if 1 not in x %}yes{% else %}no{% endif %}") << dict
542  << QStringLiteral("no") << NoError;
543 
544  dict.clear();
545  dict.insert(QStringLiteral("x"), QVariantList{1});
546  QTest::newRow("if-tag-not-in-02")
547  << QStringLiteral("{% if 2 not in x %}yes{% else %}no{% endif %}") << dict
548  << QStringLiteral("yes") << NoError;
549 
550  // operator in with string
551  dict.clear();
552  dict.insert(QStringLiteral("colors"), QStringLiteral("green"));
553  QTest::newRow("if-tag-operator-in-string01")
554  << QStringLiteral(
555  "{% if \"green\" in colors %}yes{% else %}no{% endif %}")
556  << dict << QStringLiteral("yes") << NoError;
557 
558  dict.clear();
559  dict.insert(QStringLiteral("colors"), QStringLiteral("red"));
560  QTest::newRow("if-tag-operator-in-string02")
561  << QStringLiteral(
562  "{% if \"green\" in colors %}yes{% else %}no{% endif %}")
563  << dict << QStringLiteral("no") << NoError;
564 
565  dict.clear();
566  dict.insert(QStringLiteral("colors"), QStringLiteral("green"));
567  QTest::newRow("if-tag-operator-in-string03")
568  << QStringLiteral(
569  "{% if \"green\" in colors %}yes{% else %}no{% endif %}")
570  << dict << QStringLiteral("yes") << NoError;
571 
572  dict.clear();
573  dict.insert(QStringLiteral("colors"), QStringLiteral("red"));
574  QTest::newRow("if-tag-operator-in-string04")
575  << QStringLiteral(
576  "{% if \"green\" in colors %}yes{% else %}no{% endif %}")
577  << dict << QStringLiteral("no") << NoError;
578 
579  dict.clear();
580  dict.insert(QStringLiteral("color"), QStringLiteral("green"));
581  dict.insert(QStringLiteral("colors"), QStringLiteral("green"));
582  QTest::newRow("if-tag-operator-in-string05")
583  << QStringLiteral("{% if color in colors %}yes{% else %}no{% endif %}")
584  << dict << QStringLiteral("yes") << NoError;
585 
586  dict.clear();
587  dict.insert(QStringLiteral("color"), QStringLiteral("green"));
588  dict.insert(QStringLiteral("colors"), QStringLiteral("red"));
589  QTest::newRow("if-tag-operator-in-string06")
590  << QStringLiteral("{% if color in colors %}yes{% else %}no{% endif %}")
591  << dict << QStringLiteral("no") << NoError;
592 
593  // AND
594 
595  dict.clear();
596  dict.insert(QStringLiteral("foo"), true);
597  dict.insert(QStringLiteral("bar"), true);
598  QTest::newRow("if-tag-and01")
599  << QStringLiteral("{% if foo and bar %}yes{% else %}no{% endif %}")
600  << dict << QStringLiteral("yes") << NoError;
601 
602  dict.clear();
603  dict.insert(QStringLiteral("foo"), true);
604  dict.insert(QStringLiteral("bar"), false);
605  QTest::newRow("if-tag-and02")
606  << QStringLiteral("{% if foo and bar %}yes{% else %}no{% endif %}")
607  << dict << QStringLiteral("no") << NoError;
608 
609  dict.clear();
610  dict.insert(QStringLiteral("foo"), false);
611  dict.insert(QStringLiteral("bar"), true);
612  QTest::newRow("if-tag-and03")
613  << QStringLiteral("{% if foo and bar %}yes{% else %}no{% endif %}")
614  << dict << QStringLiteral("no") << NoError;
615 
616  dict.clear();
617  dict.insert(QStringLiteral("foo"), false);
618  dict.insert(QStringLiteral("bar"), false);
619  QTest::newRow("if-tag-and04")
620  << QStringLiteral("{% if foo and bar %}yes{% else %}no{% endif %}")
621  << dict << QStringLiteral("no") << NoError;
622 
623  dict.clear();
624  dict.insert(QStringLiteral("foo"), false);
625  QTest::newRow("if-tag-and05")
626  << QStringLiteral("{% if foo and bar %}yes{% else %}no{% endif %}")
627  << dict << QStringLiteral("no") << NoError;
628 
629  dict.clear();
630  dict.insert(QStringLiteral("bar"), false);
631  QTest::newRow("if-tag-and06")
632  << QStringLiteral("{% if foo and bar %}yes{% else %}no{% endif %}")
633  << dict << QStringLiteral("no") << NoError;
634 
635  dict.clear();
636  dict.insert(QStringLiteral("foo"), true);
637  QTest::newRow("if-tag-and07")
638  << QStringLiteral("{% if foo and bar %}yes{% else %}no{% endif %}")
639  << dict << QStringLiteral("no") << NoError;
640 
641  dict.clear();
642  dict.insert(QStringLiteral("bar"), true);
643  QTest::newRow("if-tag-and08")
644  << QStringLiteral("{% if foo and bar %}yes{% else %}no{% endif %}")
645  << dict << QStringLiteral("no") << NoError;
646 
647  // OR
648 
649  dict.clear();
650  dict.insert(QStringLiteral("foo"), true);
651  dict.insert(QStringLiteral("bar"), true);
652  QTest::newRow("if-tag-or01")
653  << QStringLiteral("{% if foo or bar %}yes{% else %}no{% endif %}") << dict
654  << QStringLiteral("yes") << NoError;
655 
656  dict.clear();
657  dict.insert(QStringLiteral("foo"), true);
658  dict.insert(QStringLiteral("bar"), false);
659  QTest::newRow("if-tag-or02")
660  << QStringLiteral("{% if foo or bar %}yes{% else %}no{% endif %}") << dict
661  << QStringLiteral("yes") << NoError;
662 
663  dict.clear();
664  dict.insert(QStringLiteral("foo"), false);
665  dict.insert(QStringLiteral("bar"), true);
666  QTest::newRow("if-tag-or03")
667  << QStringLiteral("{% if foo or bar %}yes{% else %}no{% endif %}") << dict
668  << QStringLiteral("yes") << NoError;
669 
670  dict.clear();
671  dict.insert(QStringLiteral("foo"), false);
672  dict.insert(QStringLiteral("bar"), false);
673  QTest::newRow("if-tag-or04")
674  << QStringLiteral("{% if foo or bar %}yes{% else %}no{% endif %}") << dict
675  << QStringLiteral("no") << NoError;
676 
677  dict.clear();
678  dict.insert(QStringLiteral("foo"), false);
679  QTest::newRow("if-tag-or05")
680  << QStringLiteral("{% if foo or bar %}yes{% else %}no{% endif %}") << dict
681  << QStringLiteral("no") << NoError;
682 
683  dict.clear();
684  dict.insert(QStringLiteral("bar"), false);
685  QTest::newRow("if-tag-or06")
686  << QStringLiteral("{% if foo or bar %}yes{% else %}no{% endif %}") << dict
687  << QStringLiteral("no") << NoError;
688 
689  dict.clear();
690  dict.insert(QStringLiteral("foo"), true);
691  QTest::newRow("if-tag-or07")
692  << QStringLiteral("{% if foo or bar %}yes{% else %}no{% endif %}") << dict
693  << QStringLiteral("yes") << NoError;
694 
695  dict.clear();
696  dict.insert(QStringLiteral("bar"), true);
697  QTest::newRow("if-tag-or08")
698  << QStringLiteral("{% if foo or bar %}yes{% else %}no{% endif %}") << dict
699  << QStringLiteral("yes") << NoError;
700 
701  dict.clear();
702  dict.insert(QStringLiteral("baz"), true);
703  QTest::newRow("if-tag-or09")
704  << QStringLiteral("{% if foo or bar or baz %}yes{% else %}no{% endif %}")
705  << dict << QStringLiteral("yes") << NoError;
706 
707  // NOT
708 
709  dict.clear();
710  dict.insert(QStringLiteral("foo"), true);
711  QTest::newRow("if-tag-not01")
712  << QStringLiteral("{% if not foo %}no{% else %}yes{% endif %}") << dict
713  << QStringLiteral("yes") << NoError;
714 
715  dict.insert(QStringLiteral("foo"), true);
716  QTest::newRow("if-tag-not02")
717  << QStringLiteral("{% if not not foo %}no{% else %}yes{% endif %}")
718  << dict << QStringLiteral("no") << NoError;
719 
720  dict.clear();
721  QTest::newRow("if-tag-not06")
722  << QStringLiteral("{% if foo and not bar %}yes{% else %}no{% endif %}")
723  << dict << QStringLiteral("no") << NoError;
724 
725  dict.clear();
726  dict.insert(QStringLiteral("foo"), true);
727  dict.insert(QStringLiteral("bar"), true);
728  QTest::newRow("if-tag-not07")
729  << QStringLiteral("{% if foo and not bar %}yes{% else %}no{% endif %}")
730  << dict << QStringLiteral("no") << NoError;
731 
732  dict.clear();
733  dict.insert(QStringLiteral("foo"), true);
734  dict.insert(QStringLiteral("bar"), false);
735  QTest::newRow("if-tag-not08")
736  << QStringLiteral("{% if foo and not bar %}yes{% else %}no{% endif %}")
737  << dict << QStringLiteral("yes") << NoError;
738 
739  dict.clear();
740  dict.insert(QStringLiteral("foo"), false);
741  dict.insert(QStringLiteral("bar"), true);
742  QTest::newRow("if-tag-not09")
743  << QStringLiteral("{% if foo and not bar %}yes{% else %}no{% endif %}")
744  << dict << QStringLiteral("no") << NoError;
745 
746  dict.clear();
747  dict.insert(QStringLiteral("foo"), false);
748  dict.insert(QStringLiteral("bar"), false);
749  QTest::newRow("if-tag-not10")
750  << QStringLiteral("{% if foo and not bar %}yes{% else %}no{% endif %}")
751  << dict << QStringLiteral("no") << NoError;
752 
753  dict.clear();
754  QTest::newRow("if-tag-not11")
755  << QStringLiteral("{% if not foo and bar %}yes{% else %}no{% endif %}")
756  << dict << QStringLiteral("no") << NoError;
757 
758  dict.clear();
759  dict.insert(QStringLiteral("foo"), true);
760  dict.insert(QStringLiteral("bar"), true);
761  QTest::newRow("if-tag-not12")
762  << QStringLiteral("{% if not foo and bar %}yes{% else %}no{% endif %}")
763  << dict << QStringLiteral("no") << NoError;
764 
765  dict.clear();
766  dict.insert(QStringLiteral("foo"), true);
767  dict.insert(QStringLiteral("bar"), false);
768  QTest::newRow("if-tag-not13")
769  << QStringLiteral("{% if not foo and bar %}yes{% else %}no{% endif %}")
770  << dict << QStringLiteral("no") << NoError;
771 
772  dict.clear();
773  dict.insert(QStringLiteral("foo"), false);
774  dict.insert(QStringLiteral("bar"), true);
775  QTest::newRow("if-tag-not14")
776  << QStringLiteral("{% if not foo and bar %}yes{% else %}no{% endif %}")
777  << dict << QStringLiteral("yes") << NoError;
778 
779  dict.clear();
780  dict.insert(QStringLiteral("foo"), false);
781  dict.insert(QStringLiteral("bar"), false);
782  QTest::newRow("if-tag-not15")
783  << QStringLiteral("{% if not foo and bar %}yes{% else %}no{% endif %}")
784  << dict << QStringLiteral("no") << NoError;
785 
786  dict.clear();
787  QTest::newRow("if-tag-not16")
788  << QStringLiteral("{% if foo or not bar %}yes{% else %}no{% endif %}")
789  << dict << QStringLiteral("yes") << NoError;
790 
791  dict.clear();
792  dict.insert(QStringLiteral("foo"), true);
793  dict.insert(QStringLiteral("bar"), true);
794  QTest::newRow("if-tag-not17")
795  << QStringLiteral("{% if foo or not bar %}yes{% else %}no{% endif %}")
796  << dict << QStringLiteral("yes") << NoError;
797 
798  dict.clear();
799  dict.insert(QStringLiteral("foo"), true);
800  dict.insert(QStringLiteral("bar"), false);
801  QTest::newRow("if-tag-not18")
802  << QStringLiteral("{% if foo or not bar %}yes{% else %}no{% endif %}")
803  << dict << QStringLiteral("yes") << NoError;
804 
805  dict.clear();
806  dict.insert(QStringLiteral("foo"), false);
807  dict.insert(QStringLiteral("bar"), true);
808  QTest::newRow("if-tag-not19")
809  << QStringLiteral("{% if foo or not bar %}yes{% else %}no{% endif %}")
810  << dict << QStringLiteral("no") << NoError;
811 
812  dict.clear();
813  dict.insert(QStringLiteral("foo"), false);
814  dict.insert(QStringLiteral("bar"), false);
815  QTest::newRow("if-tag-not20")
816  << QStringLiteral("{% if foo or not bar %}yes{% else %}no{% endif %}")
817  << dict << QStringLiteral("yes") << NoError;
818 
819  dict.clear();
820  QTest::newRow("if-tag-not21")
821  << QStringLiteral("{% if not foo or bar %}yes{% else %}no{% endif %}")
822  << dict << QStringLiteral("yes") << NoError;
823 
824  dict.clear();
825  dict.insert(QStringLiteral("foo"), true);
826  dict.insert(QStringLiteral("bar"), true);
827  QTest::newRow("if-tag-not22")
828  << QStringLiteral("{% if not foo or bar %}yes{% else %}no{% endif %}")
829  << dict << QStringLiteral("yes") << NoError;
830 
831  dict.clear();
832  dict.insert(QStringLiteral("foo"), true);
833  dict.insert(QStringLiteral("bar"), false);
834  QTest::newRow("if-tag-not23")
835  << QStringLiteral("{% if not foo or bar %}yes{% else %}no{% endif %}")
836  << dict << QStringLiteral("no") << NoError;
837 
838  dict.clear();
839  dict.insert(QStringLiteral("foo"), false);
840  dict.insert(QStringLiteral("bar"), true);
841  QTest::newRow("if-tag-not24")
842  << QStringLiteral("{% if not foo or bar %}yes{% else %}no{% endif %}")
843  << dict << QStringLiteral("yes") << NoError;
844 
845  dict.clear();
846  dict.insert(QStringLiteral("foo"), false);
847  dict.insert(QStringLiteral("bar"), false);
848  QTest::newRow("if-tag-not25")
849  << QStringLiteral("{% if not foo or bar %}yes{% else %}no{% endif %}")
850  << dict << QStringLiteral("yes") << NoError;
851 
852  dict.clear();
853  QTest::newRow("if-tag-not26") << QStringLiteral(
854  "{% if not foo and not bar %}yes{% else %}no{% endif %}")
855  << dict << QStringLiteral("yes") << NoError;
856 
857  dict.clear();
858  dict.insert(QStringLiteral("foo"), true);
859  dict.insert(QStringLiteral("bar"), true);
860  QTest::newRow("if-tag-not27") << QStringLiteral(
861  "{% if not foo and not bar %}yes{% else %}no{% endif %}")
862  << dict << QStringLiteral("no") << NoError;
863 
864  dict.clear();
865  dict.insert(QStringLiteral("foo"), true);
866  dict.insert(QStringLiteral("bar"), false);
867  QTest::newRow("if-tag-not28") << QStringLiteral(
868  "{% if not foo and not bar %}yes{% else %}no{% endif %}")
869  << dict << QStringLiteral("no") << NoError;
870 
871  dict.clear();
872  dict.insert(QStringLiteral("foo"), false);
873  dict.insert(QStringLiteral("bar"), true);
874  QTest::newRow("if-tag-not29") << QStringLiteral(
875  "{% if not foo and not bar %}yes{% else %}no{% endif %}")
876  << dict << QStringLiteral("no") << NoError;
877 
878  dict.clear();
879  dict.insert(QStringLiteral("foo"), false);
880  dict.insert(QStringLiteral("bar"), false);
881  QTest::newRow("if-tag-not30") << QStringLiteral(
882  "{% if not foo and not bar %}yes{% else %}no{% endif %}")
883  << dict << QStringLiteral("yes") << NoError;
884 
885  dict.clear();
886  QTest::newRow("if-tag-not31")
887  << QStringLiteral("{% if not foo or not bar %}yes{% else %}no{% endif %}")
888  << dict << QStringLiteral("yes") << NoError;
889 
890  dict.clear();
891  dict.insert(QStringLiteral("foo"), true);
892  dict.insert(QStringLiteral("bar"), true);
893  QTest::newRow("if-tag-not32")
894  << QStringLiteral("{% if not foo or not bar %}yes{% else %}no{% endif %}")
895  << dict << QStringLiteral("no") << NoError;
896 
897  dict.clear();
898  dict.insert(QStringLiteral("foo"), true);
899  dict.insert(QStringLiteral("bar"), false);
900  QTest::newRow("if-tag-not33")
901  << QStringLiteral("{% if not foo or not bar %}yes{% else %}no{% endif %}")
902  << dict << QStringLiteral("yes") << NoError;
903 
904  dict.clear();
905  dict.insert(QStringLiteral("foo"), false);
906  dict.insert(QStringLiteral("bar"), true);
907  QTest::newRow("if-tag-not34")
908  << QStringLiteral("{% if not foo or not bar %}yes{% else %}no{% endif %}")
909  << dict << QStringLiteral("yes") << NoError;
910 
911  dict.clear();
912  dict.insert(QStringLiteral("foo"), false);
913  dict.insert(QStringLiteral("bar"), false);
914  QTest::newRow("if-tag-not35")
915  << QStringLiteral("{% if not foo or not bar %}yes{% else %}no{% endif %}")
916  << dict << QStringLiteral("yes") << NoError;
917 
918  QTest::newRow("if-tag-error01") << QStringLiteral("{% if %}yes{% endif %}")
919  << dict << QString() << TagSyntaxError;
920 
921  dict.clear();
922  dict.insert(QStringLiteral("foo"), true);
923  QTest::newRow("if-tag-error02")
924  << QStringLiteral("{% if foo and %}yes{% else %}no{% endif %}") << dict
925  << QString() << TagSyntaxError;
926  QTest::newRow("if-tag-error03")
927  << QStringLiteral("{% if foo or %}yes{% else %}no{% endif %}") << dict
928  << QString() << TagSyntaxError;
929  QTest::newRow("if-tag-error04")
930  << QStringLiteral("{% if not foo and %}yes{% else %}no{% endif %}")
931  << dict << QString() << TagSyntaxError;
932  QTest::newRow("if-tag-error05")
933  << QStringLiteral("{% if not foo or %}yes{% else %}no{% endif %}") << dict
934  << QString() << TagSyntaxError;
935  QTest::newRow("if-tag-error06")
936  << QStringLiteral("{% if abc def %}yes{% endif %}") << dict << QString()
937  << TagSyntaxError;
938  QTest::newRow("if-tag-error07")
939  << QStringLiteral("{% if not %}yes{% endif %}") << dict << QString()
940  << TagSyntaxError;
941  QTest::newRow("if-tag-error08")
942  << QStringLiteral("{% if and %}yes{% endif %}") << dict << QString()
943  << TagSyntaxError;
944  QTest::newRow("if-tag-error09") << QStringLiteral("{% if or %}yes{% endif %}")
945  << dict << QString() << TagSyntaxError;
946  QTest::newRow("if-tag-error10") << QStringLiteral("{% if == %}yes{% endif %}")
947  << dict << QString() << TagSyntaxError;
948  QTest::newRow("if-tag-error11")
949  << QStringLiteral("{% if 1 == %}yes{% endif %}") << dict << QString()
950  << TagSyntaxError;
951  QTest::newRow("if-tag-error12")
952  << QStringLiteral("{% if a not b %}yes{% endif %}") << dict << QString()
953  << TagSyntaxError;
954 
955  // Short circuit
956  dict.clear();
957  {
959  dict.insert(QStringLiteral("x"), QVariant::fromValue(bio));
960  QTest::newRow("if-tag-shortcircuit01")
961  << QStringLiteral(
962  "{% if x.isTrue or x.isBad %}yes{% else %}no{% endif %}")
963  << dict << QStringLiteral("yes") << NoError;
964 
965  QTest::newRow("if-tag-shortcircuit02")
966  << QStringLiteral(
967  "{% if x.isFalse and x.isBad %}yes{% else %}no{% endif %}")
968  << dict << QStringLiteral("no") << NoError;
969  dict.clear();
970  }
971 
972  QTest::newRow("if-tag-badarg01")
973  << QStringLiteral("{% if x|default_if_none:y %}yes{% endif %}") << dict
974  << QString() << NoError;
975 
976  dict.clear();
977  dict.insert(QStringLiteral("y"), 0);
978  QTest::newRow("if-tag-badarg02")
979  << QStringLiteral("{% if x|default_if_none:y %}yes{% endif %}") << dict
980  << QString() << NoError;
981 
982  dict.clear();
983  dict.insert(QStringLiteral("y"), 1);
984  QTest::newRow("if-tag-badarg03")
985  << QStringLiteral("{% if x|default_if_none:y %}yes{% endif %}") << dict
986  << QStringLiteral("yes") << NoError;
987 
988  dict.clear();
989  QTest::newRow("if-tag-badarg04") << QStringLiteral(
990  "{% if x|default_if_none:y %}yes{% else %}no{% endif %}")
991  << dict << QStringLiteral("no") << NoError;
992 
993  dict.clear();
994  dict.insert(QStringLiteral("foo"), 1);
995  QTest::newRow("if-tag-single-eq")
996  << QStringLiteral("{% if foo = bar %}yes{% else %}no{% endif %}") << dict
997  << QString() << TagSyntaxError;
998 
999  // Truthiness
1000  dict.clear();
1001  QVariantHash hash;
1002  dict.insert(QStringLiteral("var"), hash);
1003  QTest::newRow("if-truthiness01")
1004  << QStringLiteral("{% if var %}Yes{% else %}No{% endif %}") << dict
1005  << QStringLiteral("No") << NoError;
1006  hash.insert(QStringLiteral("foo"), QStringLiteral("bar"));
1007  dict.insert(QStringLiteral("var"), hash);
1008  QTest::newRow("if-truthiness02")
1009  << QStringLiteral("{% if var %}Yes{% else %}No{% endif %}") << dict
1010  << QStringLiteral("Yes") << NoError;
1011  QVariantList list;
1012  dict.insert(QStringLiteral("var"), list);
1013  QTest::newRow("if-truthiness03")
1014  << QStringLiteral("{% if var %}Yes{% else %}No{% endif %}") << dict
1015  << QStringLiteral("No") << NoError;
1016  list.append(QStringLiteral("foo"));
1017  dict.insert(QStringLiteral("var"), list);
1018  QTest::newRow("if-truthiness04")
1019  << QStringLiteral("{% if var %}Yes{% else %}No{% endif %}") << dict
1020  << QStringLiteral("Yes") << NoError;
1021  QVariantMap map;
1022  dict.insert(QStringLiteral("var"), map);
1023  QTest::newRow("if-truthiness05")
1024  << QStringLiteral("{% if var %}Yes{% else %}No{% endif %}") << dict
1025  << QStringLiteral("No") << NoError;
1026  map.insert(QStringLiteral("foo"), QStringLiteral("bar"));
1027  dict.insert(QStringLiteral("var"), map);
1028  QTest::newRow("if-truthiness06")
1029  << QStringLiteral("{% if var %}Yes{% else %}No{% endif %}") << dict
1030  << QStringLiteral("Yes") << NoError;
1031 
1032 
1033  QVariant var;
1034  dict.insert(QStringLiteral("var"), var);
1035  QTest::newRow("if-truthiness07")
1036  << QStringLiteral("{% if var %}Yes{% else %}No{% endif %}") << dict
1037  << QStringLiteral("No") << NoError;
1038  var = QStringLiteral("foo");
1039  dict.insert(QStringLiteral("var"), var);
1040  QTest::newRow("if-truthiness08")
1041  << QStringLiteral("{% if var %}Yes{% else %}No{% endif %}") << dict
1042  << QStringLiteral("Yes") << NoError;
1043 
1044  QString str;
1045  dict.insert(QStringLiteral("var"), str);
1046  QTest::newRow("if-truthiness09")
1047  << QStringLiteral("{% if var %}Yes{% else %}No{% endif %}") << dict
1048  << QStringLiteral("No") << NoError;
1049  str = QStringLiteral("foo");
1050  dict.insert(QStringLiteral("var"), str);
1051  QTest::newRow("if-truthiness10")
1052  << QStringLiteral("{% if var %}Yes{% else %}No{% endif %}") << dict
1053  << QStringLiteral("Yes") << NoError;
1054 
1055  auto i = 0;
1056  dict.insert(QStringLiteral("var"), i);
1057  QTest::newRow("if-truthiness11")
1058  << QStringLiteral("{% if var %}Yes{% else %}No{% endif %}") << dict
1059  << QStringLiteral("No") << NoError;
1060  i = 7;
1061  dict.insert(QStringLiteral("var"), i);
1062  QTest::newRow("if-truthiness12")
1063  << QStringLiteral("{% if var %}Yes{% else %}No{% endif %}") << dict
1064  << QStringLiteral("Yes") << NoError;
1065 
1066  auto r = 0.0;
1067  dict.insert(QStringLiteral("var"), r);
1068  QTest::newRow("if-truthiness13")
1069  << QStringLiteral("{% if var %}Yes{% else %}No{% endif %}") << dict
1070  << QStringLiteral("No") << NoError;
1071  r = 7.1;
1072  dict.insert(QStringLiteral("var"), r);
1073  QTest::newRow("if-truthiness14")
1074  << QStringLiteral("{% if var %}Yes{% else %}No{% endif %}") << dict
1075  << QStringLiteral("Yes") << NoError;
1076 
1077  dict.clear();
1078  QTest::newRow("if-tag-badarg01")
1079  << QStringLiteral("{% if x|default_if_none:y %}yes{% endif %}") << dict
1080  << QString() << NoError;
1081 
1082  dict.insert(QStringLiteral("y"), 0);
1083 
1084  QTest::newRow("if-tag-badarg02")
1085  << QStringLiteral("{% if x|default_if_none:y %}yes{% endif %}") << dict
1086  << QString() << NoError;
1087 
1088  dict.clear();
1089  dict.insert(QStringLiteral("y"), 1);
1090 
1091  QTest::newRow("if-tag-badarg03")
1092  << QStringLiteral("{% if x|default_if_none:y %}yes{% endif %}") << dict
1093  << QStringLiteral("yes") << NoError;
1094 
1095  dict.clear();
1096  QTest::newRow("if-tag-badarg04") << QStringLiteral(
1097  "{% if x|default_if_none:y %}yes{% else %}no{% endif %}")
1098  << dict << QStringLiteral("no") << NoError;
1099 }
1100 
1101 void TestDefaultTags::testForTag_data()
1102 {
1103  QTest::addColumn<QString>("input");
1104  QTest::addColumn<Dict>("dict");
1105  QTest::addColumn<QString>("output");
1106  QTest::addColumn<Cutelee::Error>("error");
1107 
1108  Dict dict;
1109 
1110  QVariantList list{1, 2, 3};
1111  dict.insert(QStringLiteral("values"), list);
1112  QTest::newRow("for-tag01")
1113  << QStringLiteral("{% for val in values %}{{ val }}{% endfor %}") << dict
1114  << QStringLiteral("123") << NoError;
1115  QTest::newRow("for-tag02")
1116  << QStringLiteral("{% for val in values reversed %}{{ val }}{% endfor %}")
1117  << dict << QStringLiteral("321") << NoError;
1118  list.clear();
1119  dict.insert(QStringLiteral("values"), list);
1120  QTest::newRow("for-tag03") << QStringLiteral(
1121  "{% for val in values %}({{ val }} sdfsdf,){% endfor %}")
1122  << dict << QString() << NoError;
1123  QStringList emails{QStringLiteral("one"), QStringLiteral("two")};
1124  QVariantHash obj;
1125  obj.insert(QStringLiteral("emails"), emails);
1126  dict.insert(QStringLiteral("contact"), obj);
1127  QTest::newRow("for-tag04")
1128  << QStringLiteral(
1129  "{% for val in contact.emails %}({{ val }},){% endfor %}")
1130  << dict << QStringLiteral("(one,)(two,)") << NoError;
1131  emails.clear();
1132  obj.insert(QStringLiteral("emails"), emails);
1133  dict.insert(QStringLiteral("contact"), obj);
1134  QTest::newRow("for-tag05") << QStringLiteral(
1135  "{% for val in contact.emails %}({{ val }},){% endfor %}")
1136  << dict << QString() << NoError;
1137  list.clear();
1138  dict.clear();
1139  emails << QStringLiteral("one");
1140  dict.insert(QStringLiteral("emails"), emails);
1141  QTest::newRow("for-tag06")
1142  << QStringLiteral("{% for val in emails %}({{ val }},){% endfor %}")
1143  << dict << QStringLiteral("(one,)") << NoError;
1144  list.clear();
1145  dict.clear();
1146 
1147  QTest::newRow("for-tag07") << QStringLiteral("{% for %}{% endfor %}") << dict
1148  << QString() << TagSyntaxError;
1149  QTest::newRow("for-tag08")
1150  << QStringLiteral("{% for foo bar bat %}{% endfor %}") << dict
1151  << QString() << TagSyntaxError;
1152  QTest::newRow("for-tag09")
1153  << QStringLiteral("{% for foo bar '' %}{% endfor %}") << dict << QString()
1154  << TagSyntaxError;
1155 
1156  list << 1 << 2 << 3;
1157  dict.insert(QStringLiteral("values"), list);
1158  QTest::newRow("for-tag-vars01") << QStringLiteral(
1159  "{% for val in values %}{{ forloop.counter }}{% endfor %}")
1160  << dict << QStringLiteral("123") << NoError;
1161  QTest::newRow("for-tag-vars02") << QStringLiteral(
1162  "{% for val in values %}{{ forloop.counter0 }}{% endfor %}")
1163  << dict << QStringLiteral("012") << NoError;
1164  QTest::newRow("for-tag-vars03") << QStringLiteral(
1165  "{% for val in values %}{{ forloop.revcounter }}{% endfor %}")
1166  << dict << QStringLiteral("321") << NoError;
1167  QTest::newRow("for-tag-vars04") << QStringLiteral(
1168  "{% for val in values %}{{ forloop.revcounter0 }}{% endfor %}")
1169  << dict << QStringLiteral("210") << NoError;
1170  QTest::newRow("for-tag-vars05")
1171  << QStringLiteral("{% for val in values %}{% if forloop.first %}f{% else "
1172  "%}x{% endif %}{% endfor %}")
1173  << dict << QStringLiteral("fxx") << NoError;
1174  QTest::newRow("for-tag-vars06")
1175  << QStringLiteral("{% for val in values %}{% if forloop.last %}l{% else "
1176  "%}x{% endif %}{% endfor %}")
1177  << dict << QStringLiteral("xxl") << NoError;
1178 
1179  dict.clear();
1180  list.clear();
1181  QVariantList innerList{QStringLiteral("one"), 1};
1182  list.append(QVariant(innerList));
1183  innerList.clear();
1184  innerList << QStringLiteral("two") << 2;
1185  list.append(QVariant(innerList));
1186  dict.insert(QStringLiteral("items"), list);
1187  QTest::newRow("for-tag-unpack01")
1188  << QStringLiteral(
1189  "{% for key,value in items %}{{ key }}:{{ value }}/{% endfor %}")
1190  << dict << QStringLiteral("one:1/two:2/") << NoError;
1191 
1192  QTest::newRow("for-tag-unpack03")
1193  << QStringLiteral(
1194  "{% for key, value in items %}{{ key }}:{{ value }}/{% endfor %}")
1195  << dict << QStringLiteral("one:1/two:2/") << NoError;
1196  QTest::newRow("for-tag-unpack04")
1197  << QStringLiteral(
1198  "{% for key , value in items %}{{ key }}:{{ value }}/{% endfor %}")
1199  << dict << QStringLiteral("one:1/two:2/") << NoError;
1200  QTest::newRow("for-tag-unpack05")
1201  << QStringLiteral(
1202  "{% for key ,value in items %}{{ key }}:{{ value }}/{% endfor %}")
1203  << dict << QStringLiteral("one:1/two:2/") << NoError;
1204  QTest::newRow("for-tag-unpack06")
1205  << QStringLiteral(
1206  "{% for key value in items %}{{ key }}:{{ value }}/{% endfor %}")
1207  << dict << QStringLiteral("one:1/two:2/") << NoError;
1208  QTest::newRow("for-tag-unpack07")
1209  << QStringLiteral(
1210  "{% for key,,value in items %}{{ key }}:{{ value }}/{% endfor %}")
1211  << dict << QStringLiteral("one:1/two:2/") << NoError;
1212  QTest::newRow("for-tag-unpack08")
1213  << QStringLiteral(
1214  "{% for key,value, in items %}{{ key }}:{{ value }}/{% endfor %}")
1215  << dict << QStringLiteral("one:1/two:2/") << NoError;
1216 
1217  // Ensure that a single loopvar doesn't truncate the list in val.
1218  QTest::newRow("for-tag-unpack09")
1219  << QStringLiteral(
1220  "{% for val in items %}{{ val.0 }}:{{ val.1 }}/{% endfor %}")
1221  << dict << QStringLiteral("one:1/two:2/") << NoError;
1222 
1223  // Otherwise, silently truncate if the length of loopvars differs to the
1224  // length of each set of items.
1225 
1226  dict.clear();
1227  list.clear();
1228  innerList.clear();
1229  innerList << QStringLiteral("one") << 1 << QStringLiteral("carrot");
1230  list.append(QVariant(innerList));
1231  innerList.clear();
1232  innerList << QStringLiteral("two") << 2 << QStringLiteral("orange");
1233  list.append(QVariant(innerList));
1234  dict.insert(QStringLiteral("items"), list);
1235 
1236  QTest::newRow("for-tag-unpack10")
1237  << QStringLiteral("{% for x,y in items %}{{ x }}:{{ y }}/{% endfor %}")
1238  << dict << QStringLiteral("one:1/two:2/") << NoError;
1239 
1240  dict.clear();
1241  list.clear();
1242  innerList.clear();
1243  innerList << QStringLiteral("one") << 1;
1244  list.append(QVariant(innerList));
1245  innerList.clear();
1246  innerList << QStringLiteral("two") << 2;
1247  list.append(QVariant(innerList));
1248  dict.insert(QStringLiteral("items"), list);
1249 
1250  QTest::newRow("for-tag-unpack11")
1251  << QStringLiteral(
1252  "{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}")
1253  << dict << QStringLiteral("one:1,/two:2,/") << NoError;
1254 
1255  dict.clear();
1256  list.clear();
1257  innerList.clear();
1258  innerList << QStringLiteral("one") << 1 << QStringLiteral("carrot");
1259  list.append(QVariant(innerList));
1260  innerList.clear();
1261  innerList << QStringLiteral("two") << 2;
1262  list.append(QVariant(innerList));
1263  dict.insert(QStringLiteral("items"), list);
1264 
1265  QTest::newRow("for-tag-unpack12")
1266  << QStringLiteral(
1267  "{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}")
1268  << dict << QStringLiteral("one:1,carrot/two:2,/") << NoError;
1269 
1270  dict.clear();
1271  list.clear();
1272  innerList.clear();
1273  innerList << QStringLiteral("one") << 1 << QStringLiteral("carrot");
1274  list.append(QVariant(innerList));
1275  innerList.clear();
1276  innerList << QStringLiteral("two") << 2 << QStringLiteral("cheese");
1277  list.append(QVariant(innerList));
1278 
1279  dict.insert(QStringLiteral("items"), list);
1280 
1281  QTest::newRow("for-tag-unpack13")
1282  << QStringLiteral(
1283  "{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}")
1284  << dict << QStringLiteral("one:1,carrot/two:2,cheese/") << NoError;
1285 
1286  // Empty tag:
1287 
1288  dict.clear();
1289  dict.insert(QStringLiteral("values"), QVariantList{1, 2, 3});
1290  QTest::newRow("for-tag-empty01") << QStringLiteral(
1291  "{% for val in values %}{{ val }}{% empty %}empty text{% endfor %}")
1292  << dict << QStringLiteral("123") << NoError;
1293 
1294  dict.clear();
1295  dict.insert(QStringLiteral("values"), QVariantList());
1296  QTest::newRow("for-tag-empty02")
1297  << QStringLiteral("{% for val in values %}{{ val }}{% empty %}values "
1298  "array empty{% endfor %}")
1299  << dict << QStringLiteral("values array empty") << NoError;
1300 
1301  dict.clear();
1302  QTest::newRow("for-tag-empty03")
1303  << QStringLiteral("{% for val in values %}{{ val }}{% empty %}values "
1304  "array not found{% endfor %}")
1305  << dict << QStringLiteral("values array not found") << NoError;
1306 }
1307 
1308 void TestDefaultTags::testIfEqualTag_data()
1309 {
1310  QTest::addColumn<QString>("input");
1311  QTest::addColumn<Dict>("dict");
1312  QTest::addColumn<QString>("output");
1313  QTest::addColumn<Cutelee::Error>("error");
1314 
1315  Dict dict;
1316 
1317  dict.insert(QStringLiteral("a"), 1);
1318  dict.insert(QStringLiteral("b"), 2);
1319 
1320  QTest::newRow("ifequal01")
1321  << QStringLiteral("{% ifequal a b %}yes{% endifequal %}") << dict
1322  << QString() << NoError;
1323  QTest::newRow("ifequal03")
1324  << QStringLiteral("{% ifequal a b %}yes{% else %}no{% endifequal %}")
1325  << dict << QStringLiteral("no") << NoError;
1326 
1327  dict.clear();
1328  dict.insert(QStringLiteral("a"), 1);
1329  dict.insert(QStringLiteral("b"), 1);
1330 
1331  QTest::newRow("ifequal02")
1332  << QStringLiteral("{% ifequal a b %}yes{% endifequal %}") << dict
1333  << QStringLiteral("yes") << NoError;
1334  QTest::newRow("ifequal04")
1335  << QStringLiteral("{% ifequal a b %}yes{% else %}no{% endifequal %}")
1336  << dict << QStringLiteral("yes") << NoError;
1337 
1338  dict.clear();
1339  dict.insert(QStringLiteral("a"), QStringLiteral("test"));
1340 
1341  QTest::newRow("ifequal05")
1342  << QStringLiteral("{% ifequal a 'test' %}yes{% else %}no{% endifequal %}")
1343  << dict << QStringLiteral("yes") << NoError;
1344 
1345  dict.clear();
1346  dict.insert(QStringLiteral("a"), QStringLiteral("no"));
1347 
1348  QTest::newRow("ifequal06")
1349  << QStringLiteral("{% ifequal a 'test' %}yes{% else %}no{% endifequal %}")
1350  << dict << QStringLiteral("no") << NoError;
1351 
1352  dict.clear();
1353  dict.insert(QStringLiteral("a"), QStringLiteral("test"));
1354 
1355  QTest::newRow("ifequal07")
1356  << "{% ifequal a \"test\" %}yes{% else %}no{% endifequal %}" << dict
1357  << QStringLiteral("yes") << NoError;
1358 
1359  dict.clear();
1360  dict.insert(QStringLiteral("a"), QStringLiteral("no"));
1361 
1362  QTest::newRow("ifequal08")
1363  << "{% ifequal a \"test\" %}yes{% else %}no{% endifequal %}" << dict
1364  << QStringLiteral("no") << NoError;
1365 
1366  dict.clear();
1367 
1368  QTest::newRow("ifequal09")
1369  << "{% ifequal a \"test\" %}yes{% else %}no{% endifequal %}" << dict
1370  << QStringLiteral("no") << NoError;
1371 
1372  QTest::newRow("ifequal10")
1373  << QStringLiteral("{% ifequal a b %}yes{% else %}no{% endifequal %}")
1374  << dict << QStringLiteral("yes") << NoError;
1375 
1376  QTest::newRow("ifequal-split01")
1377  << "{% ifequal a \"test man\" %}yes{% else %}no{% endifequal %}" << dict
1378  << QStringLiteral("no") << NoError;
1379 
1380  dict.insert(QStringLiteral("a"), QStringLiteral("foo"));
1381  QTest::newRow("ifequal-split02")
1382  << "{% ifequal a \"test man\" %}yes{% else %}no{% endifequal %}" << dict
1383  << QStringLiteral("no") << NoError;
1384 
1385  dict.clear();
1386  dict.insert(QStringLiteral("a"), QStringLiteral("test man"));
1387  QTest::newRow("ifequal-split03")
1388  << "{% ifequal a \"test man\" %}yes{% else %}no{% endifequal %}" << dict
1389  << QStringLiteral("yes") << NoError;
1390  QTest::newRow("ifequal-split04") << QStringLiteral(
1391  "{% ifequal a 'test man' %}yes{% else %}no{% endifequal %}")
1392  << dict << QStringLiteral("yes") << NoError;
1393 
1394  dict.clear();
1395  dict.insert(QStringLiteral("a"), QStringLiteral(""));
1396  QTest::newRow("ifequal-split05")
1397  << "{% ifequal a 'i \"love\" you' %}yes{% else %}no{% endifequal %}"
1398  << dict << QStringLiteral("no") << NoError;
1399 
1400  dict.clear();
1401  dict.insert(QStringLiteral("a"), QStringLiteral("i \"love\" you"));
1402  QTest::newRow("ifequal-split06")
1403  << "{% ifequal a 'i \"love\" you' %}yes{% else %}no{% endifequal %}"
1404  << dict << QStringLiteral("yes") << NoError;
1405 
1406  dict.clear();
1407  dict.insert(QStringLiteral("a"), QStringLiteral("i love you"));
1408  QTest::newRow("ifequal-split07")
1409  << "{% ifequal a 'i \"love\" you' %}yes{% else %}no{% endifequal %}"
1410  << dict << QStringLiteral("no") << NoError;
1411 
1412  dict.clear();
1413  dict.insert(QStringLiteral("a"), QStringLiteral("I'm happy"));
1414  QTest::newRow("ifequal-split08")
1415  << "{% ifequal a 'I\\'m happy' %}yes{% else %}no{% endifequal %}" << dict
1416  << QStringLiteral("yes") << NoError;
1417 
1418  dict.clear();
1419  dict.insert(QStringLiteral("a"), QStringLiteral("slash\\man"));
1420  QTest::newRow("ifequal-split09")
1421  << "{% ifequal a 'slash\\man' %}yes{% else %}no{% endifequal %}" << dict
1422  << QStringLiteral("yes") << NoError;
1423 
1424  dict.clear();
1425  dict.insert(QStringLiteral("a"), QStringLiteral("slashman"));
1426  QTest::newRow("ifequal-split10")
1427  << "{% ifequal a 'slash\\man' %}yes{% else %}no{% endifequal %}" << dict
1428  << QStringLiteral("no") << NoError;
1429  // NUMERIC RESOLUTION
1430 
1431  dict.clear();
1432  dict.insert(QStringLiteral("x"), QStringLiteral("5"));
1433 
1434  QTest::newRow("ifequal-numeric01")
1435  << QStringLiteral("{% ifequal x 5 %}yes{% endifequal %}") << dict
1436  << QString() << NoError;
1437 
1438  dict.clear();
1439  dict.insert(QStringLiteral("x"), 5);
1440  QTest::newRow("ifequal-numeric02")
1441  << QStringLiteral("{% ifequal x 5 %}yes{% endifequal %}") << dict
1442  << QStringLiteral("yes") << NoError;
1443 
1444  dict.clear();
1445  dict.insert(QStringLiteral("x"), 5.2);
1446  QTest::newRow("ifequal-numeric03")
1447  << QStringLiteral("{% ifequal x 5 %}yes{% endifequal %}") << dict
1448  << QString() << NoError;
1449  QTest::newRow("ifequal-numeric04")
1450  << QStringLiteral("{% ifequal x 5.2 %}yes{% endifequal %}") << dict
1451  << QStringLiteral("yes") << NoError;
1452 
1453  dict.clear();
1454  dict.insert(QStringLiteral("x"), .2);
1455 
1456  QTest::newRow("ifequal-numeric05")
1457  << QStringLiteral("{% ifequal x 0.2 %}yes{% endifequal %}") << dict
1458  << QStringLiteral("yes") << NoError;
1459  QTest::newRow("ifequal-numeric06")
1460  << QStringLiteral("{% ifequal x .2 %}yes{% endifequal %}") << dict
1461  << QStringLiteral("yes") << NoError;
1462 
1463  dict.clear();
1464  dict.insert(QStringLiteral("x"), 2);
1465 
1466  QTest::newRow("ifequal-numeric07")
1467  << QStringLiteral("{% ifequal x 2. %}yes{% endifequal %}") << dict
1468  << QString() << TagSyntaxError;
1469 
1470  dict.clear();
1471  dict.insert(QStringLiteral("x"), 5);
1472  QTest::newRow("ifequal-numeric08")
1473  << "{% ifequal x \"5\" %}yes{% endifequal %}" << dict << QString()
1474  << NoError;
1475 
1476  dict.clear();
1477  dict.insert(QStringLiteral("x"), QStringLiteral("5"));
1478  QTest::newRow("ifequal-numeric09")
1479  << "{% ifequal x \"5\" %}yes{% endifequal %}" << dict
1480  << QStringLiteral("yes") << NoError;
1481 
1482  dict.clear();
1483  dict.insert(QStringLiteral("x"), -5);
1484  QTest::newRow("ifequal-numeric10")
1485  << QStringLiteral("{% ifequal x -5 %}yes{% endifequal %}") << dict
1486  << QStringLiteral("yes") << NoError;
1487 
1488  dict.clear();
1489  dict.insert(QStringLiteral("x"), -5.2);
1490  QTest::newRow("ifequal-numeric11")
1491  << QStringLiteral("{% ifequal x -5.2 %}yes{% endifequal %}") << dict
1492  << QStringLiteral("yes") << NoError;
1493 
1494  dict.clear();
1495  dict.insert(QStringLiteral("x"), 5);
1496  QTest::newRow("ifequal-numeric12")
1497  << QStringLiteral("{% ifequal x +5 %}yes{% endifequal %}") << dict
1498  << QStringLiteral("yes") << NoError;
1499 
1500  // FILTER EXPRESSIONS AS ARGUMENTS
1501 
1502  dict.clear();
1503  dict.insert(QStringLiteral("a"), QStringLiteral("a"));
1504  QTest::newRow("ifequal-filter01")
1505  << "{% ifequal a|upper \"A\" %}x{% endifequal %}" << dict
1506  << QStringLiteral("x") << NoError;
1507 
1508  QTest::newRow("ifequal-filter02")
1509  << "{% ifequal \"A\" a|upper %}x{% endifequal %}" << dict
1510  << QStringLiteral("x") << NoError;
1511 
1512  dict.clear();
1513  dict.insert(QStringLiteral("a"), QStringLiteral("x"));
1514  dict.insert(QStringLiteral("b"), QStringLiteral("X"));
1515 
1516  QTest::newRow("ifequal-filter03")
1517  << QStringLiteral("{% ifequal a|upper b|upper %}x{% endifequal %}")
1518  << dict << QStringLiteral("x") << NoError;
1519 
1520  dict.clear();
1521  dict.insert(QStringLiteral("x"), QStringLiteral("aaa"));
1522 
1523  QTest::newRow("ifequal-filter04")
1524  << "{% ifequal x|slice:\"1\" \"a\" %}x{% endifequal %}" << dict
1525  << QStringLiteral("x") << NoError;
1526 
1527  dict.clear();
1528  dict.insert(QStringLiteral("x"), QStringLiteral("aaa"));
1529 
1530  QTest::newRow("ifequal-filter05")
1531  << "{% ifequal x|slice:\"1\"|upper \"A\" %}x{% endifequal %}" << dict
1532  << QStringLiteral("x") << NoError;
1533 
1534  QTest::newRow("ifequal-error01")
1535  << "{% ifequal x|slice:\"1\"|upper %}x{% endifequal %}" << dict
1536  << QString() << TagSyntaxError;
1537 }
1538 
1539 void TestDefaultTags::testIfNotEqualTag_data()
1540 {
1541  QTest::addColumn<QString>("input");
1542  QTest::addColumn<Dict>("dict");
1543  QTest::addColumn<QString>("output");
1544  QTest::addColumn<Cutelee::Error>("error");
1545 
1546  Dict dict;
1547 
1548  dict.insert(QStringLiteral("a"), 1);
1549  dict.insert(QStringLiteral("b"), 2);
1550 
1551  QTest::newRow("ifnotequal01")
1552  << QStringLiteral("{% ifnotequal a b %}yes{% endifnotequal %}") << dict
1553  << QStringLiteral("yes") << NoError;
1554  QTest::newRow("ifnotequal03") << QStringLiteral(
1555  "{% ifnotequal a b %}yes{% else %}no{% endifnotequal %}")
1556  << dict << QStringLiteral("yes") << NoError;
1557 
1558  dict.clear();
1559  dict.insert(QStringLiteral("a"), 1);
1560  dict.insert(QStringLiteral("b"), 1);
1561 
1562  QTest::newRow("ifnotequal02")
1563  << QStringLiteral("{% ifnotequal a b %}yes{% endifnotequal %}") << dict
1564  << QString() << NoError;
1565  QTest::newRow("ifnotequal04") << QStringLiteral(
1566  "{% ifnotequal a b %}yes{% else %}no{% endifnotequal %}")
1567  << dict << QStringLiteral("no") << NoError;
1568 }
1569 
1570 void TestDefaultTags::testTemplateTagTag_data()
1571 {
1572  QTest::addColumn<QString>("input");
1573  QTest::addColumn<Dict>("dict");
1574  QTest::addColumn<QString>("output");
1575  QTest::addColumn<Cutelee::Error>("error");
1576 
1577  Dict dict;
1578 
1579  QTest::newRow("templatetag01")
1580  << QStringLiteral("{% templatetag openblock %}") << dict
1581  << QStringLiteral("{%") << NoError;
1582  QTest::newRow("templatetag02")
1583  << QStringLiteral("{% templatetag closeblock %}") << dict
1584  << QStringLiteral("%}") << NoError;
1585  QTest::newRow("templatetag03")
1586  << QStringLiteral("{% templatetag openvariable %}") << dict
1587  << QStringLiteral("{{") << NoError;
1588  QTest::newRow("templatetag04")
1589  << QStringLiteral("{% templatetag closevariable %}") << dict
1590  << QStringLiteral("}}") << NoError;
1591  QTest::newRow("templatetag05") << QStringLiteral("{% templatetag %}") << dict
1592  << QString() << TagSyntaxError;
1593  QTest::newRow("templatetag06") << QStringLiteral("{% templatetag foo %}")
1594  << dict << QString() << TagSyntaxError;
1595  QTest::newRow("templatetag07")
1596  << QStringLiteral("{% templatetag openbrace %}") << dict
1597  << QStringLiteral("{") << NoError;
1598  QTest::newRow("templatetag08")
1599  << QStringLiteral("{% templatetag closebrace %}") << dict
1600  << QStringLiteral("}") << NoError;
1601  QTest::newRow("templatetag09") << QStringLiteral(
1602  "{% templatetag openbrace %}{% templatetag openbrace %}")
1603  << dict << QStringLiteral("{{") << NoError;
1604  QTest::newRow("templatetag10") << QStringLiteral(
1605  "{% templatetag closebrace %}{% templatetag closebrace %}")
1606  << dict << QStringLiteral("}}") << NoError;
1607  QTest::newRow("templatetag11")
1608  << QStringLiteral("{% templatetag opencomment %}") << dict
1609  << QStringLiteral("{#") << NoError;
1610  QTest::newRow("templatetag12")
1611  << QStringLiteral("{% templatetag closecomment %}") << dict
1612  << QStringLiteral("#}") << NoError;
1613 }
1614 
1615 void TestDefaultTags::testWithTag_data()
1616 {
1617  QTest::addColumn<QString>("input");
1618  QTest::addColumn<Dict>("dict");
1619  QTest::addColumn<QString>("output");
1620  QTest::addColumn<Cutelee::Error>("error");
1621 
1622  Dict dict;
1623 
1624  QVariantHash hash;
1625  hash.insert(QStringLiteral("key"), 50);
1626  dict.insert(QStringLiteral("dict"), hash);
1627  QTest::newRow("with01") << QStringLiteral(
1628  "{% with dict.key as key %}{{ key }}{% endwith %}")
1629  << dict << QStringLiteral("50") << NoError;
1630  QTest::newRow("with02") << QStringLiteral(
1631  "{{ key }}{% with dict.key as key %}{{ key }}-{{ dict.key }}-{{ key }}{% "
1632  "endwith %}{{ key }}")
1633  << dict << QStringLiteral("50-50-50") << NoError;
1634  QTest::newRow("with03") << QStringLiteral(
1635  "{{ key }}{% with key=dict.key %}{{ key }}-{{ dict.key }}-{{ key }}{% "
1636  "endwith %}{{ key }}")
1637  << dict << QStringLiteral("50-50-50") << NoError;
1638  QTest::newRow("with04") << QStringLiteral(
1639  "{{ key1 }}{% with key1=dict.key key2=dict.key key3=dict.key %}{{ key1 }}-{{ dict.key }}-{{ key3 }}{% "
1640  "endwith %}{{ key }}")
1641  << dict << QStringLiteral("50-50-50") << NoError;
1642  QTest::newRow("with-error01")
1643  << QStringLiteral("{% with dict.key xx key %}{{ key }}{% endwith %}")
1644  << dict << QString() << TagSyntaxError;
1645  QTest::newRow("with-error02")
1646  << QStringLiteral("{% with dict.key as %}{{ key }}{% endwith %}") << dict
1647  << QString() << TagSyntaxError;
1648 }
1649 
1650 void TestDefaultTags::testCycleTag_data()
1651 {
1652  QTest::addColumn<QString>("input");
1653  QTest::addColumn<Dict>("dict");
1654  QTest::addColumn<QString>("output");
1655  QTest::addColumn<Cutelee::Error>("error");
1656 
1657  Dict dict;
1658 
1659  QTest::newRow("cycle01") << QStringLiteral("{% cycle a %}") << dict
1660  << QString() << TagSyntaxError;
1661  QTest::newRow("cycle02") << QStringLiteral(
1662  "{% cycle a,b,c as abc %}{% cycle abc %}")
1663  << dict << QStringLiteral("ab") << NoError;
1664  QTest::newRow("cycle03") << QStringLiteral(
1665  "{% cycle a,b,c as abc %}{% cycle abc %}{% cycle abc %}")
1666  << dict << QStringLiteral("abc") << NoError;
1667  QTest::newRow("cycle04") << QStringLiteral(
1668  "{% cycle a,b,c as abc %}{% cycle abc %}{% cycle abc %}{% cycle abc %}")
1669  << dict << QStringLiteral("abca") << NoError;
1670  QTest::newRow("cycle05") << QStringLiteral("{% cycle a %}") << dict
1671  << QString() << TagSyntaxError;
1672  // TODO: This is the same as cycle01. Remove.
1673  QTest::newRow("cycle06") << QStringLiteral("{% cycle a %}") << dict
1674  << QString() << TagSyntaxError;
1675  QTest::newRow("cycle07") << QStringLiteral(
1676  "{% cycle a,b,c as foo %}{% cycle bar %}")
1677  << dict << QString() << TagSyntaxError;
1678  QTest::newRow("cycle08") << QStringLiteral(
1679  "{% cycle a,b,c as foo %}{% cycle foo %}{{ foo }}{{ foo }}{% cycle foo "
1680  "%}{{ foo }}") << dict
1681  << QStringLiteral("abbbcc") << NoError;
1682 
1683  dict.insert(QStringLiteral("test"), QVariantList{0, 1, 2, 3, 4});
1684  QTest::newRow("cycle09") << QStringLiteral(
1685  "{% for i in test %}{% cycle a,b %}{{ i }},{% endfor %}")
1686  << dict << QStringLiteral("a0,b1,a2,b3,a4,")
1687  << NoError;
1688 
1689  dict.clear();
1690  QTest::newRow("cycle10") << QStringLiteral(
1691  "{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}")
1692  << dict << QStringLiteral("ab") << NoError;
1693  QTest::newRow("cycle11") << QStringLiteral(
1694  "{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}{% cycle abc %}")
1695  << dict << QStringLiteral("abc") << NoError;
1696  QTest::newRow("cycle12") << QStringLiteral(
1697  "{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}{% cycle abc %}{% cycle "
1698  "abc %}") << dict << QStringLiteral("abca")
1699  << NoError;
1700 
1701  dict.insert(QStringLiteral("test"), QVariantList{0, 1, 2, 3, 4});
1702  QTest::newRow("cycle13") << QStringLiteral(
1703  "{% for i in test %}{% cycle 'a' 'b' %}{{ i }},{% endfor %}")
1704  << dict << QStringLiteral("a0,b1,a2,b3,a4,")
1705  << NoError;
1706 
1707  dict.clear();
1708  dict.insert(QStringLiteral("one"), QStringLiteral("1"));
1709  dict.insert(QStringLiteral("two"), QStringLiteral("2"));
1710  QTest::newRow("cycle14") << QStringLiteral(
1711  "{% cycle one two as foo %}{% cycle foo %}")
1712  << dict << QStringLiteral("12") << NoError;
1713 
1714  dict.clear();
1715  dict.insert(QStringLiteral("test"), QVariantList{0, 1, 2, 3, 4});
1716  dict.insert(QStringLiteral("aye"), QStringLiteral("a"));
1717  dict.insert(QStringLiteral("bee"), QStringLiteral("b"));
1718  QTest::newRow("cycle15") << QStringLiteral(
1719  "{% for i in test %}{% cycle aye bee %}{{ i }},{% endfor %}")
1720  << dict << QStringLiteral("a0,b1,a2,b3,a4,")
1721  << NoError;
1722 
1723  dict.clear();
1724  dict.insert(QStringLiteral("one"), QStringLiteral("A"));
1725  dict.insert(QStringLiteral("two"), QStringLiteral("2"));
1726  QTest::newRow("cycle16") << QStringLiteral(
1727  "{% cycle one|lower two as foo %}{% cycle foo %}")
1728  << dict << QStringLiteral("a2") << NoError;
1729 
1730  QTest::newRow("cycle17") << QStringLiteral("{% cycle %}") << dict << QString()
1731  << TagSyntaxError;
1732  QTest::newRow("cycle18") << QStringLiteral("{% cycle var %}") << dict
1733  << QString() << TagSyntaxError;
1734 
1735  dict.insert(QStringLiteral("three"), QStringLiteral("B"));
1736  dict.insert(QStringLiteral("four"), QStringLiteral("4"));
1737 
1738  QTest::newRow("cycle19") << QStringLiteral("{% cycle one two three foo %}")
1739  << dict << QStringLiteral("A") << NoError;
1740  QTest::newRow("cycle20") << QStringLiteral(
1741  "{% cycle one two as foo %}{% cycle three four as bar %}")
1742  << dict << QStringLiteral("AB") << NoError;
1743 }
1744 
1745 void TestDefaultTags::testWidthRatioTag_data()
1746 {
1747  QTest::addColumn<QString>("input");
1748  QTest::addColumn<Dict>("dict");
1749  QTest::addColumn<QString>("output");
1750  QTest::addColumn<Cutelee::Error>("error");
1751 
1752  Dict dict;
1753 
1754  dict.insert(QStringLiteral("a"), 50);
1755  dict.insert(QStringLiteral("b"), 100);
1756  QTest::newRow("widthratio01") << QStringLiteral("{% widthratio a b 0 %}")
1757  << dict << QStringLiteral("0") << NoError;
1758 
1759  dict.clear();
1760  dict.insert(QStringLiteral("a"), 0);
1761  dict.insert(QStringLiteral("b"), 0);
1762  QTest::newRow("widthratio02") << QStringLiteral("{% widthratio a b 0 %}")
1763  << dict << QString() << NoError;
1764 
1765  dict.clear();
1766  dict.insert(QStringLiteral("a"), 0);
1767  dict.insert(QStringLiteral("b"), 100);
1768  QTest::newRow("widthratio03") << QStringLiteral("{% widthratio a b 100 %}")
1769  << dict << QStringLiteral("0") << NoError;
1770 
1771  dict.clear();
1772  dict.insert(QStringLiteral("a"), 50);
1773  dict.insert(QStringLiteral("b"), 100);
1774  QTest::newRow("widthratio04") << QStringLiteral("{% widthratio a b 100 %}")
1775  << dict << QStringLiteral("50") << NoError;
1776 
1777  dict.clear();
1778  dict.insert(QStringLiteral("a"), 100);
1779  dict.insert(QStringLiteral("b"), 100);
1780  QTest::newRow("widthratio05") << QStringLiteral("{% widthratio a b 100 %}")
1781  << dict << QStringLiteral("100") << NoError;
1782 
1783  dict.clear();
1784  dict.insert(QStringLiteral("a"), 50);
1785  dict.insert(QStringLiteral("b"), 80);
1786  QTest::newRow("widthratio06") << QStringLiteral("{% widthratio a b 100 %}")
1787  << dict << QStringLiteral("63") << NoError;
1788 
1789  dict.clear();
1790  dict.insert(QStringLiteral("a"), 50);
1791  dict.insert(QStringLiteral("b"), 70);
1792  QTest::newRow("widthratio07") << QStringLiteral("{% widthratio a b 100 %}")
1793  << dict << QStringLiteral("71") << NoError;
1794 
1795  dict.clear();
1796  // Raise exception if we don't have 3 args, last one an integer
1797  QTest::newRow("widthratio08") << QStringLiteral("{% widthratio %}") << dict
1798  << QString() << TagSyntaxError;
1799 
1800  dict.clear();
1801  QTest::newRow("widthratio09") << QStringLiteral("{% widthratio a b %}")
1802  << dict << QString() << TagSyntaxError;
1803 
1804  dict.clear();
1805  dict.insert(QStringLiteral("a"), 50);
1806  dict.insert(QStringLiteral("b"), 100);
1807  QTest::newRow("widthratio10") << QStringLiteral("{% widthratio a b 100.0 %}")
1808  << dict << QStringLiteral("50") << NoError;
1809 
1810  dict.clear();
1811  dict.insert(QStringLiteral("a"), 50);
1812  dict.insert(QStringLiteral("b"), 100);
1813  dict.insert(QStringLiteral("c"), 100);
1814  QTest::newRow("widthratio11") << QStringLiteral("{% widthratio a b c %}")
1815  << dict << QStringLiteral("50") << NoError;
1816 }
1817 
1818 void TestDefaultTags::testFilterTag_data()
1819 {
1820  QTest::addColumn<QString>("input");
1821  QTest::addColumn<Dict>("dict");
1822  QTest::addColumn<QString>("output");
1823  QTest::addColumn<Cutelee::Error>("error");
1824 
1825  Dict dict;
1826 
1827  QTest::newRow("filter01")
1828  << QStringLiteral("{% filter upper %}{% endfilter %}") << dict
1829  << QString() << NoError;
1830  QTest::newRow("filter02")
1831  << QStringLiteral("{% filter upper %}django{% endfilter %}") << dict
1832  << QStringLiteral("DJANGO") << NoError;
1833  QTest::newRow("filter03")
1834  << QStringLiteral("{% filter upper|lower %}django{% endfilter %}") << dict
1835  << QStringLiteral("django") << NoError;
1836 
1837  dict.insert(QStringLiteral("remove"), QStringLiteral("spam"));
1838  QTest::newRow("filter04")
1839  << QStringLiteral("{% filter cut:remove %}djangospam{% endfilter %}")
1840  << dict << QStringLiteral("django") << NoError;
1841 }
1842 
1843 void TestDefaultTags::testNowTag_data()
1844 {
1845  QTest::addColumn<QString>("input");
1846  QTest::addColumn<Dict>("dict");
1847  QTest::addColumn<QString>("output");
1848  QTest::addColumn<Cutelee::Error>("error");
1849 
1850  Dict dict;
1851 
1852  auto today = QDateTime::currentDateTime().date();
1853 
1854  QTest::newRow("now01") << QStringLiteral("{% now \"d M yyyy\"%}") << dict
1855  << (QString::number(today.day()) + QLatin1Char(' ')
1856  + QString::number(today.month()) + QLatin1Char(' ')
1857  + QString::number(today.year()))
1858  << NoError;
1859 
1860  QTest::newRow("now02") << QStringLiteral("{% now \"d \"M\" yyyy\"%}") << dict
1861  << QString() << TagSyntaxError;
1862 }
1863 
1864 void TestDefaultTags::testSpacelessTag_data()
1865 {
1866  QTest::addColumn<QString>("input");
1867  QTest::addColumn<Dict>("dict");
1868  QTest::addColumn<QString>("output");
1869  QTest::addColumn<Cutelee::Error>("error");
1870 
1871  Dict dict;
1872 
1873  QTest::newRow("spaceless01")
1874  << QStringLiteral(
1875  "{% spaceless %} <b> <i> text </i> </b> {% endspaceless %}")
1876  << dict << QStringLiteral("<b><i> text </i></b>") << NoError;
1877  QTest::newRow("spaceless02")
1878  << "{% spaceless %} <b> \n <i> text </i> \n </b> {% endspaceless %}"
1879  << dict << QStringLiteral("<b><i> text </i></b>") << NoError;
1880  QTest::newRow("spaceless03")
1881  << QStringLiteral("{% spaceless %}<b><i>text</i></b>{% endspaceless %}")
1882  << dict << QStringLiteral("<b><i>text</i></b>") << NoError;
1883 
1884  dict.insert(QStringLiteral("text"), QStringLiteral("This & that"));
1885  QTest::newRow("spaceless04")
1886  << QStringLiteral(
1887  "{% spaceless %}<b> <i>{{ text }}</i> </b>{% endspaceless %}")
1888  << dict << QStringLiteral("<b><i>This &amp; that</i></b>") << NoError;
1889  QTest::newRow("spaceless05")
1890  << QStringLiteral("{% autoescape off %}{% spaceless %}<b> <i>{{ text "
1891  "}}</i> </b>{% endspaceless %}{% endautoescape %}")
1892  << dict << QStringLiteral("<b><i>This & that</i></b>") << NoError;
1893  QTest::newRow("spaceless06") << QStringLiteral(
1894  "{% spaceless %}<b> <i>{{ text|safe }}</i> </b>{% endspaceless %}")
1895  << dict
1896  << QStringLiteral("<b><i>This & that</i></b>")
1897  << NoError;
1898 }
1899 
1900 void TestDefaultTags::testRegroupTag_data()
1901 {
1902  QTest::addColumn<QString>("input");
1903  QTest::addColumn<Dict>("dict");
1904  QTest::addColumn<QString>("output");
1905  QTest::addColumn<Cutelee::Error>("error");
1906 
1907  Dict dict;
1908 
1909  QVariantList list;
1910  QVariantHash hash;
1911 
1912  hash.insert(QStringLiteral("foo"), QStringLiteral("c"));
1913  hash.insert(QStringLiteral("bar"), 1);
1914  list.append(hash);
1915 
1916  hash.clear();
1917  hash.insert(QStringLiteral("foo"), QStringLiteral("d"));
1918  hash.insert(QStringLiteral("bar"), 1);
1919  list.append(hash);
1920 
1921  hash.clear();
1922  hash.insert(QStringLiteral("foo"), QStringLiteral("a"));
1923  hash.insert(QStringLiteral("bar"), 2);
1924  list.append(hash);
1925 
1926  hash.clear();
1927  hash.insert(QStringLiteral("foo"), QStringLiteral("b"));
1928  hash.insert(QStringLiteral("bar"), 2);
1929  list.append(hash);
1930 
1931  hash.clear();
1932  hash.insert(QStringLiteral("foo"), QStringLiteral("x"));
1933  hash.insert(QStringLiteral("bar"), 3);
1934  list.append(hash);
1935 
1936  dict.insert(QStringLiteral("data"), list);
1937 
1938  QTest::newRow("regroup01")
1939  << QString::fromLatin1("{% regroup data by bar as grouped %}"
1940  "{% for group in grouped %}"
1941  "{{ group.grouper }}:"
1942  "{% for item in group.list %}"
1943  "{{ item.foo }}"
1944  "{% endfor %},"
1945  "{% endfor %}")
1946  << dict << QStringLiteral("1:cd,2:ab,3:x,") << NoError;
1947 
1948  dict.clear();
1949  hash.clear();
1950  list.clear();
1951 
1952  hash.insert(QStringLiteral("foo"), QStringLiteral("a"));
1953  hash.insert(QStringLiteral("bar"), 2);
1954  list.append(hash);
1955 
1956  hash.clear();
1957  hash.insert(QStringLiteral("foo"), QStringLiteral("b"));
1958  hash.insert(QStringLiteral("bar"), 2);
1959  list.append(hash);
1960 
1961  hash.clear();
1962  hash.insert(QStringLiteral("foo"), QStringLiteral("x"));
1963  hash.insert(QStringLiteral("bar"), 3);
1964  list.append(hash);
1965 
1966  hash.clear();
1967  hash.insert(QStringLiteral("foo"), QStringLiteral("c"));
1968  hash.insert(QStringLiteral("bar"), 1);
1969  list.append(hash);
1970 
1971  hash.clear();
1972  hash.insert(QStringLiteral("foo"), QStringLiteral("d"));
1973  hash.insert(QStringLiteral("bar"), 1);
1974  list.append(hash);
1975 
1976  dict.insert(QStringLiteral("data"), list);
1977 
1978  // Data is output in the order it is sent in.
1979 
1980  QTest::newRow("regroup02")
1981  << QString::fromLatin1("{% regroup data by bar as grouped %}"
1982  "{% for group in grouped %}"
1983  "{{ group.grouper }}:"
1984  "{% for item in group.list %}"
1985  "{{ item.foo }}"
1986  "{% endfor %},"
1987  "{% endfor %}")
1988  << dict << QStringLiteral("2:ab,3:x,1:cd,") << NoError;
1989 
1990  dict.clear();
1991  hash.clear();
1992  list.clear();
1993 
1994  Table table;
1995 
1996  QVariantList row;
1997  row.append(1);
1998  row.append(QStringLiteral("a"));
1999  table.append(row);
2000 
2001  row.clear();
2002  row.append(1);
2003  row.append(QStringLiteral("b"));
2004  table.append(row);
2005 
2006  row.clear();
2007  row.append(2);
2008  row.append(QStringLiteral("a"));
2009  table.append(row);
2010 
2011  row.clear();
2012  row.append(3);
2013  row.append(QStringLiteral("c"));
2014  table.append(row);
2015 
2016  row.clear();
2017  row.append(3);
2018  row.append(QStringLiteral("d"));
2019  table.append(row);
2020 
2021  dict.insert(QStringLiteral("data"), QVariant::fromValue(table));
2022 
2023  QTest::newRow("regroup03")
2024  << QString::fromLatin1("{% regroup data by 0 as grouped %}"
2025  "{% for group in grouped %}"
2026  "{{ group.grouper }}:"
2027  "{% for item in group.list %}"
2028  "{{ item.1 }}"
2029  "{% endfor %},"
2030  "{% endfor %}")
2031  << dict << QStringLiteral("1:ab,2:a,3:cd,") << NoError;
2032 }
2033 
2034 void TestDefaultTags::testIfChangedTag_data()
2035 {
2036  QTest::addColumn<QString>("input");
2037  QTest::addColumn<Dict>("dict");
2038  QTest::addColumn<QString>("output");
2039  QTest::addColumn<Cutelee::Error>("error");
2040 
2041  Dict dict;
2042 
2043  dict.insert(QStringLiteral("num"), QVariantList{1, 2, 3});
2044  QTest::newRow("ifchanged01") << QStringLiteral(
2045  "{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}")
2046  << dict << QStringLiteral("123") << NoError;
2047 
2048  dict.clear();
2049  dict.insert(QStringLiteral("num"), QVariantList{1, 1, 3});
2050  QTest::newRow("ifchanged02") << QStringLiteral(
2051  "{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}")
2052  << dict << QStringLiteral("13") << NoError;
2053 
2054  dict.clear();
2055  dict.insert(QStringLiteral("num"), QVariantList{1, 1, 1});
2056  QTest::newRow("ifchanged03") << QStringLiteral(
2057  "{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}")
2058  << dict << QStringLiteral("1") << NoError;
2059 
2060  dict.clear();
2061  dict.insert(QStringLiteral("num"), QVariantList{1, 2, 3});
2062  dict.insert(QStringLiteral("numx"), QVariantList{2, 2, 2});
2063  QTest::newRow("ifchanged04") << QStringLiteral(
2064  "{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in "
2065  "numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}")
2066  << dict << QStringLiteral("122232") << NoError;
2067 
2068  dict.clear();
2069  dict.insert(QStringLiteral("num"), QVariantList{1, 1, 1});
2070  dict.insert(QStringLiteral("numx"), QVariantList{1, 2, 3});
2071  QTest::newRow("ifchanged05") << QStringLiteral(
2072  "{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in "
2073  "numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}")
2074  << dict << QStringLiteral("1123123123")
2075  << NoError;
2076 
2077  dict.clear();
2078  dict.insert(QStringLiteral("num"), QVariantList{1, 1, 1});
2079  dict.insert(QStringLiteral("numx"), QVariantList{2, 2, 2});
2080  QTest::newRow("ifchanged06") << QStringLiteral(
2081  "{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in "
2082  "numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}")
2083  << dict << QStringLiteral("1222") << NoError;
2084 
2085  dict.clear();
2086  dict.insert(QStringLiteral("num"), QVariantList{1, 1, 1});
2087  dict.insert(QStringLiteral("numx"), QVariantList{2, 2, 2});
2088  dict.insert(QStringLiteral("numy"), QVariantList{3, 3, 3});
2089  QTest::newRow("ifchanged07") << QStringLiteral(
2090  "{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in "
2091  "numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% for y in numy %}{% "
2092  "ifchanged %}{{ y }}{% endifchanged %}{% endfor %}{% endfor %}{% endfor "
2093  "%}") << dict << QStringLiteral("1233323332333")
2094  << NoError;
2095  QTest::newRow("ifchanged08")
2096  << QStringLiteral("{% ifchanged %}{{ num.0 }}{% endifchanged %}") << dict
2097  << QStringLiteral("1") << NoError;
2098 
2099  // datalist': [[(1, 'a'), (1, 'a'), (0, 'b'), (1, 'c')], [(0, 'a'), (1,
2100  // 'c'),
2101  // (1, 'd'), (1, 'd'), (0, 'e')]]}
2102  dict.clear();
2103  QVariantList list;
2104  QVariantList innerList;
2105  QVariantList tuple{1, QStringLiteral("a")};
2106  innerList.append(QVariant(tuple));
2107  tuple = {1, QStringLiteral("a")};
2108  innerList.append(QVariant(tuple));
2109  tuple = {0, QStringLiteral("b")};
2110  innerList.append(QVariant(tuple));
2111  tuple = {1, QStringLiteral("c")};
2112  innerList.append(QVariant(tuple));
2113  list.append(QVariant(innerList));
2114  innerList.clear();
2115 
2116  tuple = {0, QStringLiteral("a")};
2117  innerList.append(QVariant(tuple));
2118  tuple = {1, QStringLiteral("c")};
2119  innerList.append(QVariant(tuple));
2120  tuple = {1, QStringLiteral("d")};
2121  innerList.append(QVariant(tuple));
2122  tuple = {1, QStringLiteral("d")};
2123  innerList.append(QVariant(tuple));
2124  tuple = {0, QStringLiteral("e")};
2125  innerList.append(QVariant(tuple));
2126  list.append(QVariant(innerList));
2127  innerList.clear();
2128 
2129  dict.insert(QStringLiteral("datalist"), list);
2130  QTest::newRow("ifchanged08") << QStringLiteral(
2131  "{% for data in datalist %}{% for c,d in data %}{% if c %}{% ifchanged "
2132  "%}{{ d }}{% endifchanged %}{% endif %}{% endfor %}{% endfor %}")
2133  << dict << QStringLiteral("accd") << NoError;
2134 
2135  // Test one parameter given to ifchanged.
2136  dict.clear();
2137  dict.insert(QStringLiteral("num"), QVariantList{1, 2, 3});
2138  QTest::newRow("ifchanged-param01")
2139  << QStringLiteral("{% for n in num %}{% ifchanged n %}..{% endifchanged "
2140  "%}{{ n }}{% endfor %}")
2141  << dict << QStringLiteral("..1..2..3") << NoError;
2142 
2143  dict.clear();
2144  dict.insert(QStringLiteral("num"), QVariantList{1, 2, 3});
2145  dict.insert(QStringLiteral("numx"), QVariantList{5, 6, 7});
2146  QTest::newRow("ifchanged-param02")
2147  << QStringLiteral("{% for n in num %}{% for x in numx %}{% ifchanged n "
2148  "%}..{% endifchanged %}{{ x }}{% endfor %}{% endfor %}")
2149  << dict << QStringLiteral("..567..567..567") << NoError;
2150 
2151  // Test multiple parameters to ifchanged.
2152 
2153  dict.clear();
2154  dict.insert(QStringLiteral("num"), QVariantList{1, 1, 2});
2155  dict.insert(QStringLiteral("numx"), QVariantList{5, 6, 6});
2156  QTest::newRow("ifchanged-param03")
2157  << QStringLiteral(
2158  "{% for n in num %}{{ n }}{% for x in numx %}{% ifchanged x n "
2159  "%}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}")
2160  << dict << QStringLiteral("156156256") << NoError;
2161 
2162  // Test a date+hour like construct, where the hour of the last day
2163  // is the same but the date had changed, so print the hour anyway.
2164 
2165  dict.clear();
2166  QVariantList days;
2167  QVariantHash hash;
2168  hash.insert(QStringLiteral("day"), 1);
2169  hash.insert(QStringLiteral("hours"), QVariantList{1, 2, 3});
2170  days << hash;
2171  hash.clear();
2172  hash.insert(QStringLiteral("day"), 2);
2173  hash.insert(QStringLiteral("hours"), QVariantList{3});
2174  days << hash;
2175  dict.insert(QStringLiteral("days"), days);
2176  QTest::newRow("ifchanged-param04")
2177  << QStringLiteral("{% for d in days %}{% ifchanged %}{{ d.day }}{% "
2178  "endifchanged %}{% for h in d.hours %}{% ifchanged d h "
2179  "%}{{ h }}{% endifchanged %}{% endfor %}{% endfor %}")
2180  << dict << QStringLiteral("112323") << NoError;
2181 
2182  // Logically the same as above, just written with explicit
2183  // ifchanged for the day.
2184 
2185  QTest::newRow("ifchanged-param05") << QStringLiteral(
2186  "{% for d in days %}{% ifchanged d.day %}{{ d.day }}{% endifchanged %}{% "
2187  "for h in d.hours %}{% ifchanged d.day h %}{{ h }}{% endifchanged %}{% "
2188  "endfor %}{% endfor %}") << dict
2189  << QStringLiteral("112323") << NoError;
2190 
2191  // Test the else clause of ifchanged.
2192  dict.clear();
2193  dict.insert(QStringLiteral("ids"), QVariantList{1, 1, 2, 2, 2, 3});
2194  QTest::newRow("ifchanged-else01")
2195  << QStringLiteral("{% for id in ids %}{{ id }}{% ifchanged id %}-first{% "
2196  "else %}-other{% endifchanged %},{% endfor %}")
2197  << dict
2198  << QStringLiteral("1-first,1-other,2-first,2-other,2-other,3-first,")
2199  << NoError;
2200  QTest::newRow("ifchanged-else02")
2201  << QStringLiteral(
2202  "{% for id in ids %}{{ id }}-{% ifchanged id %}{% cycle red,blue "
2203  "%}{% else %}grey{% endifchanged %},{% endfor %}")
2204  << dict << QStringLiteral("1-red,1-grey,2-blue,2-grey,2-grey,3-red,")
2205  << NoError;
2206  QTest::newRow("ifchanged-else03")
2207  << QStringLiteral(
2208  "{% for id in ids %}{{ id }}{% ifchanged id %}-{% cycle red,blue "
2209  "%}{% else %}{% endifchanged %},{% endfor %}")
2210  << dict << QStringLiteral("1-red,1,2-blue,2,2,3-red,") << NoError;
2211 
2212  dict.clear();
2213  dict.insert(QStringLiteral("ids"), QVariantList{1, 1, 2, 2, 2, 3, 4});
2214  QTest::newRow("ifchanged-else04")
2215  << QStringLiteral(
2216  "{% for id in ids %}{% ifchanged %}***{{ id }}*{% else %}...{% "
2217  "endifchanged %}{{ forloop.counter }}{% endfor %}")
2218  << dict << QStringLiteral("***1*1...2***2*3...4...5***3*6***4*7")
2219  << NoError;
2220 }
2221 
2222 void TestDefaultTags::testAutoescapeTag_data()
2223 {
2224  QTest::addColumn<QString>("input");
2225  QTest::addColumn<Dict>("dict");
2226  QTest::addColumn<QString>("output");
2227  QTest::addColumn<Cutelee::Error>("error");
2228 
2229  Dict dict;
2230 
2231  QTest::newRow("autoescape-tag01")
2232  << QStringLiteral("{% autoescape off %}hello{% endautoescape %}") << dict
2233  << QStringLiteral("hello") << NoError;
2234 
2235  dict.insert(QStringLiteral("first"), QStringLiteral("<b>hello</b>"));
2236  QTest::newRow("autoescape-tag02")
2237  << QStringLiteral("{% autoescape off %}{{ first }}{% endautoescape %}")
2238  << dict << QStringLiteral("<b>hello</b>") << NoError;
2239  QTest::newRow("autoescape-tag03")
2240  << QStringLiteral("{% autoescape on %}{{ first }}{% endautoescape %}")
2241  << dict << QStringLiteral("&lt;b&gt;hello&lt;/b&gt;") << NoError;
2242  // Autoescape disabling and enabling nest in a predictable way.
2243  dict.insert(QStringLiteral("first"), QStringLiteral("<a>"));
2244  QTest::newRow("autoescape-tag04")
2245  << QStringLiteral("{% autoescape off %}{{ first }} {% autoescape on%}{{ "
2246  "first }}{% endautoescape %}{% endautoescape %}")
2247  << dict << QStringLiteral("<a> &lt;a&gt;") << NoError;
2248 
2249  dict.insert(QStringLiteral("first"), QStringLiteral("<b>first</b>"));
2250  QTest::newRow("autoescape-tag05")
2251  << QStringLiteral("{% autoescape on %}{{ first }}{% endautoescape %}")
2252  << dict << QStringLiteral("&lt;b&gt;first&lt;/b&gt;") << NoError;
2253  // Strings (ASCII or unicode) already marked as "safe" are not
2254  // auto-escaped
2255  SafeString safeString(QStringLiteral("<b>first</b>"));
2256  auto safeStringVar = QVariant::fromValue<SafeString>(markSafe(safeString));
2257  dict.insert(QStringLiteral("first"), safeStringVar);
2258 
2259  QTest::newRow("autoescape-tag06")
2260  << QStringLiteral("{{ first }}") << dict << QStringLiteral("<b>first</b>")
2261  << NoError;
2262  QTest::newRow("autoescape-tag07")
2263  << QStringLiteral("{% autoescape on %}{{ first }}{% endautoescape %}")
2264  << dict << QStringLiteral("<b>first</b>") << NoError;
2265 
2266  // Literal string arguments to filters, if used in the result, are
2267  // safe.
2268  dict.clear();
2269  dict.insert(QStringLiteral("var"), QVariant());
2270  QTest::newRow("autoescape-tag08")
2271  << "{% autoescape on %}{{ var|default_if_none:\"endquote\\\" hah\" "
2272  "}}{% "
2273  "endautoescape %}"
2274  << dict << "endquote\" hah" << NoError;
2275 
2276  QTest::newRow("autoescape-tag09")
2277  << "{% autoescape on extra %}{{ var|default_if_none:\"endquote\\\" "
2278  "hah\" "
2279  "}}{% endautoescape %}"
2280  << dict << "" << TagSyntaxError;
2281  QTest::newRow("autoescape-tag10")
2282  << "{% autoescape bad %}{{ var|default_if_none:\"endquote\\\" hah\" "
2283  "}}{% "
2284  "endautoescape %}"
2285  << dict << "" << TagSyntaxError;
2286 
2287  // Objects which return safe strings as their __unicode__ method
2288  // won't get double-escaped.
2289  // 'autoescape-tag09': (r'{{ unsafe }}', {'unsafe':
2290  // filters.UnsafeClass()},
2291  // 'you &amp; me'),
2292  // 'autoescape-tag10': (r'{{ safe }}', {'safe': filters.SafeClass()}, 'you
2293  // &gt; me'),
2294  // The "safe" and "escape" filters cannot work due to internal
2295  // implementation details (fortunately, the (no)autoescape block
2296  // tags can be used in those cases)
2297  dict.clear();
2298  dict.insert(QStringLiteral("first"), QStringLiteral("<a>"));
2299  QTest::newRow("autoescape-filtertag01")
2300  << QStringLiteral(
2301  "{{ first }}{% filter safe %}{{ first }} x<y{% endfilter %}")
2302  << dict << QString() << TagSyntaxError;
2303  QTest::newRow("autoescape-filtertag02")
2304  << QStringLiteral(
2305  "{{ first }}{% filter escape %}{{ first }} x<y{% endfilter %}")
2306  << dict << QString() << TagSyntaxError;
2307 }
2308 
2309 void TestDefaultTags::testMediaFinderTag_data()
2310 {
2311  QTest::addColumn<QString>("input");
2312  QTest::addColumn<Dict>("dict");
2313  QTest::addColumn<QString>("output");
2314  QTest::addColumn<Cutelee::Error>("error");
2315 
2316  Dict dict;
2317  QTest::newRow("media_finder-tag01")
2318  << "{% media_finder \"existing_image.png\" %}" << dict
2319  << QStringLiteral("file:///path/to/existing_image.png") << NoError;
2320  QTest::newRow("media_finder-tag02")
2321  << "{% media_finder \"does_not_exist.png\" %}" << dict << QString()
2322  << NoError;
2323  QTest::newRow("media_finder-tag03")
2324  << "{% media_finder \"existing_image.png\" \"does_not_exist.png\" %}"
2325  << dict << QStringLiteral("file:///path/to/existing_image.png")
2326  << NoError;
2327 
2328  dict.insert(QStringLiteral("existing_img"),
2329  QStringLiteral("existing_image.png"));
2330  dict.insert(QStringLiteral("nonexisting_img"),
2331  QStringLiteral("does_not_exist.png"));
2332 
2333  QTest::newRow("media_finder-tag04") << QStringLiteral("{% media_finder %}")
2334  << dict << QString() << TagSyntaxError;
2335  QTest::newRow("media_finder-tag05")
2336  << QStringLiteral("{% media_finder existing_img %}") << dict
2337  << QStringLiteral("file:///path/to/existing_image.png") << NoError;
2338  QTest::newRow("media_finder-tag06")
2339  << QStringLiteral("{% media_finder nonexisting_img %}") << dict
2340  << QString() << NoError;
2341  QTest::newRow("media_finder-tag07")
2342  << "{% media_finder \"does_not_exist.png\" existing_img %}" << dict
2343  << QStringLiteral("file:///path/to/existing_image.png") << NoError;
2344  QTest::newRow("media_finder-tag08")
2345  << QStringLiteral("{% media_finder nonexisting_img existing_img %}")
2346  << dict << QStringLiteral("file:///path/to/existing_image.png")
2347  << NoError;
2348  QTest::newRow("media_finder-tag09")
2349  << "{% media_finder \"existing_image.png\" "
2350  "\"another_existing_image.png\" %}"
2351  << dict << QStringLiteral("file:///path/to/existing_image.png")
2352  << NoError;
2353  QTest::newRow("media_finder-tag10")
2354  << "{% media_finder \"another_existing_image.png\" "
2355  "\"existing_image.png\" %}"
2356  << dict << QStringLiteral("file:///path/to/another_existing_image.png")
2357  << NoError;
2358 
2359  dict.insert(QStringLiteral("this_and_that_img"),
2360  QStringLiteral("this&that.png"));
2361 
2362  QTest::newRow("media_finder-tag11")
2363  << "{% media_finder \"this&that.png\" %}" << dict
2364  << QStringLiteral("file:///path/to/this&amp;that.png") << NoError;
2365  QTest::newRow("media_finder-tag12")
2366  << "{% media_finder this_and_that_img %}" << dict
2367  << QStringLiteral("file:///path/to/this&amp;that.png") << NoError;
2368  QTest::newRow("media_finder-tag13")
2369  << "{% autoescape off %}{% media_finder \"this&that.png\" %}{% "
2370  "endautoescape %}"
2371  << dict << QStringLiteral("file:///path/to/this&that.png") << NoError;
2372  QTest::newRow("media_finder-tag14")
2373  << "{% autoescape off %}{% media_finder this_and_that_img %}{% "
2374  "endautoescape %}"
2375  << dict << QStringLiteral("file:///path/to/this&that.png") << NoError;
2376 }
2377 
2378 void TestDefaultTags::testRangeTag_data()
2379 {
2380  QTest::addColumn<QString>("input");
2381  QTest::addColumn<Dict>("dict");
2382  QTest::addColumn<QString>("output");
2383  QTest::addColumn<Cutelee::Error>("error");
2384 
2385  Dict dict;
2386 
2387  QTest::newRow("range-tag01")
2388  << QStringLiteral("{% range 5 as i %}{{ i }};{% endrange %}") << dict
2389  << QStringLiteral("0;1;2;3;4;") << NoError;
2390  QTest::newRow("range-tag02")
2391  << QStringLiteral("{% range 1 6 as i %}{{ i }};{% endrange %}") << dict
2392  << QStringLiteral("1;2;3;4;5;") << NoError;
2393  QTest::newRow("range-tag03")
2394  << QStringLiteral("{% range 5 26 5 as i %}{{ i }};{% endrange %}") << dict
2395  << QStringLiteral("5;10;15;20;25;") << NoError;
2396 
2397  QVariantList list{10, 15, 2};
2398  dict.insert(QStringLiteral("values"), list);
2399 
2400  QTest::newRow("range-tag04") << QStringLiteral(
2401  "{% range values.0 values.1 values.2 as i %}{{ i }};{% endrange %}")
2402  << dict << QStringLiteral("10;12;14;")
2403  << NoError;
2404 
2405  QTest::newRow("range-tag05")
2406  << QStringLiteral("{% range 5 %}Foo;{% endrange %}") << dict
2407  << QStringLiteral("Foo;Foo;Foo;Foo;Foo;") << NoError;
2408  QTest::newRow("range-tag06")
2409  << QStringLiteral("{% range 5 6 %}Foo;{% endrange %}") << dict
2410  << QString() << TagSyntaxError;
2411  QTest::newRow("range-tag07")
2412  << QStringLiteral("{% range 5 6 7 %}Foo;{% endrange %}") << dict
2413  << QString() << TagSyntaxError;
2414 }
2415 
2416 void TestDefaultTags::testDebugTag_data()
2417 {
2418 
2419  QTest::addColumn<QString>("input");
2420  QTest::addColumn<Dict>("dict");
2421  QTest::addColumn<QString>("output");
2422  QTest::addColumn<Cutelee::Error>("error");
2423 
2424  Dict dict;
2425 
2426  QTest::newRow("debug-tag01")
2427  << QStringLiteral("{% debug %}") << dict
2428  << QStringLiteral("\n\nContext:\nEnd context:\n\n") << NoError;
2429  dict.insert(QStringLiteral("answer"), 42);
2430  QTest::newRow("debug-tag02")
2431  << QStringLiteral("{% debug %}") << dict
2432  << QStringLiteral("\n\nContext:\nkey answer, type int\nEnd context:\n\n")
2433  << NoError;
2434 }
2435 
2436 void TestDefaultTags::testLoadTag_data()
2437 {
2438 
2439  QTest::addColumn<QString>("input");
2440  QTest::addColumn<Dict>("dict");
2441  QTest::addColumn<QString>("output");
2442  QTest::addColumn<Cutelee::Error>("error");
2443 
2444  Dict dict;
2445 
2446  QTest::newRow("load-tag01") << QStringLiteral("{% load does_not_exist %}foo")
2447  << dict << QString() << TagSyntaxError;
2448 }
2449 
2450 void TestDefaultTags::testUrlTypes_data()
2451 {
2452  QTest::addColumn<QString>("input");
2453  QTest::addColumn<Dict>("dict");
2454  QTest::addColumn<QPair<QString, QString>>("output");
2455 
2456  Dict dict;
2457  QTest::newRow("url-types01")
2458  << "{% media_finder \"existing_image.png\" %}" << dict
2459  << qMakePair(QStringLiteral("file:///path/to/"),
2460  QStringLiteral("existing_image.png"));
2461 
2462  QTest::newRow("url-types02") << "{% media_finder \"does_not_exist.png\" %}"
2463  << dict << qMakePair(QString(), QString());
2464 
2465  dict.insert(QStringLiteral("existing_img"),
2466  QStringLiteral("existing_image.png"));
2467  dict.insert(QStringLiteral("nonexisting_img"),
2468  QStringLiteral("does_not_exist.png"));
2469 
2470  QTest::newRow("url-types03")
2471  << QStringLiteral("{% media_finder existing_img %}") << dict
2472  << qMakePair(QStringLiteral("file:///path/to/"),
2473  QStringLiteral("existing_image.png"));
2474 
2475  QTest::newRow("url-types04")
2476  << QStringLiteral("{% media_finder nonexisting_img %}") << dict
2477  << qMakePair(QString(), QString());
2478 }
2479 
2480 void TestDefaultTags::testUrlTypes()
2481 {
2482  QFETCH(QString, input);
2483  QFETCH(Dict, dict);
2484  QFETCH(StringPair, output);
2485 
2486  auto t = m_engine->newTemplate(input, QLatin1String(QTest::currentDataTag()));
2487  QVERIFY(t->error() == NoError);
2488  Context c(dict);
2489  auto result = t->render(&c);
2490  QVERIFY(t->error() == NoError);
2491  QVERIFY(result == output.first + output.second);
2492 
2493  c.setUrlType(Context::RelativeUrls);
2494  result = t->render(&c);
2495  QVERIFY(t->error() == NoError);
2496  QVERIFY(result == output.second);
2497 }
2498 
2499 void TestDefaultTags::testRelativePaths_data()
2500 {
2501  QTest::addColumn<QString>("input");
2502  QTest::addColumn<Dict>("dict");
2503  QTest::addColumn<QString>("output");
2504 
2505  Dict dict;
2506  QTest::newRow("relativepaths01")
2507  << "{% media_finder \"existing_image.png\" %}" << dict
2508  << QStringLiteral("existing_image.png");
2509 
2510  QTest::newRow("relativepaths02")
2511  << "{% media_finder \"does_not_exist.png\" %}" << dict << QString();
2512 
2513  dict.insert(QStringLiteral("existing_img"),
2514  QStringLiteral("existing_image.png"));
2515  dict.insert(QStringLiteral("nonexisting_img"),
2516  QStringLiteral("does_not_exist.png"));
2517 
2518  QTest::newRow("relativepaths03")
2519  << QStringLiteral("{% media_finder existing_img %}") << dict
2520  << QStringLiteral("existing_image.png");
2521 
2522  QTest::newRow("relativepaths04")
2523  << QStringLiteral("{% media_finder nonexisting_img %}") << dict
2524  << QString();
2525 }
2526 
2527 void TestDefaultTags::testRelativePaths()
2528 {
2529  QFETCH(QString, input);
2530  QFETCH(Dict, dict);
2531  QFETCH(QString, output);
2532 
2533  auto t = m_engine->newTemplate(input, QLatin1String(QTest::currentDataTag()));
2534  QVERIFY(t->error() == NoError);
2535  Context c(dict);
2536  auto result = t->render(&c);
2537  QVERIFY(t->error() == NoError);
2538  if (!output.isEmpty())
2539  QVERIFY(result == QStringLiteral("file:///path/to/") + output);
2540  else
2541  QVERIFY(result.isEmpty());
2542 
2543  c.setUrlType(Context::RelativeUrls);
2544  auto relativePath = QStringLiteral("relative/path");
2545  c.setRelativeMediaPath(relativePath);
2546  result = t->render(&c);
2547  QVERIFY(t->error() == NoError);
2548  if (!output.isEmpty())
2549  QVERIFY(result == relativePath + QLatin1Char('/') + output);
2550  else
2551  QVERIFY(result.isEmpty());
2552 }
2553 
2554 QTEST_MAIN(TestDefaultTags)
2555 #include "testdefaulttags.moc"
2556 
2557 #endif
Q_ENUM(...)
Relative URLs should be put in the template.
Definition: context.h:244
QHash::iterator insert(const Key &key, const T &value)
The Context class holds the context to render a Template with.
Definition: context.h:118
Cutelee::SafeString markSafe(const Cutelee::SafeString &input)
Definition: util.cpp:93
The Cutelee namespace holds all public Cutelee API.
Definition: Mainpage.dox:7
QString number(int n, int base)
void append(const T &value)
QString & insert(int position, QChar ch)
Q_OBJECTQ_OBJECT
Cutelee::Engine is the main entry point for creating Cutelee Templates.
Definition: engine.h:120
Utility functions used throughout Cutelee.
A QString wrapper class for containing whether a string is safe or needs to be escaped.
Definition: safestring.h:91
QVariant fromValue(const T &value)
QObject(QObject *parent)
QDateTime currentDateTime()
QDate date() const const
The InMemoryTemplateLoader loads Templates set dynamically in memory.
void insert(int i, const T &value)
QString fromLatin1(const char *str, int size)
QObject * parent() const const
std::pair< QString, QString > getMediaUri(const QString &fileName) const override