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 
35 typedef QList<QVariantList> Table;
36 typedef QHash<QString, QVariant> Dict;
37 typedef QPair<QString, QString> StringPair;
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  {
958  auto bio = QSharedPointer<BadIfObject>(new BadIfObject);
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 
1022  QVariant var;
1023  dict.insert(QStringLiteral("var"), var);
1024  QTest::newRow("if-truthiness05")
1025  << QStringLiteral("{% if var %}Yes{% else %}No{% endif %}") << dict
1026  << QStringLiteral("No") << NoError;
1027  var = QStringLiteral("foo");
1028  dict.insert(QStringLiteral("var"), var);
1029  QTest::newRow("if-truthiness06")
1030  << QStringLiteral("{% if var %}Yes{% else %}No{% endif %}") << dict
1031  << QStringLiteral("Yes") << NoError;
1032 
1033  QString str;
1034  dict.insert(QStringLiteral("var"), str);
1035  QTest::newRow("if-truthiness07")
1036  << QStringLiteral("{% if var %}Yes{% else %}No{% endif %}") << dict
1037  << QStringLiteral("No") << NoError;
1038  str = QStringLiteral("foo");
1039  dict.insert(QStringLiteral("var"), str);
1040  QTest::newRow("if-truthiness08")
1041  << QStringLiteral("{% if var %}Yes{% else %}No{% endif %}") << dict
1042  << QStringLiteral("Yes") << NoError;
1043 
1044  auto i = 0;
1045  dict.insert(QStringLiteral("var"), i);
1046  QTest::newRow("if-truthiness07")
1047  << QStringLiteral("{% if var %}Yes{% else %}No{% endif %}") << dict
1048  << QStringLiteral("No") << NoError;
1049  i = 7;
1050  dict.insert(QStringLiteral("var"), i);
1051  QTest::newRow("if-truthiness08")
1052  << QStringLiteral("{% if var %}Yes{% else %}No{% endif %}") << dict
1053  << QStringLiteral("Yes") << NoError;
1054 
1055  auto r = 0.0;
1056  dict.insert(QStringLiteral("var"), r);
1057  QTest::newRow("if-truthiness09")
1058  << QStringLiteral("{% if var %}Yes{% else %}No{% endif %}") << dict
1059  << QStringLiteral("No") << NoError;
1060  r = 7.1;
1061  dict.insert(QStringLiteral("var"), r);
1062  QTest::newRow("if-truthiness10")
1063  << QStringLiteral("{% if var %}Yes{% else %}No{% endif %}") << dict
1064  << QStringLiteral("Yes") << NoError;
1065 
1066  dict.clear();
1067  QTest::newRow("if-tag-badarg01")
1068  << QStringLiteral("{% if x|default_if_none:y %}yes{% endif %}") << dict
1069  << QString() << NoError;
1070 
1071  dict.insert(QStringLiteral("y"), 0);
1072 
1073  QTest::newRow("if-tag-badarg02")
1074  << QStringLiteral("{% if x|default_if_none:y %}yes{% endif %}") << dict
1075  << QString() << NoError;
1076 
1077  dict.clear();
1078  dict.insert(QStringLiteral("y"), 1);
1079 
1080  QTest::newRow("if-tag-badarg03")
1081  << QStringLiteral("{% if x|default_if_none:y %}yes{% endif %}") << dict
1082  << QStringLiteral("yes") << NoError;
1083 
1084  dict.clear();
1085  QTest::newRow("if-tag-badarg04") << QStringLiteral(
1086  "{% if x|default_if_none:y %}yes{% else %}no{% endif %}")
1087  << dict << QStringLiteral("no") << NoError;
1088 }
1089 
1090 void TestDefaultTags::testForTag_data()
1091 {
1092  QTest::addColumn<QString>("input");
1093  QTest::addColumn<Dict>("dict");
1094  QTest::addColumn<QString>("output");
1095  QTest::addColumn<Cutelee::Error>("error");
1096 
1097  Dict dict;
1098 
1099  QVariantList list{1, 2, 3};
1100  dict.insert(QStringLiteral("values"), list);
1101  QTest::newRow("for-tag01")
1102  << QStringLiteral("{% for val in values %}{{ val }}{% endfor %}") << dict
1103  << QStringLiteral("123") << NoError;
1104  QTest::newRow("for-tag02")
1105  << QStringLiteral("{% for val in values reversed %}{{ val }}{% endfor %}")
1106  << dict << QStringLiteral("321") << NoError;
1107  list.clear();
1108  dict.insert(QStringLiteral("values"), list);
1109  QTest::newRow("for-tag03") << QStringLiteral(
1110  "{% for val in values %}({{ val }} sdfsdf,){% endfor %}")
1111  << dict << QString() << NoError;
1112  QStringList emails{QStringLiteral("one"), QStringLiteral("two")};
1113  QVariantHash obj;
1114  obj.insert(QStringLiteral("emails"), emails);
1115  dict.insert(QStringLiteral("contact"), obj);
1116  QTest::newRow("for-tag04")
1117  << QStringLiteral(
1118  "{% for val in contact.emails %}({{ val }},){% endfor %}")
1119  << dict << QStringLiteral("(one,)(two,)") << NoError;
1120  emails.clear();
1121  obj.insert(QStringLiteral("emails"), emails);
1122  dict.insert(QStringLiteral("contact"), obj);
1123  QTest::newRow("for-tag05") << QStringLiteral(
1124  "{% for val in contact.emails %}({{ val }},){% endfor %}")
1125  << dict << QString() << NoError;
1126  list.clear();
1127  dict.clear();
1128  emails << QStringLiteral("one");
1129  dict.insert(QStringLiteral("emails"), emails);
1130  QTest::newRow("for-tag06")
1131  << QStringLiteral("{% for val in emails %}({{ val }},){% endfor %}")
1132  << dict << QStringLiteral("(one,)") << NoError;
1133  list.clear();
1134  dict.clear();
1135 
1136  QTest::newRow("for-tag07") << QStringLiteral("{% for %}{% endfor %}") << dict
1137  << QString() << TagSyntaxError;
1138  QTest::newRow("for-tag08")
1139  << QStringLiteral("{% for foo bar bat %}{% endfor %}") << dict
1140  << QString() << TagSyntaxError;
1141  QTest::newRow("for-tag09")
1142  << QStringLiteral("{% for foo bar '' %}{% endfor %}") << dict << QString()
1143  << TagSyntaxError;
1144 
1145  list << 1 << 2 << 3;
1146  dict.insert(QStringLiteral("values"), list);
1147  QTest::newRow("for-tag-vars01") << QStringLiteral(
1148  "{% for val in values %}{{ forloop.counter }}{% endfor %}")
1149  << dict << QStringLiteral("123") << NoError;
1150  QTest::newRow("for-tag-vars02") << QStringLiteral(
1151  "{% for val in values %}{{ forloop.counter0 }}{% endfor %}")
1152  << dict << QStringLiteral("012") << NoError;
1153  QTest::newRow("for-tag-vars03") << QStringLiteral(
1154  "{% for val in values %}{{ forloop.revcounter }}{% endfor %}")
1155  << dict << QStringLiteral("321") << NoError;
1156  QTest::newRow("for-tag-vars04") << QStringLiteral(
1157  "{% for val in values %}{{ forloop.revcounter0 }}{% endfor %}")
1158  << dict << QStringLiteral("210") << NoError;
1159  QTest::newRow("for-tag-vars05")
1160  << QStringLiteral("{% for val in values %}{% if forloop.first %}f{% else "
1161  "%}x{% endif %}{% endfor %}")
1162  << dict << QStringLiteral("fxx") << NoError;
1163  QTest::newRow("for-tag-vars06")
1164  << QStringLiteral("{% for val in values %}{% if forloop.last %}l{% else "
1165  "%}x{% endif %}{% endfor %}")
1166  << dict << QStringLiteral("xxl") << NoError;
1167 
1168  dict.clear();
1169  list.clear();
1170  QVariantList innerList{QStringLiteral("one"), 1};
1171  list.append(QVariant(innerList));
1172  innerList.clear();
1173  innerList << QStringLiteral("two") << 2;
1174  list.append(QVariant(innerList));
1175  dict.insert(QStringLiteral("items"), list);
1176  QTest::newRow("for-tag-unpack01")
1177  << QStringLiteral(
1178  "{% for key,value in items %}{{ key }}:{{ value }}/{% endfor %}")
1179  << dict << QStringLiteral("one:1/two:2/") << NoError;
1180 
1181  QTest::newRow("for-tag-unpack03")
1182  << QStringLiteral(
1183  "{% for key, value in items %}{{ key }}:{{ value }}/{% endfor %}")
1184  << dict << QStringLiteral("one:1/two:2/") << NoError;
1185  QTest::newRow("for-tag-unpack04")
1186  << QStringLiteral(
1187  "{% for key , value in items %}{{ key }}:{{ value }}/{% endfor %}")
1188  << dict << QStringLiteral("one:1/two:2/") << NoError;
1189  QTest::newRow("for-tag-unpack05")
1190  << QStringLiteral(
1191  "{% for key ,value in items %}{{ key }}:{{ value }}/{% endfor %}")
1192  << dict << QStringLiteral("one:1/two:2/") << NoError;
1193  QTest::newRow("for-tag-unpack06")
1194  << QStringLiteral(
1195  "{% for key value in items %}{{ key }}:{{ value }}/{% endfor %}")
1196  << dict << QStringLiteral("one:1/two:2/") << NoError;
1197  QTest::newRow("for-tag-unpack07")
1198  << QStringLiteral(
1199  "{% for key,,value in items %}{{ key }}:{{ value }}/{% endfor %}")
1200  << dict << QStringLiteral("one:1/two:2/") << NoError;
1201  QTest::newRow("for-tag-unpack08")
1202  << QStringLiteral(
1203  "{% for key,value, in items %}{{ key }}:{{ value }}/{% endfor %}")
1204  << dict << QStringLiteral("one:1/two:2/") << NoError;
1205 
1206  // Ensure that a single loopvar doesn't truncate the list in val.
1207  QTest::newRow("for-tag-unpack09")
1208  << QStringLiteral(
1209  "{% for val in items %}{{ val.0 }}:{{ val.1 }}/{% endfor %}")
1210  << dict << QStringLiteral("one:1/two:2/") << NoError;
1211 
1212  // Otherwise, silently truncate if the length of loopvars differs to the
1213  // length of each set of items.
1214 
1215  dict.clear();
1216  list.clear();
1217  innerList.clear();
1218  innerList << QStringLiteral("one") << 1 << QStringLiteral("carrot");
1219  list.append(QVariant(innerList));
1220  innerList.clear();
1221  innerList << QStringLiteral("two") << 2 << QStringLiteral("orange");
1222  list.append(QVariant(innerList));
1223  dict.insert(QStringLiteral("items"), list);
1224 
1225  QTest::newRow("for-tag-unpack10")
1226  << QStringLiteral("{% for x,y in items %}{{ x }}:{{ y }}/{% endfor %}")
1227  << dict << QStringLiteral("one:1/two:2/") << NoError;
1228 
1229  dict.clear();
1230  list.clear();
1231  innerList.clear();
1232  innerList << QStringLiteral("one") << 1;
1233  list.append(QVariant(innerList));
1234  innerList.clear();
1235  innerList << QStringLiteral("two") << 2;
1236  list.append(QVariant(innerList));
1237  dict.insert(QStringLiteral("items"), list);
1238 
1239  QTest::newRow("for-tag-unpack11")
1240  << QStringLiteral(
1241  "{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}")
1242  << dict << QStringLiteral("one:1,/two:2,/") << NoError;
1243 
1244  dict.clear();
1245  list.clear();
1246  innerList.clear();
1247  innerList << QStringLiteral("one") << 1 << QStringLiteral("carrot");
1248  list.append(QVariant(innerList));
1249  innerList.clear();
1250  innerList << QStringLiteral("two") << 2;
1251  list.append(QVariant(innerList));
1252  dict.insert(QStringLiteral("items"), list);
1253 
1254  QTest::newRow("for-tag-unpack12")
1255  << QStringLiteral(
1256  "{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}")
1257  << dict << QStringLiteral("one:1,carrot/two:2,/") << NoError;
1258 
1259  dict.clear();
1260  list.clear();
1261  innerList.clear();
1262  innerList << QStringLiteral("one") << 1 << QStringLiteral("carrot");
1263  list.append(QVariant(innerList));
1264  innerList.clear();
1265  innerList << QStringLiteral("two") << 2 << QStringLiteral("cheese");
1266  list.append(QVariant(innerList));
1267 
1268  dict.insert(QStringLiteral("items"), list);
1269 
1270  QTest::newRow("for-tag-unpack13")
1271  << QStringLiteral(
1272  "{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}")
1273  << dict << QStringLiteral("one:1,carrot/two:2,cheese/") << NoError;
1274 
1275  // Empty tag:
1276 
1277  dict.clear();
1278  dict.insert(QStringLiteral("values"), QVariantList{1, 2, 3});
1279  QTest::newRow("for-tag-empty01") << QStringLiteral(
1280  "{% for val in values %}{{ val }}{% empty %}empty text{% endfor %}")
1281  << dict << QStringLiteral("123") << NoError;
1282 
1283  dict.clear();
1284  dict.insert(QStringLiteral("values"), QVariantList());
1285  QTest::newRow("for-tag-empty02")
1286  << QStringLiteral("{% for val in values %}{{ val }}{% empty %}values "
1287  "array empty{% endfor %}")
1288  << dict << QStringLiteral("values array empty") << NoError;
1289 
1290  dict.clear();
1291  QTest::newRow("for-tag-empty03")
1292  << QStringLiteral("{% for val in values %}{{ val }}{% empty %}values "
1293  "array not found{% endfor %}")
1294  << dict << QStringLiteral("values array not found") << NoError;
1295 }
1296 
1297 void TestDefaultTags::testIfEqualTag_data()
1298 {
1299  QTest::addColumn<QString>("input");
1300  QTest::addColumn<Dict>("dict");
1301  QTest::addColumn<QString>("output");
1302  QTest::addColumn<Cutelee::Error>("error");
1303 
1304  Dict dict;
1305 
1306  dict.insert(QStringLiteral("a"), 1);
1307  dict.insert(QStringLiteral("b"), 2);
1308 
1309  QTest::newRow("ifequal01")
1310  << QStringLiteral("{% ifequal a b %}yes{% endifequal %}") << dict
1311  << QString() << NoError;
1312  QTest::newRow("ifequal03")
1313  << QStringLiteral("{% ifequal a b %}yes{% else %}no{% endifequal %}")
1314  << dict << QStringLiteral("no") << NoError;
1315 
1316  dict.clear();
1317  dict.insert(QStringLiteral("a"), 1);
1318  dict.insert(QStringLiteral("b"), 1);
1319 
1320  QTest::newRow("ifequal02")
1321  << QStringLiteral("{% ifequal a b %}yes{% endifequal %}") << dict
1322  << QStringLiteral("yes") << NoError;
1323  QTest::newRow("ifequal04")
1324  << QStringLiteral("{% ifequal a b %}yes{% else %}no{% endifequal %}")
1325  << dict << QStringLiteral("yes") << NoError;
1326 
1327  dict.clear();
1328  dict.insert(QStringLiteral("a"), QStringLiteral("test"));
1329 
1330  QTest::newRow("ifequal05")
1331  << QStringLiteral("{% ifequal a 'test' %}yes{% else %}no{% endifequal %}")
1332  << dict << QStringLiteral("yes") << NoError;
1333 
1334  dict.clear();
1335  dict.insert(QStringLiteral("a"), QStringLiteral("no"));
1336 
1337  QTest::newRow("ifequal06")
1338  << QStringLiteral("{% ifequal a 'test' %}yes{% else %}no{% endifequal %}")
1339  << dict << QStringLiteral("no") << NoError;
1340 
1341  dict.clear();
1342  dict.insert(QStringLiteral("a"), QStringLiteral("test"));
1343 
1344  QTest::newRow("ifequal07")
1345  << "{% ifequal a \"test\" %}yes{% else %}no{% endifequal %}" << dict
1346  << QStringLiteral("yes") << NoError;
1347 
1348  dict.clear();
1349  dict.insert(QStringLiteral("a"), QStringLiteral("no"));
1350 
1351  QTest::newRow("ifequal08")
1352  << "{% ifequal a \"test\" %}yes{% else %}no{% endifequal %}" << dict
1353  << QStringLiteral("no") << NoError;
1354 
1355  dict.clear();
1356 
1357  QTest::newRow("ifequal09")
1358  << "{% ifequal a \"test\" %}yes{% else %}no{% endifequal %}" << dict
1359  << QStringLiteral("no") << NoError;
1360 
1361  QTest::newRow("ifequal10")
1362  << QStringLiteral("{% ifequal a b %}yes{% else %}no{% endifequal %}")
1363  << dict << QStringLiteral("yes") << NoError;
1364 
1365  QTest::newRow("ifequal-split01")
1366  << "{% ifequal a \"test man\" %}yes{% else %}no{% endifequal %}" << dict
1367  << QStringLiteral("no") << NoError;
1368 
1369  dict.insert(QStringLiteral("a"), QStringLiteral("foo"));
1370  QTest::newRow("ifequal-split02")
1371  << "{% ifequal a \"test man\" %}yes{% else %}no{% endifequal %}" << dict
1372  << QStringLiteral("no") << NoError;
1373 
1374  dict.clear();
1375  dict.insert(QStringLiteral("a"), QStringLiteral("test man"));
1376  QTest::newRow("ifequal-split03")
1377  << "{% ifequal a \"test man\" %}yes{% else %}no{% endifequal %}" << dict
1378  << QStringLiteral("yes") << NoError;
1379  QTest::newRow("ifequal-split04") << QStringLiteral(
1380  "{% ifequal a 'test man' %}yes{% else %}no{% endifequal %}")
1381  << dict << QStringLiteral("yes") << NoError;
1382 
1383  dict.clear();
1384  dict.insert(QStringLiteral("a"), QStringLiteral(""));
1385  QTest::newRow("ifequal-split05")
1386  << "{% ifequal a 'i \"love\" you' %}yes{% else %}no{% endifequal %}"
1387  << dict << QStringLiteral("no") << NoError;
1388 
1389  dict.clear();
1390  dict.insert(QStringLiteral("a"), QStringLiteral("i \"love\" you"));
1391  QTest::newRow("ifequal-split06")
1392  << "{% ifequal a 'i \"love\" you' %}yes{% else %}no{% endifequal %}"
1393  << dict << QStringLiteral("yes") << NoError;
1394 
1395  dict.clear();
1396  dict.insert(QStringLiteral("a"), QStringLiteral("i love you"));
1397  QTest::newRow("ifequal-split07")
1398  << "{% ifequal a 'i \"love\" you' %}yes{% else %}no{% endifequal %}"
1399  << dict << QStringLiteral("no") << NoError;
1400 
1401  dict.clear();
1402  dict.insert(QStringLiteral("a"), QStringLiteral("I'm happy"));
1403  QTest::newRow("ifequal-split08")
1404  << "{% ifequal a 'I\\'m happy' %}yes{% else %}no{% endifequal %}" << dict
1405  << QStringLiteral("yes") << NoError;
1406 
1407  dict.clear();
1408  dict.insert(QStringLiteral("a"), QStringLiteral("slash\\man"));
1409  QTest::newRow("ifequal-split09")
1410  << "{% ifequal a 'slash\\man' %}yes{% else %}no{% endifequal %}" << dict
1411  << QStringLiteral("yes") << NoError;
1412 
1413  dict.clear();
1414  dict.insert(QStringLiteral("a"), QStringLiteral("slashman"));
1415  QTest::newRow("ifequal-split10")
1416  << "{% ifequal a 'slash\\man' %}yes{% else %}no{% endifequal %}" << dict
1417  << QStringLiteral("no") << NoError;
1418  // NUMERIC RESOLUTION
1419 
1420  dict.clear();
1421  dict.insert(QStringLiteral("x"), QStringLiteral("5"));
1422 
1423  QTest::newRow("ifequal-numeric01")
1424  << QStringLiteral("{% ifequal x 5 %}yes{% endifequal %}") << dict
1425  << QString() << NoError;
1426 
1427  dict.clear();
1428  dict.insert(QStringLiteral("x"), 5);
1429  QTest::newRow("ifequal-numeric02")
1430  << QStringLiteral("{% ifequal x 5 %}yes{% endifequal %}") << dict
1431  << QStringLiteral("yes") << NoError;
1432 
1433  dict.clear();
1434  dict.insert(QStringLiteral("x"), 5.2);
1435  QTest::newRow("ifequal-numeric03")
1436  << QStringLiteral("{% ifequal x 5 %}yes{% endifequal %}") << dict
1437  << QString() << NoError;
1438  QTest::newRow("ifequal-numeric04")
1439  << QStringLiteral("{% ifequal x 5.2 %}yes{% endifequal %}") << dict
1440  << QStringLiteral("yes") << NoError;
1441 
1442  dict.clear();
1443  dict.insert(QStringLiteral("x"), .2);
1444 
1445  QTest::newRow("ifequal-numeric05")
1446  << QStringLiteral("{% ifequal x 0.2 %}yes{% endifequal %}") << dict
1447  << QStringLiteral("yes") << NoError;
1448  QTest::newRow("ifequal-numeric06")
1449  << QStringLiteral("{% ifequal x .2 %}yes{% endifequal %}") << dict
1450  << QStringLiteral("yes") << NoError;
1451 
1452  dict.clear();
1453  dict.insert(QStringLiteral("x"), 2);
1454 
1455  QTest::newRow("ifequal-numeric07")
1456  << QStringLiteral("{% ifequal x 2. %}yes{% endifequal %}") << dict
1457  << QString() << TagSyntaxError;
1458 
1459  dict.clear();
1460  dict.insert(QStringLiteral("x"), 5);
1461  QTest::newRow("ifequal-numeric08")
1462  << "{% ifequal x \"5\" %}yes{% endifequal %}" << dict << QString()
1463  << NoError;
1464 
1465  dict.clear();
1466  dict.insert(QStringLiteral("x"), QStringLiteral("5"));
1467  QTest::newRow("ifequal-numeric09")
1468  << "{% ifequal x \"5\" %}yes{% endifequal %}" << dict
1469  << QStringLiteral("yes") << NoError;
1470 
1471  dict.clear();
1472  dict.insert(QStringLiteral("x"), -5);
1473  QTest::newRow("ifequal-numeric10")
1474  << QStringLiteral("{% ifequal x -5 %}yes{% endifequal %}") << dict
1475  << QStringLiteral("yes") << NoError;
1476 
1477  dict.clear();
1478  dict.insert(QStringLiteral("x"), -5.2);
1479  QTest::newRow("ifequal-numeric11")
1480  << QStringLiteral("{% ifequal x -5.2 %}yes{% endifequal %}") << dict
1481  << QStringLiteral("yes") << NoError;
1482 
1483  dict.clear();
1484  dict.insert(QStringLiteral("x"), 5);
1485  QTest::newRow("ifequal-numeric12")
1486  << QStringLiteral("{% ifequal x +5 %}yes{% endifequal %}") << dict
1487  << QStringLiteral("yes") << NoError;
1488 
1489  // FILTER EXPRESSIONS AS ARGUMENTS
1490 
1491  dict.clear();
1492  dict.insert(QStringLiteral("a"), QStringLiteral("a"));
1493  QTest::newRow("ifequal-filter01")
1494  << "{% ifequal a|upper \"A\" %}x{% endifequal %}" << dict
1495  << QStringLiteral("x") << NoError;
1496 
1497  QTest::newRow("ifequal-filter02")
1498  << "{% ifequal \"A\" a|upper %}x{% endifequal %}" << dict
1499  << QStringLiteral("x") << NoError;
1500 
1501  dict.clear();
1502  dict.insert(QStringLiteral("a"), QStringLiteral("x"));
1503  dict.insert(QStringLiteral("b"), QStringLiteral("X"));
1504 
1505  QTest::newRow("ifequal-filter03")
1506  << QStringLiteral("{% ifequal a|upper b|upper %}x{% endifequal %}")
1507  << dict << QStringLiteral("x") << NoError;
1508 
1509  dict.clear();
1510  dict.insert(QStringLiteral("x"), QStringLiteral("aaa"));
1511 
1512  QTest::newRow("ifequal-filter04")
1513  << "{% ifequal x|slice:\"1\" \"a\" %}x{% endifequal %}" << dict
1514  << QStringLiteral("x") << NoError;
1515 
1516  dict.clear();
1517  dict.insert(QStringLiteral("x"), QStringLiteral("aaa"));
1518 
1519  QTest::newRow("ifequal-filter05")
1520  << "{% ifequal x|slice:\"1\"|upper \"A\" %}x{% endifequal %}" << dict
1521  << QStringLiteral("x") << NoError;
1522 
1523  QTest::newRow("ifequal-error01")
1524  << "{% ifequal x|slice:\"1\"|upper %}x{% endifequal %}" << dict
1525  << QString() << TagSyntaxError;
1526 }
1527 
1528 void TestDefaultTags::testIfNotEqualTag_data()
1529 {
1530  QTest::addColumn<QString>("input");
1531  QTest::addColumn<Dict>("dict");
1532  QTest::addColumn<QString>("output");
1533  QTest::addColumn<Cutelee::Error>("error");
1534 
1535  Dict dict;
1536 
1537  dict.insert(QStringLiteral("a"), 1);
1538  dict.insert(QStringLiteral("b"), 2);
1539 
1540  QTest::newRow("ifnotequal01")
1541  << QStringLiteral("{% ifnotequal a b %}yes{% endifnotequal %}") << dict
1542  << QStringLiteral("yes") << NoError;
1543  QTest::newRow("ifnotequal03") << QStringLiteral(
1544  "{% ifnotequal a b %}yes{% else %}no{% endifnotequal %}")
1545  << dict << QStringLiteral("yes") << NoError;
1546 
1547  dict.clear();
1548  dict.insert(QStringLiteral("a"), 1);
1549  dict.insert(QStringLiteral("b"), 1);
1550 
1551  QTest::newRow("ifnotequal02")
1552  << QStringLiteral("{% ifnotequal a b %}yes{% endifnotequal %}") << dict
1553  << QString() << NoError;
1554  QTest::newRow("ifnotequal04") << QStringLiteral(
1555  "{% ifnotequal a b %}yes{% else %}no{% endifnotequal %}")
1556  << dict << QStringLiteral("no") << NoError;
1557 }
1558 
1559 void TestDefaultTags::testTemplateTagTag_data()
1560 {
1561  QTest::addColumn<QString>("input");
1562  QTest::addColumn<Dict>("dict");
1563  QTest::addColumn<QString>("output");
1564  QTest::addColumn<Cutelee::Error>("error");
1565 
1566  Dict dict;
1567 
1568  QTest::newRow("templatetag01")
1569  << QStringLiteral("{% templatetag openblock %}") << dict
1570  << QStringLiteral("{%") << NoError;
1571  QTest::newRow("templatetag02")
1572  << QStringLiteral("{% templatetag closeblock %}") << dict
1573  << QStringLiteral("%}") << NoError;
1574  QTest::newRow("templatetag03")
1575  << QStringLiteral("{% templatetag openvariable %}") << dict
1576  << QStringLiteral("{{") << NoError;
1577  QTest::newRow("templatetag04")
1578  << QStringLiteral("{% templatetag closevariable %}") << dict
1579  << QStringLiteral("}}") << NoError;
1580  QTest::newRow("templatetag05") << QStringLiteral("{% templatetag %}") << dict
1581  << QString() << TagSyntaxError;
1582  QTest::newRow("templatetag06") << QStringLiteral("{% templatetag foo %}")
1583  << dict << QString() << TagSyntaxError;
1584  QTest::newRow("templatetag07")
1585  << QStringLiteral("{% templatetag openbrace %}") << dict
1586  << QStringLiteral("{") << NoError;
1587  QTest::newRow("templatetag08")
1588  << QStringLiteral("{% templatetag closebrace %}") << dict
1589  << QStringLiteral("}") << NoError;
1590  QTest::newRow("templatetag09") << QStringLiteral(
1591  "{% templatetag openbrace %}{% templatetag openbrace %}")
1592  << dict << QStringLiteral("{{") << NoError;
1593  QTest::newRow("templatetag10") << QStringLiteral(
1594  "{% templatetag closebrace %}{% templatetag closebrace %}")
1595  << dict << QStringLiteral("}}") << NoError;
1596  QTest::newRow("templatetag11")
1597  << QStringLiteral("{% templatetag opencomment %}") << dict
1598  << QStringLiteral("{#") << NoError;
1599  QTest::newRow("templatetag12")
1600  << QStringLiteral("{% templatetag closecomment %}") << dict
1601  << QStringLiteral("#}") << NoError;
1602 }
1603 
1604 void TestDefaultTags::testWithTag_data()
1605 {
1606  QTest::addColumn<QString>("input");
1607  QTest::addColumn<Dict>("dict");
1608  QTest::addColumn<QString>("output");
1609  QTest::addColumn<Cutelee::Error>("error");
1610 
1611  Dict dict;
1612 
1613  QVariantHash hash;
1614  hash.insert(QStringLiteral("key"), 50);
1615  dict.insert(QStringLiteral("dict"), hash);
1616  QTest::newRow("with01") << QStringLiteral(
1617  "{% with dict.key as key %}{{ key }}{% endwith %}")
1618  << dict << QStringLiteral("50") << NoError;
1619  QTest::newRow("with02") << QStringLiteral(
1620  "{{ key }}{% with dict.key as key %}{{ key }}-{{ dict.key }}-{{ key }}{% "
1621  "endwith %}{{ key }}")
1622  << dict << QStringLiteral("50-50-50") << NoError;
1623  QTest::newRow("with03") << QStringLiteral(
1624  "{{ key }}{% with key=dict.key %}{{ key }}-{{ dict.key }}-{{ key }}{% "
1625  "endwith %}{{ key }}")
1626  << dict << QStringLiteral("50-50-50") << NoError;
1627  QTest::newRow("with04") << QStringLiteral(
1628  "{{ key1 }}{% with key1=dict.key key2=dict.key key3=dict.key %}{{ key1 }}-{{ dict.key }}-{{ key3 }}{% "
1629  "endwith %}{{ key }}")
1630  << dict << QStringLiteral("50-50-50") << NoError;
1631  QTest::newRow("with-error01")
1632  << QStringLiteral("{% with dict.key xx key %}{{ key }}{% endwith %}")
1633  << dict << QString() << TagSyntaxError;
1634  QTest::newRow("with-error02")
1635  << QStringLiteral("{% with dict.key as %}{{ key }}{% endwith %}") << dict
1636  << QString() << TagSyntaxError;
1637 }
1638 
1639 void TestDefaultTags::testCycleTag_data()
1640 {
1641  QTest::addColumn<QString>("input");
1642  QTest::addColumn<Dict>("dict");
1643  QTest::addColumn<QString>("output");
1644  QTest::addColumn<Cutelee::Error>("error");
1645 
1646  Dict dict;
1647 
1648  QTest::newRow("cycle01") << QStringLiteral("{% cycle a %}") << dict
1649  << QString() << TagSyntaxError;
1650  QTest::newRow("cycle02") << QStringLiteral(
1651  "{% cycle a,b,c as abc %}{% cycle abc %}")
1652  << dict << QStringLiteral("ab") << NoError;
1653  QTest::newRow("cycle03") << QStringLiteral(
1654  "{% cycle a,b,c as abc %}{% cycle abc %}{% cycle abc %}")
1655  << dict << QStringLiteral("abc") << NoError;
1656  QTest::newRow("cycle04") << QStringLiteral(
1657  "{% cycle a,b,c as abc %}{% cycle abc %}{% cycle abc %}{% cycle abc %}")
1658  << dict << QStringLiteral("abca") << NoError;
1659  QTest::newRow("cycle05") << QStringLiteral("{% cycle a %}") << dict
1660  << QString() << TagSyntaxError;
1661  // TODO: This is the same as cycle01. Remove.
1662  QTest::newRow("cycle06") << QStringLiteral("{% cycle a %}") << dict
1663  << QString() << TagSyntaxError;
1664  QTest::newRow("cycle07") << QStringLiteral(
1665  "{% cycle a,b,c as foo %}{% cycle bar %}")
1666  << dict << QString() << TagSyntaxError;
1667  QTest::newRow("cycle08") << QStringLiteral(
1668  "{% cycle a,b,c as foo %}{% cycle foo %}{{ foo }}{{ foo }}{% cycle foo "
1669  "%}{{ foo }}") << dict
1670  << QStringLiteral("abbbcc") << NoError;
1671 
1672  dict.insert(QStringLiteral("test"), QVariantList{0, 1, 2, 3, 4});
1673  QTest::newRow("cycle09") << QStringLiteral(
1674  "{% for i in test %}{% cycle a,b %}{{ i }},{% endfor %}")
1675  << dict << QStringLiteral("a0,b1,a2,b3,a4,")
1676  << NoError;
1677 
1678  dict.clear();
1679  QTest::newRow("cycle10") << QStringLiteral(
1680  "{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}")
1681  << dict << QStringLiteral("ab") << NoError;
1682  QTest::newRow("cycle11") << QStringLiteral(
1683  "{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}{% cycle abc %}")
1684  << dict << QStringLiteral("abc") << NoError;
1685  QTest::newRow("cycle12") << QStringLiteral(
1686  "{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}{% cycle abc %}{% cycle "
1687  "abc %}") << dict << QStringLiteral("abca")
1688  << NoError;
1689 
1690  dict.insert(QStringLiteral("test"), QVariantList{0, 1, 2, 3, 4});
1691  QTest::newRow("cycle13") << QStringLiteral(
1692  "{% for i in test %}{% cycle 'a' 'b' %}{{ i }},{% endfor %}")
1693  << dict << QStringLiteral("a0,b1,a2,b3,a4,")
1694  << NoError;
1695 
1696  dict.clear();
1697  dict.insert(QStringLiteral("one"), QStringLiteral("1"));
1698  dict.insert(QStringLiteral("two"), QStringLiteral("2"));
1699  QTest::newRow("cycle14") << QStringLiteral(
1700  "{% cycle one two as foo %}{% cycle foo %}")
1701  << dict << QStringLiteral("12") << NoError;
1702 
1703  dict.clear();
1704  dict.insert(QStringLiteral("test"), QVariantList{0, 1, 2, 3, 4});
1705  dict.insert(QStringLiteral("aye"), QStringLiteral("a"));
1706  dict.insert(QStringLiteral("bee"), QStringLiteral("b"));
1707  QTest::newRow("cycle15") << QStringLiteral(
1708  "{% for i in test %}{% cycle aye bee %}{{ i }},{% endfor %}")
1709  << dict << QStringLiteral("a0,b1,a2,b3,a4,")
1710  << NoError;
1711 
1712  dict.clear();
1713  dict.insert(QStringLiteral("one"), QStringLiteral("A"));
1714  dict.insert(QStringLiteral("two"), QStringLiteral("2"));
1715  QTest::newRow("cycle16") << QStringLiteral(
1716  "{% cycle one|lower two as foo %}{% cycle foo %}")
1717  << dict << QStringLiteral("a2") << NoError;
1718 
1719  QTest::newRow("cycle17") << QStringLiteral("{% cycle %}") << dict << QString()
1720  << TagSyntaxError;
1721  QTest::newRow("cycle18") << QStringLiteral("{% cycle var %}") << dict
1722  << QString() << TagSyntaxError;
1723 
1724  dict.insert(QStringLiteral("three"), QStringLiteral("B"));
1725  dict.insert(QStringLiteral("four"), QStringLiteral("4"));
1726 
1727  QTest::newRow("cycle19") << QStringLiteral("{% cycle one two three foo %}")
1728  << dict << QStringLiteral("A") << NoError;
1729  QTest::newRow("cycle20") << QStringLiteral(
1730  "{% cycle one two as foo %}{% cycle three four as bar %}")
1731  << dict << QStringLiteral("AB") << NoError;
1732 }
1733 
1734 void TestDefaultTags::testWidthRatioTag_data()
1735 {
1736  QTest::addColumn<QString>("input");
1737  QTest::addColumn<Dict>("dict");
1738  QTest::addColumn<QString>("output");
1739  QTest::addColumn<Cutelee::Error>("error");
1740 
1741  Dict dict;
1742 
1743  dict.insert(QStringLiteral("a"), 50);
1744  dict.insert(QStringLiteral("b"), 100);
1745  QTest::newRow("widthratio01") << QStringLiteral("{% widthratio a b 0 %}")
1746  << dict << QStringLiteral("0") << NoError;
1747 
1748  dict.clear();
1749  dict.insert(QStringLiteral("a"), 0);
1750  dict.insert(QStringLiteral("b"), 0);
1751  QTest::newRow("widthratio02") << QStringLiteral("{% widthratio a b 0 %}")
1752  << dict << QString() << NoError;
1753 
1754  dict.clear();
1755  dict.insert(QStringLiteral("a"), 0);
1756  dict.insert(QStringLiteral("b"), 100);
1757  QTest::newRow("widthratio03") << QStringLiteral("{% widthratio a b 100 %}")
1758  << dict << QStringLiteral("0") << NoError;
1759 
1760  dict.clear();
1761  dict.insert(QStringLiteral("a"), 50);
1762  dict.insert(QStringLiteral("b"), 100);
1763  QTest::newRow("widthratio04") << QStringLiteral("{% widthratio a b 100 %}")
1764  << dict << QStringLiteral("50") << NoError;
1765 
1766  dict.clear();
1767  dict.insert(QStringLiteral("a"), 100);
1768  dict.insert(QStringLiteral("b"), 100);
1769  QTest::newRow("widthratio05") << QStringLiteral("{% widthratio a b 100 %}")
1770  << dict << QStringLiteral("100") << NoError;
1771 
1772  dict.clear();
1773  dict.insert(QStringLiteral("a"), 50);
1774  dict.insert(QStringLiteral("b"), 80);
1775  QTest::newRow("widthratio06") << QStringLiteral("{% widthratio a b 100 %}")
1776  << dict << QStringLiteral("63") << NoError;
1777 
1778  dict.clear();
1779  dict.insert(QStringLiteral("a"), 50);
1780  dict.insert(QStringLiteral("b"), 70);
1781  QTest::newRow("widthratio07") << QStringLiteral("{% widthratio a b 100 %}")
1782  << dict << QStringLiteral("71") << NoError;
1783 
1784  dict.clear();
1785  // Raise exception if we don't have 3 args, last one an integer
1786  QTest::newRow("widthratio08") << QStringLiteral("{% widthratio %}") << dict
1787  << QString() << TagSyntaxError;
1788 
1789  dict.clear();
1790  QTest::newRow("widthratio09") << QStringLiteral("{% widthratio a b %}")
1791  << dict << QString() << TagSyntaxError;
1792 
1793  dict.clear();
1794  dict.insert(QStringLiteral("a"), 50);
1795  dict.insert(QStringLiteral("b"), 100);
1796  QTest::newRow("widthratio10") << QStringLiteral("{% widthratio a b 100.0 %}")
1797  << dict << QStringLiteral("50") << NoError;
1798 
1799  dict.clear();
1800  dict.insert(QStringLiteral("a"), 50);
1801  dict.insert(QStringLiteral("b"), 100);
1802  dict.insert(QStringLiteral("c"), 100);
1803  QTest::newRow("widthratio11") << QStringLiteral("{% widthratio a b c %}")
1804  << dict << QStringLiteral("50") << NoError;
1805 }
1806 
1807 void TestDefaultTags::testFilterTag_data()
1808 {
1809  QTest::addColumn<QString>("input");
1810  QTest::addColumn<Dict>("dict");
1811  QTest::addColumn<QString>("output");
1812  QTest::addColumn<Cutelee::Error>("error");
1813 
1814  Dict dict;
1815 
1816  QTest::newRow("filter01")
1817  << QStringLiteral("{% filter upper %}{% endfilter %}") << dict
1818  << QString() << NoError;
1819  QTest::newRow("filter02")
1820  << QStringLiteral("{% filter upper %}django{% endfilter %}") << dict
1821  << QStringLiteral("DJANGO") << NoError;
1822  QTest::newRow("filter03")
1823  << QStringLiteral("{% filter upper|lower %}django{% endfilter %}") << dict
1824  << QStringLiteral("django") << NoError;
1825 
1826  dict.insert(QStringLiteral("remove"), QStringLiteral("spam"));
1827  QTest::newRow("filter04")
1828  << QStringLiteral("{% filter cut:remove %}djangospam{% endfilter %}")
1829  << dict << QStringLiteral("django") << NoError;
1830 }
1831 
1832 void TestDefaultTags::testNowTag_data()
1833 {
1834  QTest::addColumn<QString>("input");
1835  QTest::addColumn<Dict>("dict");
1836  QTest::addColumn<QString>("output");
1837  QTest::addColumn<Cutelee::Error>("error");
1838 
1839  Dict dict;
1840 
1841  auto today = QDateTime::currentDateTime().date();
1842 
1843  QTest::newRow("now01") << QStringLiteral("{% now \"d M yyyy\"%}") << dict
1844  << (QString::number(today.day()) + QLatin1Char(' ')
1845  + QString::number(today.month()) + QLatin1Char(' ')
1846  + QString::number(today.year()))
1847  << NoError;
1848 
1849  QTest::newRow("now02") << QStringLiteral("{% now \"d \"M\" yyyy\"%}") << dict
1850  << QString() << TagSyntaxError;
1851 }
1852 
1853 void TestDefaultTags::testSpacelessTag_data()
1854 {
1855  QTest::addColumn<QString>("input");
1856  QTest::addColumn<Dict>("dict");
1857  QTest::addColumn<QString>("output");
1858  QTest::addColumn<Cutelee::Error>("error");
1859 
1860  Dict dict;
1861 
1862  QTest::newRow("spaceless01")
1863  << QStringLiteral(
1864  "{% spaceless %} <b> <i> text </i> </b> {% endspaceless %}")
1865  << dict << QStringLiteral("<b><i> text </i></b>") << NoError;
1866  QTest::newRow("spaceless02")
1867  << "{% spaceless %} <b> \n <i> text </i> \n </b> {% endspaceless %}"
1868  << dict << QStringLiteral("<b><i> text </i></b>") << NoError;
1869  QTest::newRow("spaceless03")
1870  << QStringLiteral("{% spaceless %}<b><i>text</i></b>{% endspaceless %}")
1871  << dict << QStringLiteral("<b><i>text</i></b>") << NoError;
1872 
1873  dict.insert(QStringLiteral("text"), QStringLiteral("This & that"));
1874  QTest::newRow("spaceless04")
1875  << QStringLiteral(
1876  "{% spaceless %}<b> <i>{{ text }}</i> </b>{% endspaceless %}")
1877  << dict << QStringLiteral("<b><i>This &amp; that</i></b>") << NoError;
1878  QTest::newRow("spaceless05")
1879  << QStringLiteral("{% autoescape off %}{% spaceless %}<b> <i>{{ text "
1880  "}}</i> </b>{% endspaceless %}{% endautoescape %}")
1881  << dict << QStringLiteral("<b><i>This & that</i></b>") << NoError;
1882  QTest::newRow("spaceless06") << QStringLiteral(
1883  "{% spaceless %}<b> <i>{{ text|safe }}</i> </b>{% endspaceless %}")
1884  << dict
1885  << QStringLiteral("<b><i>This & that</i></b>")
1886  << NoError;
1887 }
1888 
1889 void TestDefaultTags::testRegroupTag_data()
1890 {
1891  QTest::addColumn<QString>("input");
1892  QTest::addColumn<Dict>("dict");
1893  QTest::addColumn<QString>("output");
1894  QTest::addColumn<Cutelee::Error>("error");
1895 
1896  Dict dict;
1897 
1898  QVariantList list;
1899  QVariantHash hash;
1900 
1901  hash.insert(QStringLiteral("foo"), QStringLiteral("c"));
1902  hash.insert(QStringLiteral("bar"), 1);
1903  list.append(hash);
1904 
1905  hash.clear();
1906  hash.insert(QStringLiteral("foo"), QStringLiteral("d"));
1907  hash.insert(QStringLiteral("bar"), 1);
1908  list.append(hash);
1909 
1910  hash.clear();
1911  hash.insert(QStringLiteral("foo"), QStringLiteral("a"));
1912  hash.insert(QStringLiteral("bar"), 2);
1913  list.append(hash);
1914 
1915  hash.clear();
1916  hash.insert(QStringLiteral("foo"), QStringLiteral("b"));
1917  hash.insert(QStringLiteral("bar"), 2);
1918  list.append(hash);
1919 
1920  hash.clear();
1921  hash.insert(QStringLiteral("foo"), QStringLiteral("x"));
1922  hash.insert(QStringLiteral("bar"), 3);
1923  list.append(hash);
1924 
1925  dict.insert(QStringLiteral("data"), list);
1926 
1927  QTest::newRow("regroup01")
1928  << QString::fromLatin1("{% regroup data by bar as grouped %}"
1929  "{% for group in grouped %}"
1930  "{{ group.grouper }}:"
1931  "{% for item in group.list %}"
1932  "{{ item.foo }}"
1933  "{% endfor %},"
1934  "{% endfor %}")
1935  << dict << QStringLiteral("1:cd,2:ab,3:x,") << NoError;
1936 
1937  dict.clear();
1938  hash.clear();
1939  list.clear();
1940 
1941  hash.insert(QStringLiteral("foo"), QStringLiteral("a"));
1942  hash.insert(QStringLiteral("bar"), 2);
1943  list.append(hash);
1944 
1945  hash.clear();
1946  hash.insert(QStringLiteral("foo"), QStringLiteral("b"));
1947  hash.insert(QStringLiteral("bar"), 2);
1948  list.append(hash);
1949 
1950  hash.clear();
1951  hash.insert(QStringLiteral("foo"), QStringLiteral("x"));
1952  hash.insert(QStringLiteral("bar"), 3);
1953  list.append(hash);
1954 
1955  hash.clear();
1956  hash.insert(QStringLiteral("foo"), QStringLiteral("c"));
1957  hash.insert(QStringLiteral("bar"), 1);
1958  list.append(hash);
1959 
1960  hash.clear();
1961  hash.insert(QStringLiteral("foo"), QStringLiteral("d"));
1962  hash.insert(QStringLiteral("bar"), 1);
1963  list.append(hash);
1964 
1965  dict.insert(QStringLiteral("data"), list);
1966 
1967  // Data is output in the order it is sent in.
1968 
1969  QTest::newRow("regroup02")
1970  << QString::fromLatin1("{% regroup data by bar as grouped %}"
1971  "{% for group in grouped %}"
1972  "{{ group.grouper }}:"
1973  "{% for item in group.list %}"
1974  "{{ item.foo }}"
1975  "{% endfor %},"
1976  "{% endfor %}")
1977  << dict << QStringLiteral("2:ab,3:x,1:cd,") << NoError;
1978 
1979  dict.clear();
1980  hash.clear();
1981  list.clear();
1982 
1983  Table table;
1984 
1985  QVariantList row;
1986  row.append(1);
1987  row.append(QStringLiteral("a"));
1988  table.append(row);
1989 
1990  row.clear();
1991  row.append(1);
1992  row.append(QStringLiteral("b"));
1993  table.append(row);
1994 
1995  row.clear();
1996  row.append(2);
1997  row.append(QStringLiteral("a"));
1998  table.append(row);
1999 
2000  row.clear();
2001  row.append(3);
2002  row.append(QStringLiteral("c"));
2003  table.append(row);
2004 
2005  row.clear();
2006  row.append(3);
2007  row.append(QStringLiteral("d"));
2008  table.append(row);
2009 
2010  dict.insert(QStringLiteral("data"), QVariant::fromValue(table));
2011 
2012  QTest::newRow("regroup03")
2013  << QString::fromLatin1("{% regroup data by 0 as grouped %}"
2014  "{% for group in grouped %}"
2015  "{{ group.grouper }}:"
2016  "{% for item in group.list %}"
2017  "{{ item.1 }}"
2018  "{% endfor %},"
2019  "{% endfor %}")
2020  << dict << QStringLiteral("1:ab,2:a,3:cd,") << NoError;
2021 }
2022 
2023 void TestDefaultTags::testIfChangedTag_data()
2024 {
2025  QTest::addColumn<QString>("input");
2026  QTest::addColumn<Dict>("dict");
2027  QTest::addColumn<QString>("output");
2028  QTest::addColumn<Cutelee::Error>("error");
2029 
2030  Dict dict;
2031 
2032  dict.insert(QStringLiteral("num"), QVariantList{1, 2, 3});
2033  QTest::newRow("ifchanged01") << QStringLiteral(
2034  "{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}")
2035  << dict << QStringLiteral("123") << NoError;
2036 
2037  dict.clear();
2038  dict.insert(QStringLiteral("num"), QVariantList{1, 1, 3});
2039  QTest::newRow("ifchanged02") << QStringLiteral(
2040  "{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}")
2041  << dict << QStringLiteral("13") << NoError;
2042 
2043  dict.clear();
2044  dict.insert(QStringLiteral("num"), QVariantList{1, 1, 1});
2045  QTest::newRow("ifchanged03") << QStringLiteral(
2046  "{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}")
2047  << dict << QStringLiteral("1") << NoError;
2048 
2049  dict.clear();
2050  dict.insert(QStringLiteral("num"), QVariantList{1, 2, 3});
2051  dict.insert(QStringLiteral("numx"), QVariantList{2, 2, 2});
2052  QTest::newRow("ifchanged04") << QStringLiteral(
2053  "{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in "
2054  "numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}")
2055  << dict << QStringLiteral("122232") << NoError;
2056 
2057  dict.clear();
2058  dict.insert(QStringLiteral("num"), QVariantList{1, 1, 1});
2059  dict.insert(QStringLiteral("numx"), QVariantList{1, 2, 3});
2060  QTest::newRow("ifchanged05") << QStringLiteral(
2061  "{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in "
2062  "numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}")
2063  << dict << QStringLiteral("1123123123")
2064  << NoError;
2065 
2066  dict.clear();
2067  dict.insert(QStringLiteral("num"), QVariantList{1, 1, 1});
2068  dict.insert(QStringLiteral("numx"), QVariantList{2, 2, 2});
2069  QTest::newRow("ifchanged06") << QStringLiteral(
2070  "{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in "
2071  "numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}")
2072  << dict << QStringLiteral("1222") << NoError;
2073 
2074  dict.clear();
2075  dict.insert(QStringLiteral("num"), QVariantList{1, 1, 1});
2076  dict.insert(QStringLiteral("numx"), QVariantList{2, 2, 2});
2077  dict.insert(QStringLiteral("numy"), QVariantList{3, 3, 3});
2078  QTest::newRow("ifchanged07") << QStringLiteral(
2079  "{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in "
2080  "numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% for y in numy %}{% "
2081  "ifchanged %}{{ y }}{% endifchanged %}{% endfor %}{% endfor %}{% endfor "
2082  "%}") << dict << QStringLiteral("1233323332333")
2083  << NoError;
2084  QTest::newRow("ifchanged08")
2085  << QStringLiteral("{% ifchanged %}{{ num.0 }}{% endifchanged %}") << dict
2086  << QStringLiteral("1") << NoError;
2087 
2088  // datalist': [[(1, 'a'), (1, 'a'), (0, 'b'), (1, 'c')], [(0, 'a'), (1,
2089  // 'c'),
2090  // (1, 'd'), (1, 'd'), (0, 'e')]]}
2091  dict.clear();
2092  QVariantList list;
2093  QVariantList innerList;
2094  QVariantList tuple{1, QStringLiteral("a")};
2095  innerList.append(QVariant(tuple));
2096  tuple = {1, QStringLiteral("a")};
2097  innerList.append(QVariant(tuple));
2098  tuple = {0, QStringLiteral("b")};
2099  innerList.append(QVariant(tuple));
2100  tuple = {1, QStringLiteral("c")};
2101  innerList.append(QVariant(tuple));
2102  list.append(QVariant(innerList));
2103  innerList.clear();
2104 
2105  tuple = {0, QStringLiteral("a")};
2106  innerList.append(QVariant(tuple));
2107  tuple = {1, QStringLiteral("c")};
2108  innerList.append(QVariant(tuple));
2109  tuple = {1, QStringLiteral("d")};
2110  innerList.append(QVariant(tuple));
2111  tuple = {1, QStringLiteral("d")};
2112  innerList.append(QVariant(tuple));
2113  tuple = {0, QStringLiteral("e")};
2114  innerList.append(QVariant(tuple));
2115  list.append(QVariant(innerList));
2116  innerList.clear();
2117 
2118  dict.insert(QStringLiteral("datalist"), list);
2119  QTest::newRow("ifchanged08") << QStringLiteral(
2120  "{% for data in datalist %}{% for c,d in data %}{% if c %}{% ifchanged "
2121  "%}{{ d }}{% endifchanged %}{% endif %}{% endfor %}{% endfor %}")
2122  << dict << QStringLiteral("accd") << NoError;
2123 
2124  // Test one parameter given to ifchanged.
2125  dict.clear();
2126  dict.insert(QStringLiteral("num"), QVariantList{1, 2, 3});
2127  QTest::newRow("ifchanged-param01")
2128  << QStringLiteral("{% for n in num %}{% ifchanged n %}..{% endifchanged "
2129  "%}{{ n }}{% endfor %}")
2130  << dict << QStringLiteral("..1..2..3") << NoError;
2131 
2132  dict.clear();
2133  dict.insert(QStringLiteral("num"), QVariantList{1, 2, 3});
2134  dict.insert(QStringLiteral("numx"), QVariantList{5, 6, 7});
2135  QTest::newRow("ifchanged-param02")
2136  << QStringLiteral("{% for n in num %}{% for x in numx %}{% ifchanged n "
2137  "%}..{% endifchanged %}{{ x }}{% endfor %}{% endfor %}")
2138  << dict << QStringLiteral("..567..567..567") << NoError;
2139 
2140  // Test multiple parameters to ifchanged.
2141 
2142  dict.clear();
2143  dict.insert(QStringLiteral("num"), QVariantList{1, 1, 2});
2144  dict.insert(QStringLiteral("numx"), QVariantList{5, 6, 6});
2145  QTest::newRow("ifchanged-param03")
2146  << QStringLiteral(
2147  "{% for n in num %}{{ n }}{% for x in numx %}{% ifchanged x n "
2148  "%}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}")
2149  << dict << QStringLiteral("156156256") << NoError;
2150 
2151  // Test a date+hour like construct, where the hour of the last day
2152  // is the same but the date had changed, so print the hour anyway.
2153 
2154  dict.clear();
2155  QVariantList days;
2156  QVariantHash hash;
2157  hash.insert(QStringLiteral("day"), 1);
2158  hash.insert(QStringLiteral("hours"), QVariantList{1, 2, 3});
2159  days << hash;
2160  hash.clear();
2161  hash.insert(QStringLiteral("day"), 2);
2162  hash.insert(QStringLiteral("hours"), QVariantList{3});
2163  days << hash;
2164  dict.insert(QStringLiteral("days"), days);
2165  QTest::newRow("ifchanged-param04")
2166  << QStringLiteral("{% for d in days %}{% ifchanged %}{{ d.day }}{% "
2167  "endifchanged %}{% for h in d.hours %}{% ifchanged d h "
2168  "%}{{ h }}{% endifchanged %}{% endfor %}{% endfor %}")
2169  << dict << QStringLiteral("112323") << NoError;
2170 
2171  // Logically the same as above, just written with explicit
2172  // ifchanged for the day.
2173 
2174  QTest::newRow("ifchanged-param05") << QStringLiteral(
2175  "{% for d in days %}{% ifchanged d.day %}{{ d.day }}{% endifchanged %}{% "
2176  "for h in d.hours %}{% ifchanged d.day h %}{{ h }}{% endifchanged %}{% "
2177  "endfor %}{% endfor %}") << dict
2178  << QStringLiteral("112323") << NoError;
2179 
2180  // Test the else clause of ifchanged.
2181  dict.clear();
2182  dict.insert(QStringLiteral("ids"), QVariantList{1, 1, 2, 2, 2, 3});
2183  QTest::newRow("ifchanged-else01")
2184  << QStringLiteral("{% for id in ids %}{{ id }}{% ifchanged id %}-first{% "
2185  "else %}-other{% endifchanged %},{% endfor %}")
2186  << dict
2187  << QStringLiteral("1-first,1-other,2-first,2-other,2-other,3-first,")
2188  << NoError;
2189  QTest::newRow("ifchanged-else02")
2190  << QStringLiteral(
2191  "{% for id in ids %}{{ id }}-{% ifchanged id %}{% cycle red,blue "
2192  "%}{% else %}grey{% endifchanged %},{% endfor %}")
2193  << dict << QStringLiteral("1-red,1-grey,2-blue,2-grey,2-grey,3-red,")
2194  << NoError;
2195  QTest::newRow("ifchanged-else03")
2196  << QStringLiteral(
2197  "{% for id in ids %}{{ id }}{% ifchanged id %}-{% cycle red,blue "
2198  "%}{% else %}{% endifchanged %},{% endfor %}")
2199  << dict << QStringLiteral("1-red,1,2-blue,2,2,3-red,") << NoError;
2200 
2201  dict.clear();
2202  dict.insert(QStringLiteral("ids"), QVariantList{1, 1, 2, 2, 2, 3, 4});
2203  QTest::newRow("ifchanged-else04")
2204  << QStringLiteral(
2205  "{% for id in ids %}{% ifchanged %}***{{ id }}*{% else %}...{% "
2206  "endifchanged %}{{ forloop.counter }}{% endfor %}")
2207  << dict << QStringLiteral("***1*1...2***2*3...4...5***3*6***4*7")
2208  << NoError;
2209 }
2210 
2211 void TestDefaultTags::testAutoescapeTag_data()
2212 {
2213  QTest::addColumn<QString>("input");
2214  QTest::addColumn<Dict>("dict");
2215  QTest::addColumn<QString>("output");
2216  QTest::addColumn<Cutelee::Error>("error");
2217 
2218  Dict dict;
2219 
2220  QTest::newRow("autoescape-tag01")
2221  << QStringLiteral("{% autoescape off %}hello{% endautoescape %}") << dict
2222  << QStringLiteral("hello") << NoError;
2223 
2224  dict.insert(QStringLiteral("first"), QStringLiteral("<b>hello</b>"));
2225  QTest::newRow("autoescape-tag02")
2226  << QStringLiteral("{% autoescape off %}{{ first }}{% endautoescape %}")
2227  << dict << QStringLiteral("<b>hello</b>") << NoError;
2228  QTest::newRow("autoescape-tag03")
2229  << QStringLiteral("{% autoescape on %}{{ first }}{% endautoescape %}")
2230  << dict << QStringLiteral("&lt;b&gt;hello&lt;/b&gt;") << NoError;
2231  // Autoescape disabling and enabling nest in a predictable way.
2232  dict.insert(QStringLiteral("first"), QStringLiteral("<a>"));
2233  QTest::newRow("autoescape-tag04")
2234  << QStringLiteral("{% autoescape off %}{{ first }} {% autoescape on%}{{ "
2235  "first }}{% endautoescape %}{% endautoescape %}")
2236  << dict << QStringLiteral("<a> &lt;a&gt;") << NoError;
2237 
2238  dict.insert(QStringLiteral("first"), QStringLiteral("<b>first</b>"));
2239  QTest::newRow("autoescape-tag05")
2240  << QStringLiteral("{% autoescape on %}{{ first }}{% endautoescape %}")
2241  << dict << QStringLiteral("&lt;b&gt;first&lt;/b&gt;") << NoError;
2242  // Strings (ASCII or unicode) already marked as "safe" are not
2243  // auto-escaped
2244  SafeString safeString(QStringLiteral("<b>first</b>"));
2245  auto safeStringVar = QVariant::fromValue<SafeString>(markSafe(safeString));
2246  dict.insert(QStringLiteral("first"), safeStringVar);
2247 
2248  QTest::newRow("autoescape-tag06")
2249  << QStringLiteral("{{ first }}") << dict << QStringLiteral("<b>first</b>")
2250  << NoError;
2251  QTest::newRow("autoescape-tag07")
2252  << QStringLiteral("{% autoescape on %}{{ first }}{% endautoescape %}")
2253  << dict << QStringLiteral("<b>first</b>") << NoError;
2254 
2255  // Literal string arguments to filters, if used in the result, are
2256  // safe.
2257  dict.clear();
2258  dict.insert(QStringLiteral("var"), QVariant());
2259  QTest::newRow("autoescape-tag08")
2260  << "{% autoescape on %}{{ var|default_if_none:\"endquote\\\" hah\" "
2261  "}}{% "
2262  "endautoescape %}"
2263  << dict << "endquote\" hah" << NoError;
2264 
2265  QTest::newRow("autoescape-tag09")
2266  << "{% autoescape on extra %}{{ var|default_if_none:\"endquote\\\" "
2267  "hah\" "
2268  "}}{% endautoescape %}"
2269  << dict << "" << TagSyntaxError;
2270  QTest::newRow("autoescape-tag10")
2271  << "{% autoescape bad %}{{ var|default_if_none:\"endquote\\\" hah\" "
2272  "}}{% "
2273  "endautoescape %}"
2274  << dict << "" << TagSyntaxError;
2275 
2276  // Objects which return safe strings as their __unicode__ method
2277  // won't get double-escaped.
2278  // 'autoescape-tag09': (r'{{ unsafe }}', {'unsafe':
2279  // filters.UnsafeClass()},
2280  // 'you &amp; me'),
2281  // 'autoescape-tag10': (r'{{ safe }}', {'safe': filters.SafeClass()}, 'you
2282  // &gt; me'),
2283  // The "safe" and "escape" filters cannot work due to internal
2284  // implementation details (fortunately, the (no)autoescape block
2285  // tags can be used in those cases)
2286  dict.clear();
2287  dict.insert(QStringLiteral("first"), QStringLiteral("<a>"));
2288  QTest::newRow("autoescape-filtertag01")
2289  << QStringLiteral(
2290  "{{ first }}{% filter safe %}{{ first }} x<y{% endfilter %}")
2291  << dict << QString() << TagSyntaxError;
2292  QTest::newRow("autoescape-filtertag02")
2293  << QStringLiteral(
2294  "{{ first }}{% filter escape %}{{ first }} x<y{% endfilter %}")
2295  << dict << QString() << TagSyntaxError;
2296 }
2297 
2298 void TestDefaultTags::testMediaFinderTag_data()
2299 {
2300  QTest::addColumn<QString>("input");
2301  QTest::addColumn<Dict>("dict");
2302  QTest::addColumn<QString>("output");
2303  QTest::addColumn<Cutelee::Error>("error");
2304 
2305  Dict dict;
2306  QTest::newRow("media_finder-tag01")
2307  << "{% media_finder \"existing_image.png\" %}" << dict
2308  << QStringLiteral("file:///path/to/existing_image.png") << NoError;
2309  QTest::newRow("media_finder-tag02")
2310  << "{% media_finder \"does_not_exist.png\" %}" << dict << QString()
2311  << NoError;
2312  QTest::newRow("media_finder-tag03")
2313  << "{% media_finder \"existing_image.png\" \"does_not_exist.png\" %}"
2314  << dict << QStringLiteral("file:///path/to/existing_image.png")
2315  << NoError;
2316 
2317  dict.insert(QStringLiteral("existing_img"),
2318  QStringLiteral("existing_image.png"));
2319  dict.insert(QStringLiteral("nonexisting_img"),
2320  QStringLiteral("does_not_exist.png"));
2321 
2322  QTest::newRow("media_finder-tag04") << QStringLiteral("{% media_finder %}")
2323  << dict << QString() << TagSyntaxError;
2324  QTest::newRow("media_finder-tag05")
2325  << QStringLiteral("{% media_finder existing_img %}") << dict
2326  << QStringLiteral("file:///path/to/existing_image.png") << NoError;
2327  QTest::newRow("media_finder-tag06")
2328  << QStringLiteral("{% media_finder nonexisting_img %}") << dict
2329  << QString() << NoError;
2330  QTest::newRow("media_finder-tag07")
2331  << "{% media_finder \"does_not_exist.png\" existing_img %}" << dict
2332  << QStringLiteral("file:///path/to/existing_image.png") << NoError;
2333  QTest::newRow("media_finder-tag08")
2334  << QStringLiteral("{% media_finder nonexisting_img existing_img %}")
2335  << dict << QStringLiteral("file:///path/to/existing_image.png")
2336  << NoError;
2337  QTest::newRow("media_finder-tag09")
2338  << "{% media_finder \"existing_image.png\" "
2339  "\"another_existing_image.png\" %}"
2340  << dict << QStringLiteral("file:///path/to/existing_image.png")
2341  << NoError;
2342  QTest::newRow("media_finder-tag10")
2343  << "{% media_finder \"another_existing_image.png\" "
2344  "\"existing_image.png\" %}"
2345  << dict << QStringLiteral("file:///path/to/another_existing_image.png")
2346  << NoError;
2347 
2348  dict.insert(QStringLiteral("this_and_that_img"),
2349  QStringLiteral("this&that.png"));
2350 
2351  QTest::newRow("media_finder-tag11")
2352  << "{% media_finder \"this&that.png\" %}" << dict
2353  << QStringLiteral("file:///path/to/this&amp;that.png") << NoError;
2354  QTest::newRow("media_finder-tag12")
2355  << "{% media_finder this_and_that_img %}" << dict
2356  << QStringLiteral("file:///path/to/this&amp;that.png") << NoError;
2357  QTest::newRow("media_finder-tag13")
2358  << "{% autoescape off %}{% media_finder \"this&that.png\" %}{% "
2359  "endautoescape %}"
2360  << dict << QStringLiteral("file:///path/to/this&that.png") << NoError;
2361  QTest::newRow("media_finder-tag14")
2362  << "{% autoescape off %}{% media_finder this_and_that_img %}{% "
2363  "endautoescape %}"
2364  << dict << QStringLiteral("file:///path/to/this&that.png") << NoError;
2365 }
2366 
2367 void TestDefaultTags::testRangeTag_data()
2368 {
2369  QTest::addColumn<QString>("input");
2370  QTest::addColumn<Dict>("dict");
2371  QTest::addColumn<QString>("output");
2372  QTest::addColumn<Cutelee::Error>("error");
2373 
2374  Dict dict;
2375 
2376  QTest::newRow("range-tag01")
2377  << QStringLiteral("{% range 5 as i %}{{ i }};{% endrange %}") << dict
2378  << QStringLiteral("0;1;2;3;4;") << NoError;
2379  QTest::newRow("range-tag02")
2380  << QStringLiteral("{% range 1 6 as i %}{{ i }};{% endrange %}") << dict
2381  << QStringLiteral("1;2;3;4;5;") << NoError;
2382  QTest::newRow("range-tag03")
2383  << QStringLiteral("{% range 5 26 5 as i %}{{ i }};{% endrange %}") << dict
2384  << QStringLiteral("5;10;15;20;25;") << NoError;
2385 
2386  QVariantList list{10, 15, 2};
2387  dict.insert(QStringLiteral("values"), list);
2388 
2389  QTest::newRow("range-tag04") << QStringLiteral(
2390  "{% range values.0 values.1 values.2 as i %}{{ i }};{% endrange %}")
2391  << dict << QStringLiteral("10;12;14;")
2392  << NoError;
2393 
2394  QTest::newRow("range-tag05")
2395  << QStringLiteral("{% range 5 %}Foo;{% endrange %}") << dict
2396  << QStringLiteral("Foo;Foo;Foo;Foo;Foo;") << NoError;
2397  QTest::newRow("range-tag06")
2398  << QStringLiteral("{% range 5 6 %}Foo;{% endrange %}") << dict
2399  << QString() << TagSyntaxError;
2400  QTest::newRow("range-tag07")
2401  << QStringLiteral("{% range 5 6 7 %}Foo;{% endrange %}") << dict
2402  << QString() << TagSyntaxError;
2403 }
2404 
2405 void TestDefaultTags::testDebugTag_data()
2406 {
2407 
2408  QTest::addColumn<QString>("input");
2409  QTest::addColumn<Dict>("dict");
2410  QTest::addColumn<QString>("output");
2411  QTest::addColumn<Cutelee::Error>("error");
2412 
2413  Dict dict;
2414 
2415  QTest::newRow("debug-tag01")
2416  << QStringLiteral("{% debug %}") << dict
2417  << QStringLiteral("\n\nContext:\nEnd context:\n\n") << NoError;
2418  dict.insert(QStringLiteral("answer"), 42);
2419  QTest::newRow("debug-tag02")
2420  << QStringLiteral("{% debug %}") << dict
2421  << QStringLiteral("\n\nContext:\nkey answer, type int\nEnd context:\n\n")
2422  << NoError;
2423 }
2424 
2425 void TestDefaultTags::testLoadTag_data()
2426 {
2427 
2428  QTest::addColumn<QString>("input");
2429  QTest::addColumn<Dict>("dict");
2430  QTest::addColumn<QString>("output");
2431  QTest::addColumn<Cutelee::Error>("error");
2432 
2433  Dict dict;
2434 
2435  QTest::newRow("load-tag01") << QStringLiteral("{% load does_not_exist %}foo")
2436  << dict << QString() << TagSyntaxError;
2437 }
2438 
2439 void TestDefaultTags::testUrlTypes_data()
2440 {
2441  QTest::addColumn<QString>("input");
2442  QTest::addColumn<Dict>("dict");
2443  QTest::addColumn<QPair<QString, QString>>("output");
2444 
2445  Dict dict;
2446  QTest::newRow("url-types01")
2447  << "{% media_finder \"existing_image.png\" %}" << dict
2448  << qMakePair(QStringLiteral("file:///path/to/"),
2449  QStringLiteral("existing_image.png"));
2450 
2451  QTest::newRow("url-types02") << "{% media_finder \"does_not_exist.png\" %}"
2452  << dict << qMakePair(QString(), QString());
2453 
2454  dict.insert(QStringLiteral("existing_img"),
2455  QStringLiteral("existing_image.png"));
2456  dict.insert(QStringLiteral("nonexisting_img"),
2457  QStringLiteral("does_not_exist.png"));
2458 
2459  QTest::newRow("url-types03")
2460  << QStringLiteral("{% media_finder existing_img %}") << dict
2461  << qMakePair(QStringLiteral("file:///path/to/"),
2462  QStringLiteral("existing_image.png"));
2463 
2464  QTest::newRow("url-types04")
2465  << QStringLiteral("{% media_finder nonexisting_img %}") << dict
2466  << qMakePair(QString(), QString());
2467 }
2468 
2469 void TestDefaultTags::testUrlTypes()
2470 {
2471  QFETCH(QString, input);
2472  QFETCH(Dict, dict);
2473  QFETCH(StringPair, output);
2474 
2475  auto t = m_engine->newTemplate(input, QLatin1String(QTest::currentDataTag()));
2476  QVERIFY(t->error() == NoError);
2477  Context c(dict);
2478  auto result = t->render(&c);
2479  QVERIFY(t->error() == NoError);
2480  QVERIFY(result == output.first + output.second);
2481 
2482  c.setUrlType(Context::RelativeUrls);
2483  result = t->render(&c);
2484  QVERIFY(t->error() == NoError);
2485  QVERIFY(result == output.second);
2486 }
2487 
2488 void TestDefaultTags::testRelativePaths_data()
2489 {
2490  QTest::addColumn<QString>("input");
2491  QTest::addColumn<Dict>("dict");
2492  QTest::addColumn<QString>("output");
2493 
2494  Dict dict;
2495  QTest::newRow("relativepaths01")
2496  << "{% media_finder \"existing_image.png\" %}" << dict
2497  << QStringLiteral("existing_image.png");
2498 
2499  QTest::newRow("relativepaths02")
2500  << "{% media_finder \"does_not_exist.png\" %}" << dict << QString();
2501 
2502  dict.insert(QStringLiteral("existing_img"),
2503  QStringLiteral("existing_image.png"));
2504  dict.insert(QStringLiteral("nonexisting_img"),
2505  QStringLiteral("does_not_exist.png"));
2506 
2507  QTest::newRow("relativepaths03")
2508  << QStringLiteral("{% media_finder existing_img %}") << dict
2509  << QStringLiteral("existing_image.png");
2510 
2511  QTest::newRow("relativepaths04")
2512  << QStringLiteral("{% media_finder nonexisting_img %}") << dict
2513  << QString();
2514 }
2515 
2516 void TestDefaultTags::testRelativePaths()
2517 {
2518  QFETCH(QString, input);
2519  QFETCH(Dict, dict);
2520  QFETCH(QString, output);
2521 
2522  auto t = m_engine->newTemplate(input, QLatin1String(QTest::currentDataTag()));
2523  QVERIFY(t->error() == NoError);
2524  Context c(dict);
2525  auto result = t->render(&c);
2526  QVERIFY(t->error() == NoError);
2527  if (!output.isEmpty())
2528  QVERIFY(result == QStringLiteral("file:///path/to/") + output);
2529  else
2530  QVERIFY(result.isEmpty());
2531 
2532  c.setUrlType(Context::RelativeUrls);
2533  auto relativePath = QStringLiteral("relative/path");
2534  c.setRelativeMediaPath(relativePath);
2535  result = t->render(&c);
2536  QVERIFY(t->error() == NoError);
2537  if (!output.isEmpty())
2538  QVERIFY(result == relativePath + QLatin1Char('/') + output);
2539  else
2540  QVERIFY(result.isEmpty());
2541 }
2542 
2543 QTEST_MAIN(TestDefaultTags)
2544 #include "testdefaulttags.moc"
2545 
2546 #endif
The Context class holds the context to render a Template with.
Definition: context.h:119
@ RelativeUrls
Relative URLs should be put in the template.
Definition: context.h:244
Cutelee::Engine is the main entry point for creating Cutelee Templates.
Definition: engine.h:121
The InMemoryTemplateLoader loads Templates set dynamically in memory.
A QString wrapper class for containing whether a string is safe or needs to be escaped.
Definition: safestring.h:92
std::pair< QString, QString > getMediaUri(const QString &fileName) const override
The Cutelee namespace holds all public Cutelee API.
Definition: Mainpage.dox:8
Cutelee::SafeString markSafe(const Cutelee::SafeString &input)
Definition: util.cpp:90
Utility functions used throughout Cutelee.