Cutelee  6.1.0
testfilters.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 FILTERSTEST_H
22 #define FILTERSTEST_H
23 
24 #include <QtCore/QDebug>
25 #include <QtCore/QDir>
26 #include <QtCore/QFileInfo>
27 #include <QtTest/QTest>
28 
29 #include "context.h"
30 #include "coverageobject.h"
31 #include "engine.h"
32 #include "cutelee_paths.h"
33 
34 #include "template.h"
35 #include <util.h>
36 
38 
39 Q_DECLARE_METATYPE(Cutelee::Error)
40 
41 using namespace Cutelee;
42 
44 {
45  Q_OBJECT
46 
47 private Q_SLOTS:
48  void initTestCase();
49  void cleanupTestCase();
50 
51  void testDateBasedFilters_data();
52  void testDateBasedFilters() { doTest(); }
53 
54  void testStringFilters_data();
55  void testStringFilters() { doTest(); }
56 
57  void testListFilters_data();
58  void testListFilters() { doTest(); }
59 
60  void testLogicFilters_data();
61  void testLogicFilters() { doTest(); }
62 
63  void testMiscFilters_data();
64  void testMiscFilters() { doTest(); }
65 
66  void testIntegerFilters_data();
67  void testIntegerFilters() { doTest(); }
68 
69 private:
70  void doTest();
71 
72  std::shared_ptr<InMemoryTemplateLoader> loader;
73  Engine *m_engine;
74 };
75 
76 void TestFilters::initTestCase()
77 {
78  m_engine = new Engine(this);
79 
80  loader = std::shared_ptr<InMemoryTemplateLoader>(new InMemoryTemplateLoader());
81  m_engine->addTemplateLoader(loader);
82 
83  auto appDirPath
85  m_engine->setPluginPaths({
86  QStringLiteral(CUTELEE_PLUGIN_PATH),
87  appDirPath + QStringLiteral("/tests/") // For testtags.qs
88  });
89 }
90 
91 void TestFilters::cleanupTestCase() { delete m_engine; }
92 
93 void TestFilters::doTest()
94 {
95  QFETCH(QString, input);
96  QFETCH(Dict, dict);
97  QFETCH(QString, output);
98  QFETCH(Cutelee::Error, error);
99 
100  auto t = m_engine->newTemplate(input, QLatin1String(QTest::currentDataTag()));
101 
102  Context context(dict);
103 
104  auto result = t->render(&context);
105 
106  if (t->error() != NoError) {
107  if (t->error() != error)
108  qDebug() << t->errorString();
109  QCOMPARE(t->error(), error);
110  return;
111  }
112 
113  // Didn't catch any errors, so make sure I didn't expect any.
114  QCOMPARE(NoError, error);
115 
116  QCOMPARE(t->error(), NoError);
117 
118  QCOMPARE(result, output);
119 }
120 
121 void TestFilters::testDateBasedFilters_data()
122 {
123  QTest::addColumn<QString>("input");
124  QTest::addColumn<Dict>("dict");
125  QTest::addColumn<QString>("output");
126  QTest::addColumn<Cutelee::Error>("error");
127 
128  Dict dict;
129  auto now = QDateTime::currentDateTimeUtc();
130 
131  dict.insert(QStringLiteral("a"), now.addSecs(-70));
132 
133  QTest::newRow("filter-timesince01")
134  << QStringLiteral("{{ a|timesince }}") << dict
135  << QStringLiteral("1 minute") << NoError;
136 
137  dict.clear();
138 
139  dict.insert(QStringLiteral("a"), now.addDays(-1).addSecs(-60));
140 
141  QTest::newRow("filter-timesince02")
142  << QStringLiteral("{{ a|timesince }}") << dict << QStringLiteral("1 day")
143  << NoError;
144 
145  dict.clear();
146 
147  dict.insert(QStringLiteral("a"),
148  now.addSecs(-1 * 60 * 60).addSecs(-1 * 25 * 60).addSecs(-1 * 10));
149  QTest::newRow("filter-timesince03")
150  << QStringLiteral("{{ a|timesince }}") << dict
151  << QStringLiteral("1 hour, 25 minutes") << NoError;
152 
153  dict.clear();
154 
155  // Compare to a given parameter
156 
157  dict.insert(QStringLiteral("a"), now.addDays(-2));
158  dict.insert(QStringLiteral("b"), now.addDays(-1));
159 
160  QTest::newRow("filter-timesince04")
161  << QStringLiteral("{{ a|timesince:b }}") << dict
162  << QStringLiteral("1 day") << NoError;
163 
164  dict.clear();
165 
166  dict.insert(QStringLiteral("a"), now.addDays(-2).addSecs(-60));
167  dict.insert(QStringLiteral("b"), now.addDays(-2));
168 
169  QTest::newRow("filter-timesince05")
170  << QStringLiteral("{{ a|timesince:b }}") << dict
171  << QStringLiteral("1 minute") << NoError;
172 
173  dict.clear();
174 
175  // Check that timezone is respected
176 
177  // {"a":now_tz - timedelta(hours=8), "b":now_tz
178  // QTest::newRow( "filter-timesince06" ) << QString::fromLatin1( "{{
179  // a|timesince:b }}" ) << dict << QString::fromLatin1( "8 hours" ) <<
180  // NoError;
181 
182  dict.insert(QStringLiteral("earlier"), now.addDays(-7));
183  QTest::newRow("filter-timesince07")
184  << QStringLiteral("{{ earlier|timesince }}") << dict
185  << QStringLiteral("1 week") << NoError;
186 
187  dict.clear();
188 
189  dict.insert(QStringLiteral("now"), now);
190  dict.insert(QStringLiteral("earlier"), now.addDays(-7));
191 
192  QTest::newRow("filter-timesince08")
193  << QStringLiteral("{{ earlier|timesince:now }}") << dict
194  << QStringLiteral("1 week") << NoError;
195 
196  dict.clear();
197 
198  dict.insert(QStringLiteral("later"), now.addDays(7));
199 
200  QTest::newRow("filter-timesince09")
201  << QStringLiteral("{{ later|timesince }}") << dict
202  << QStringLiteral("0 minutes") << NoError;
203 
204  dict.clear();
205 
206  dict.insert(QStringLiteral("now"), now);
207  dict.insert(QStringLiteral("later"), now.addDays(7));
208 
209  QTest::newRow("filter-timesince10")
210  << QStringLiteral("{{ later|timesince:now }}") << dict
211  << QStringLiteral("0 minutes") << NoError;
212 
213  // Ensures that differing timezones are calculated correctly
214 
215  // {"a": now
216  // QTest::newRow( "filter-timesince11" ) << QString::fromLatin1( "{{
217  // a|timesince }}" ) << dict << QString::fromLatin1( "0 minutes" ) <<
218  // NoError;
219 
220  // {"a": now_tz
221  // QTest::newRow( "filter-timesince12" ) << QString::fromLatin1( "{{
222  // a|timesince }}" ) << dict << QString::fromLatin1( "0 minutes" ) <<
223  // NoError;
224 
225  // {"a": now_tz_i
226  // QTest::newRow( "filter-timesince13" ) << QString::fromLatin1( "{{
227  // a|timesince }}" ) << dict << QString::fromLatin1( "0 minutes" ) <<
228  // NoError;
229 
230  // {"a": now_tz, "b": now_tz_i
231  // QTest::newRow( "filter-timesince14" ) << QString::fromLatin1( "{{
232  // a|timesince:b }}" ) << dict << QString::fromLatin1( "0 minutes" ) <<
233  // NoError;
234 
235  // {"a": now, "b": now_tz_i
236  // QTest::newRow( "filter-timesince15" ) << QString::fromLatin1( "{{
237  // a|timesince:b }}" ) << dict << QString() << NoError;
238 
239  // {"a": now_tz_i, "b": now
240  // QTest::newRow( "filter-timesince16" ) << QString::fromLatin1( "{{
241  // a|timesince:b }}" ) << dict << QString() << NoError;
242 
243  dict.clear();
244 
245  dict.insert(QStringLiteral("a"), now);
246  dict.insert(QStringLiteral("b"), now);
247 
248  QTest::newRow("filter-timesince17")
249  << QStringLiteral("{{ a|timesince:b }}") << dict
250  << QStringLiteral("0 minutes") << NoError;
251 
252  dict.clear();
253 
254  dict.insert(QStringLiteral("a"), now);
255  dict.insert(QStringLiteral("b"), now.addDays(1));
256 
257  QTest::newRow("filter-timesince18")
258  << QStringLiteral("{{ a|timesince:b }}") << dict
259  << QStringLiteral("1 day") << NoError;
260 
261  dict.clear();
262  QTest::newRow("filter-timesince19") << QStringLiteral("{{xx|timesince}}")
263  << dict << QStringLiteral("") << NoError;
264  QTest::newRow("filter-timesince20") << QStringLiteral("{{|timesince}}")
265  << dict << QStringLiteral("") << NoError;
266 
267  // Default compare with datetime.now()
268 
269  dict.clear();
270  dict.insert(QStringLiteral("a"), now.addSecs(130));
271 
272  QTest::newRow("filter-timeuntil01")
273  << QStringLiteral("{{ a|timeuntil }}") << dict
274  << QStringLiteral("2 minutes") << NoError;
275 
276  dict.clear();
277  dict.insert(QStringLiteral("a"), now.addDays(1).addSecs(10));
278 
279  QTest::newRow("filter-timeuntil02")
280  << QStringLiteral("{{ a|timeuntil }}") << dict << QStringLiteral("1 day")
281  << NoError;
282 
283  dict.clear();
284  dict.insert(QStringLiteral("a"), now.addSecs(60 * 60 * 8).addSecs(610));
285 
286  QTest::newRow("filter-timeuntil03")
287  << QStringLiteral("{{ a|timeuntil }}") << dict
288  << QStringLiteral("8 hours, 10 minutes") << NoError;
289 
290  // Compare to a given parameter
291 
292  dict.clear();
293  dict.insert(QStringLiteral("a"), now.addDays(-1));
294  dict.insert(QStringLiteral("b"), now.addDays(-2));
295 
296  QTest::newRow("filter-timeuntil04")
297  << QStringLiteral("{{ a|timeuntil:b }}") << dict
298  << QStringLiteral("1 day") << NoError;
299 
300  dict.clear();
301  dict.insert(QStringLiteral("a"), now.addDays(-1));
302  dict.insert(QStringLiteral("b"), now.addDays(-1).addSecs(-60));
303 
304  QTest::newRow("filter-timeuntil05")
305  << QStringLiteral("{{ a|timeuntil:b }}") << dict
306  << QStringLiteral("1 minute") << NoError;
307 
308  dict.clear();
309  dict.insert(QStringLiteral("earlier"), now.addDays(-7));
310 
311  QTest::newRow("filter-timeuntil06")
312  << QStringLiteral("{{ earlier|timeuntil }}") << dict
313  << QStringLiteral("0 minutes") << NoError;
314 
315  dict.clear();
316  dict.insert(QStringLiteral("now"), now);
317  dict.insert(QStringLiteral("earlier"), now.addDays(-7));
318 
319  QTest::newRow("filter-timeuntil07")
320  << QStringLiteral("{{ earlier|timeuntil:now }}") << dict
321  << QStringLiteral("0 minutes") << NoError;
322 
323  dict.clear();
324  dict.insert(QStringLiteral("later"), now.addDays(7).addSecs(5));
325 
326  QTest::newRow("filter-timeuntil08")
327  << QStringLiteral("{{ later|timeuntil }}") << dict
328  << QStringLiteral("1 week") << NoError;
329 
330  dict.clear();
331  dict.insert(QStringLiteral("now"), now);
332  dict.insert(QStringLiteral("later"), now.addDays(7));
333 
334  QTest::newRow("filter-timeuntil09")
335  << QStringLiteral("{{ later|timeuntil:now }}") << dict
336  << QStringLiteral("1 week") << NoError;
337 
338  // Ensures that differing timezones are calculated correctly
339  //
340  // // {"a": now_tz_i
341  // QTest::newRow( "filter-timeuntil10" ) << QString::fromLatin1( "{{
342  // a|timeuntil }}" ) << dict << QString::fromLatin1( "0 minutes" ) <<
343  // NoError;
344  //
345  // // {"a": now_tz_i, "b": now_tz
346  // QTest::newRow( "filter-timeuntil11" ) << QString::fromLatin1( "{{
347  // a|timeuntil:b }}" ) << dict << QString::fromLatin1( "0 minutes" ) <<
348  // NoError;
349 
350  dict.clear();
351  dict.insert(QStringLiteral("a"), now);
352  dict.insert(QStringLiteral("b"), now);
353  QTest::newRow("filter-timeuntil12")
354  << QStringLiteral("{{ a|timeuntil:b }}") << dict
355  << QStringLiteral("0 minutes") << NoError;
356 
357  dict.clear();
358  dict.insert(QStringLiteral("a"), now);
359  dict.insert(QStringLiteral("b"), now.addDays(-1));
360 
361  QTest::newRow("filter-timeuntil13")
362  << QStringLiteral("{{ a|timeuntil:b }}") << dict
363  << QStringLiteral("1 day") << NoError;
364 
365  dict.clear();
366  QTest::newRow("filter-timeuntil14") << QStringLiteral("{{xx|timeuntil}}")
367  << dict << QStringLiteral("") << NoError;
368  QTest::newRow("filter-timeuntil15") << QStringLiteral("{{|timeuntil}}")
369  << dict << QStringLiteral("") << NoError;
370 
371  QDate d(2008, 1, 1);
372 
373  dict.clear();
374  dict.insert(QStringLiteral("d"), d);
375 
376  QTest::newRow("date01") << "{{ d|date:\"MM\" }}" << dict
377  << QStringLiteral("01") << NoError;
378  QTest::newRow("date02") << QStringLiteral("{{ d|date }}") << dict
379  << d.toString(QStringLiteral("MMM. d, yyyy"))
380  << NoError;
381 
382  dict.clear();
383  dict.insert(QStringLiteral("d"), QStringLiteral("fail_string"));
384  QTest::newRow("date03") << "{{ d|date:\"MM\" }}" << dict << QString()
385  << NoError;
386 }
387 
388 void TestFilters::testStringFilters_data()
389 {
390  QTest::addColumn<QString>("input");
391  QTest::addColumn<Dict>("dict");
392  QTest::addColumn<QString>("output");
393  QTest::addColumn<Cutelee::Error>("error");
394 
395  Dict dict;
396 
397  dict.clear();
398  dict.insert(QStringLiteral("a"), QStringLiteral("<a>\'"));
399  dict.insert(QStringLiteral("b"),
400  QVariant::fromValue(markSafe(QStringLiteral("<a>\'"))));
401 
402  QTest::newRow("filter-addslash01")
403  << QStringLiteral("{% autoescape off %}{{ a|addslashes }} {{ "
404  "b|addslashes }}{% endautoescape %}")
405  << dict << "<a>\\\' <a>\\\'" << NoError;
406 
407  dict.clear();
408  dict.insert(QStringLiteral("a"), QStringLiteral("<a>\'"));
409  dict.insert(QStringLiteral("b"),
410  QVariant::fromValue(markSafe(QStringLiteral("<a>\'"))));
411 
412  QTest::newRow("filter-addslash02")
413  << QStringLiteral("{{ a|addslashes }} {{ b|addslashes }}") << dict
414  << "&lt;a&gt;\\&#39; <a>\\\'" << NoError;
415 
416  dict.clear();
417  dict.insert(QStringLiteral("a"), QStringLiteral("fred>"));
418  dict.insert(QStringLiteral("b"),
419  QVariant::fromValue(markSafe(QStringLiteral("fred&gt;"))));
420 
421  QTest::newRow("filter-capfirst01")
422  << QStringLiteral("{% autoescape off %}{{ a|capfirst }} {{ b|capfirst "
423  "}}{% endautoescape %}")
424  << dict << QStringLiteral("Fred> Fred&gt;") << NoError;
425 
426  dict.clear();
427  dict.insert(QStringLiteral("a"), QStringLiteral("fred>"));
428  dict.insert(QStringLiteral("b"),
429  QVariant::fromValue(markSafe(QStringLiteral("fred&gt;"))));
430 
431  QTest::newRow("filter-capfirst02")
432  << QStringLiteral("{{ a|capfirst }} {{ b|capfirst }}") << dict
433  << QStringLiteral("Fred&gt; Fred&gt;") << NoError;
434 
435  // Note that applying fix_ampsersands in autoescape mode leads to
436  // double escaping.
437 
438  dict.clear();
439  dict.insert(QStringLiteral("a"), QStringLiteral("a&b"));
440  dict.insert(QStringLiteral("b"),
441  QVariant::fromValue(markSafe(QStringLiteral("a&b"))));
442 
443  QTest::newRow("filter-fix_ampersands01")
444  << QStringLiteral("{% autoescape off %}{{ a|fix_ampersands }} {{ "
445  "b|fix_ampersands }}{% endautoescape %}")
446  << dict << QStringLiteral("a&amp;b a&amp;b") << NoError;
447 
448  dict.clear();
449  dict.insert(QStringLiteral("a"), QStringLiteral("a&b"));
450  dict.insert(QStringLiteral("b"),
451  QVariant::fromValue(markSafe(QStringLiteral("a&b"))));
452 
453  QTest::newRow("filter-fix_ampersands02")
454  << QStringLiteral("{{ a|fix_ampersands }} {{ b|fix_ampersands }}") << dict
455  << QStringLiteral("a&amp;amp;b a&amp;b") << NoError;
456 
457  dict.clear();
458  dict.insert(QStringLiteral("a"), QStringLiteral("1.42"));
459  dict.insert(QStringLiteral("b"),
460  QVariant::fromValue(markSafe(QStringLiteral("1.42"))));
461 
462  QTest::newRow("filter-floatformat01")
463  << QStringLiteral("{% autoescape off %}{{ a|floatformat }} {{ "
464  "b|floatformat }}{% endautoescape %}")
465  << dict << QStringLiteral("1.4 1.4") << NoError;
466 
467  dict.clear();
468  dict.insert(QStringLiteral("a"), QStringLiteral("1.42"));
469  dict.insert(QStringLiteral("b"),
470  QVariant::fromValue(markSafe(QStringLiteral("1.42"))));
471 
472  QTest::newRow("filter-floatformat02")
473  << QStringLiteral("{{ a|floatformat }} {{ b|floatformat }}") << dict
474  << QStringLiteral("1.4 1.4") << NoError;
475 
476  dict.clear();
477  dict.insert(QStringLiteral("a"), double(1234.54321));
478  dict.insert(QStringLiteral("b"), int(1234));
479 
480  QTest::newRow("filter-floatformat03")
481  << QStringLiteral("{{ a|floatformat }} {{ b|floatformat }}") << dict
482  << QStringLiteral("1234.5 1234.0") << NoError;
483  QTest::newRow("filter-floatformat04")
484  << QStringLiteral("{{ a|floatformat:2 }} {{ b|floatformat:2 }}") << dict
485  << QStringLiteral("1234.54 1234.00") << NoError;
486  QTest::newRow("filter-floatformat04")
487  << QStringLiteral("{{ a|floatformat:0 }} {{ b|floatformat:0 }}") << dict
488  << QStringLiteral("1235 1234") << NoError;
489 
490  // The contents of "linenumbers" is escaped according to the current
491  // autoescape setting.
492 
493  dict.clear();
494  dict.insert(QStringLiteral("a"), QStringLiteral("one\n<two>\nthree"));
495  dict.insert(
496  QStringLiteral("b"),
497  QVariant::fromValue(markSafe(QStringLiteral("one\n&lt;two&gt;\nthree"))));
498 
499  QTest::newRow("filter-linenumbers01")
500  << QStringLiteral("{{ a|linenumbers }} {{ b|linenumbers }}") << dict
501  << "1. one\n2. &lt;two&gt;\n3. three 1. one\n2. &lt;two&gt;\n3. three"
502  << NoError;
503 
504  dict.clear();
505  dict.insert(QStringLiteral("a"), QStringLiteral("one\n<two>\nthree"));
506  dict.insert(
507  QStringLiteral("b"),
508  QVariant::fromValue(markSafe(QStringLiteral("one\n&lt;two&gt;\nthree"))));
509  QTest::newRow("filter-linenumbers02")
510  << QStringLiteral("{% autoescape off %}{{ a|linenumbers }} {{ "
511  "b|linenumbers }}{% endautoescape %}")
512  << dict << "1. one\n2. <two>\n3. three 1. one\n2. &lt;two&gt;\n3. three"
513  << NoError;
514 
515  dict.clear();
516  dict.insert(QStringLiteral("a"), QStringLiteral("Apple & banana"));
517  dict.insert(QStringLiteral("b"), QVariant::fromValue(markSafe(
518  QStringLiteral("Apple &amp; banana"))));
519 
520  QTest::newRow("filter-lower01") << QStringLiteral(
521  "{% autoescape off %}{{ a|lower }} {{ b|lower }}{% endautoescape %}")
522  << dict
523  << QStringLiteral(
524  "apple & banana apple &amp; banana")
525  << NoError;
526 
527  dict.clear();
528  dict.insert(QStringLiteral("a"), QStringLiteral("Apple & banana"));
529  dict.insert(QStringLiteral("b"), QVariant::fromValue(markSafe(
530  QStringLiteral("Apple &amp; banana"))));
531 
532  QTest::newRow("filter-lower02")
533  << QStringLiteral("{{ a|lower }} {{ b|lower }}") << dict
534  << QStringLiteral("apple &amp; banana apple &amp; banana") << NoError;
535 
536  // The make_list filter can destroy existing escaping, so the results are
537  // escaped.
538 
539  dict.clear();
540  dict.insert(QStringLiteral("a"), markSafe(QStringLiteral("&")));
541 
542  QTest::newRow("filter-make_list01") << QStringLiteral(
543  "{% autoescape off %}{{ a|make_list }}{% endautoescape %}")
544  << dict << "[u\'&\']" << NoError;
545  QTest::newRow("filter-make_list02")
546  << QStringLiteral("{{ a|make_list }}") << dict
547  << QStringLiteral("[u&#39;&amp;&#39;]") << NoError;
548 
549  QTest::newRow("filter-make_list03") << QStringLiteral(
550  "{% autoescape off %}{{ a|make_list|stringformat:\"%1\"|safe }}{% "
551  "endautoescape %}") << dict << QStringLiteral("[u\'&\']")
552  << NoError;
553  QTest::newRow("filter-make_list04")
554  << QStringLiteral("{{ a|make_list|stringformat:\"%1\"|safe }}") << dict
555  << QStringLiteral("[u\'&\']") << NoError;
556 
557  // Running slugify on a pre-escaped string leads to odd behaviour,
558  // but the result is still safe.
559 
560  dict.clear();
561  dict.insert(QStringLiteral("a"), QStringLiteral("a & b"));
562  dict.insert(QStringLiteral("b"), markSafe(QStringLiteral("a &amp; b")));
563 
564  QTest::newRow("filter-slugify01") << QStringLiteral(
565  "{% autoescape off %}{{ a|slugify }} {{ b|slugify }}{% endautoescape %}")
566  << dict << QStringLiteral("a-b a-amp-b")
567  << NoError;
568  QTest::newRow("filter-slugify02")
569  << QStringLiteral("{{ a|slugify }} {{ b|slugify }}") << dict
570  << QStringLiteral("a-b a-amp-b") << NoError;
571 
572  dict.clear();
573  dict.insert(QStringLiteral("a"), QStringLiteral("Schöne Grüße"));
574 
575  QTest::newRow("filter-slugify03") << QStringLiteral("{{ a|slugify }}") << dict
576  << QStringLiteral("schone-grue") << NoError;
577 
578  dict.clear();
579  dict.insert(
580  QStringLiteral("a"),
581  QStringLiteral("testing\r\njavascript \'string\" <b>escaping</b>"));
582  QTest::newRow("escapejs01")
583  << QStringLiteral("{{ a|escapejs }}") << dict
584  << "testing\\u000D\\u000Ajavascript \\u0027string\\u0022 "
585  "\\u003Cb\\u003Eescaping\\u003C/b\\u003E"
586  << NoError;
587  QTest::newRow("escapejs02")
588  << QStringLiteral(
589  "{% autoescape off %}{{ a|escapejs }}{% endautoescape %}")
590  << dict
591  << "testing\\u000D\\u000Ajavascript \\u0027string\\u0022 "
592  "\\u003Cb\\u003Eescaping\\u003C/b\\u003E"
593  << NoError;
594 
595  // Notice that escaping is applied *after* any filters, so the string
596  // formatting here only needs to deal with pre-escaped characters.
597 
598  dict.clear();
599  dict.insert(QStringLiteral("a"), QStringLiteral("a<b"));
600  dict.insert(QStringLiteral("b"),
601  QVariant::fromValue(markSafe(QStringLiteral("a<b"))));
602 
603  QTest::newRow("filter-stringformat01")
604  << "{% autoescape off %}.{{ a|stringformat:\"%1\" }}. .{{ "
605  "b|stringformat:\"%2\" }}.{% endautoescape %}"
606  << dict << QStringLiteral(".a<b. .a<b.") << NoError;
607  QTest::newRow("filter-stringformat02")
608  << ".{{ a|stringformat:\"%1\" }}. .{{ b|stringformat:\"%2\" }}." << dict
609  << QStringLiteral(".a&lt;b. .a<b.") << NoError;
610  QTest::newRow("filter-stringformat03")
611  << ".{{ a|stringformat:\"foo %1 bar\" }}. .{{ b|stringformat:\"baz %2 "
612  "bat\" }}."
613  << dict << QStringLiteral(".foo a&lt;b bar. .baz a<b bat.") << NoError;
614 
615  dict.clear();
616  dict.insert(QStringLiteral("path"), QStringLiteral("www.cutelee.org"));
617  QTest::newRow("filter-stringformat04")
618  << "{% with path|stringformat:\"<a href=\\\"%1\\\">%1</a>\"|safe as "
619  "result %}{{ result }}{% endwith %}"
620  << dict << "<a href=\"www.cutelee.org\">www.cutelee.org</a>" << NoError;
621 
622  dict.clear();
623  dict.insert(QStringLiteral("a"), QStringLiteral("JOE\'S CRAB SHACK"));
624  QTest::newRow("filter-title01")
625  << "{{ a|title }}" << dict << QStringLiteral("Joe&#39;s Crab Shack")
626  << NoError;
627 
628  dict.clear();
629  dict.insert(QStringLiteral("a"), QStringLiteral("555 WEST 53RD STREET"));
630  QTest::newRow("filter-title02")
631  << "{{ a|title }}" << dict << QStringLiteral("555 West 53rd Street")
632  << NoError;
633 
634  dict.clear();
635  dict.insert(QStringLiteral("a"), QStringLiteral("alpha & bravo"));
636  dict.insert(QStringLiteral("b"), QVariant::fromValue(markSafe(
637  QStringLiteral("alpha &amp; bravo"))));
638 
639  QTest::newRow("filter-truncatewords01")
640  << "{% autoescape off %}{{ a|truncatewords:\"2\" }} {{ "
641  "b|truncatewords:\"2\"}}{% endautoescape %}"
642  << dict << QStringLiteral("alpha & ... alpha &amp; ...") << NoError;
643 
644  QTest::newRow("filter-truncatewords02")
645  << "{{ a|truncatewords:\"2\" }} {{ b|truncatewords:\"2\"}}" << dict
646  << QStringLiteral("alpha &amp; ... alpha &amp; ...") << NoError;
647 
648  // The "upper" filter messes up entities (which are case-sensitive),
649  // so it's not safe for non-escaping purposes.
650 
651  dict.clear();
652  dict.insert(QStringLiteral("a"), QStringLiteral("a & b"));
653  dict.insert(QStringLiteral("b"),
654  QVariant::fromValue(markSafe(QStringLiteral("a &amp; b"))));
655 
656  QTest::newRow("filter-upper01") << QStringLiteral(
657  "{% autoescape off %}{{ a|upper }} {{ b|upper }}{% endautoescape %}")
658  << dict << QStringLiteral("A & B A &AMP; B")
659  << NoError;
660  QTest::newRow("filter-upper02")
661  << QStringLiteral("{{ a|upper }} {{ b|upper }}") << dict
662  << QStringLiteral("A &amp; B A &amp;AMP; B") << NoError;
663 
664  // // {"a": "http://example.com/?x=&y=", "b":
665  // mark_safe("http://example.com?x=&amp;y=")
666  // QTest::newRow( "filter-urlize01") << QString::fromLatin1( "{%
667  // autoescape
668  // off %}{{ a|urlize }} {{ b|urlize }}{% endautoescape %}" ) << dict <<
669  // "<a
670  // href=\"http://example.com/?x=&y=\"
671  // rel=\"nofollow\">http://example.com/?x=&y=</a> <a
672  // href=\"http://example.com?x=&amp;y=\"
673  // rel=\"nofollow\">http://example.com?x=&amp;y=</a>" << NoError;
674  //
675  // // {"a": "http://example.com/?x=&y=", "b":
676  // mark_safe("http://example.com?x=&amp;y=")
677  // QTest::newRow( "filter-urlize02") << QString::fromLatin1( "{{ a|urlize
678  // }}
679  // {{ b|urlize }}" ) << dict << "<a href=\"http://example.com/?x=&amp;y=\"
680  // rel=\"nofollow\">http://example.com/?x=&amp;y=</a> <a
681  // href=\"http://example.com?x=&amp;y=\"
682  // rel=\"nofollow\">http://example.com?x=&amp;y=</a>" << NoError;
683  //
684  // // {"a": mark_safe("a &amp; b")
685  // QTest::newRow( "filter-urlize03") << QString::fromLatin1( "{%
686  // autoescape
687  // off %}{{ a|urlize }}{% endautoescape %}" ) << dict <<
688  // QString::fromLatin1( "a &amp; b" ) << NoError;
689  //
690  // // {"a": mark_safe("a &amp; b")
691  // QTest::newRow( "filter-urlize04") << QString::fromLatin1( "{{ a|urlize
692  // }}" ) << dict << QString::fromLatin1( "a &amp; b" ) << NoError;
693  //
694  // // This will lead to a nonsense result, but at least it won't be
695  //
696  // // exploitable for XSS purposes when auto-escaping is on.
697  //
698  // // {"a": "<script>alert(\"foo\")</script>"
699  // QTest::newRow( "filter-urlize05") << QString::fromLatin1( "{%
700  // autoescape
701  // off %}{{ a|urlize }}{% endautoescape %}" ) << dict <<
702  // "<script>alert(\"foo\")</script>" << NoError;
703  //
704  // // {"a": "<script>alert(\"foo\")</script>"
705  // QTest::newRow( "filter-urlize06") << QString::fromLatin1( "{{ a|urlize
706  // }}" ) << dict << QString::fromLatin1(
707  // "&lt;script&gt;alert(&#39;foo&#39;)&lt;/script&gt;" ) << NoError;
708  //
709  // // mailto: testing for urlize
710  //
711  // // {"a": "Email me at me@example.com"
712  // QTest::newRow( "filter-urlize07") << QString::fromLatin1( "{{ a|urlize
713  // }}" ) << dict << "Email me at <a
714  // href=\"mailto:me@example.com\">me@example.com</a>" << NoError;
715  //
716  // // {"a": "Email me at <me@example.com>"
717  // QTest::newRow( "filter-urlize08") << QString::fromLatin1( "{{ a|urlize
718  // }}" ) << dict << "Email me at &lt;<a
719  // href=\"mailto:me@example.com\">me@example.com</a>&gt;" << NoError;
720  //
721  // // {"a": "\"Unsafe\" http://example.com/x=&y=", "b":
722  // mark_safe("&quot;Safe&quot; http://example.com?x=&amp;y=")
723  // QTest::newRow( "filter-urlizetrunc01") << "{% autoescape off %}{{
724  // a|urlizetrunc:\"8\" }} {{ b|urlizetrunc:\"8\" }}{% endautoescape %}" <<
725  // dict << "\"Unsafe\" <a href=\"http://example.com/x=&y=\"
726  // rel=\"nofollow\">http:...</a> &quot;Safe&quot; <a
727  // href=\"http://example.com?x=&amp;y=\" rel=\"nofollow\">http:...</a>" <<
728  // NoError;
729  //
730  // // {"a": "\"Unsafe\" http://example.com/x=&y=", "b":
731  // mark_safe("&quot;Safe&quot; http://example.com?x=&amp;y=")
732  // QTest::newRow( "filter-urlizetrunc02") << "{{ a|urlizetrunc:\"8\" }} {{
733  // b|urlizetrunc:\"8\" }}" << dict << "&quot;Unsafe&quot; <a
734  // href=\"http://example.com/x=&amp;y=\" rel=\"nofollow\">http:...</a>
735  // &quot;Safe&quot; <a href=\"http://example.com?x=&amp;y=\"
736  // rel=\"nofollow\">http:...</a>" << NoError;
737 
738  // // Ensure iriencode keeps safe strings:
739  //
740  // // {"url": "?test=1&me=2"
741  // QTest::newRow( "filter-iriencode01") << QString::fromLatin1( "{{
742  // url|iriencode }}" ) << dict << QString::fromLatin1( "?test=1&amp;me=2"
743  // )
744  // << NoError;
745  //
746  // // {"url": "?test=1&me=2"
747  // QTest::newRow( "filter-iriencode02") << QString::fromLatin1( "{%
748  // autoescape off %}{{ url|iriencode }}{% endautoescape %}" ) << dict <<
749  // QString::fromLatin1( "?test=1&me=2" ) << NoError;
750  //
751  // // {"url": mark_safe("?test=1&me=2")
752  // QTest::newRow( "filter-iriencode03") << QString::fromLatin1( "{{
753  // url|iriencode }}" ) << dict << QString::fromLatin1( "?test=1&me=2" ) <<
754  // NoError;
755  //
756  // // {"url": mark_safe("?test=1&me=2")
757  // QTest::newRow( "filter-iriencode04") << QString::fromLatin1( "{%
758  // autoescape off %}{{ url|iriencode }}{% endautoescape %}" ) << dict <<
759  // QString::fromLatin1( "?test=1&me=2" ) << NoError;
760  //
761  dict.clear();
762  dict.insert(QStringLiteral("a"), QStringLiteral("a & b"));
763  dict.insert(QStringLiteral("b"),
764  QVariant::fromValue(markSafe(QStringLiteral("a &amp; b"))));
765 
766  QTest::newRow("filter-wordcount01")
767  << QStringLiteral("{% autoescape off %}{{ a|wordcount }} {{ b|wordcount "
768  "}}{% endautoescape %}")
769  << dict << QStringLiteral("3 3") << NoError;
770 
771  QTest::newRow("filter-wordcount02")
772  << QStringLiteral("{{ a|wordcount }} {{ b|wordcount }}") << dict
773  << QStringLiteral("3 3") << NoError;
774 
775  dict.clear();
776  dict.insert(QStringLiteral("a"), QStringLiteral("a & b"));
777  dict.insert(QStringLiteral("b"),
778  QVariant::fromValue(markSafe(QStringLiteral("a & b"))));
779 
780  QTest::newRow("filter-wordwrap01")
781  << QStringLiteral("{% autoescape off %}{{ a|wordwrap:3 }} {{ "
782  "b|wordwrap:3 }}{% endautoescape %}")
783  << dict << "a &\nb a &\nb" << NoError;
784 
785  QTest::newRow("filter-wordwrap02")
786  << QStringLiteral("{{ a|wordwrap:3 }} {{ b|wordwrap:3 }}") << dict
787  << "a &amp;\nb a &\nb" << NoError;
788 
789  dict.clear();
790  QTest::newRow("filter-wordwrap03")
791  << QStringLiteral("{{xx|wordwrap}}") << dict << "" << NoError;
792 
793  QTest::newRow("filter-wordwrap04")
794  << QStringLiteral("{{|wordwrap}}") << dict << "" << NoError;
795 
796  dict.clear();
797  dict.insert(QStringLiteral("a"), QStringLiteral("a&b"));
798  dict.insert(QStringLiteral("b"),
799  QVariant::fromValue(markSafe(QStringLiteral("a&b"))));
800 
801  QTest::newRow("filter-ljust01")
802  << "{% autoescape off %}.{{ a|ljust:\"5\" }}. .{{ b|ljust:\"5\" }}.{% "
803  "endautoescape %}"
804  << dict << QStringLiteral(".a&b . .a&b .") << NoError;
805 
806  QTest::newRow("filter-ljust02")
807  << ".{{ a|ljust:\"5\" }}. .{{ b|ljust:\"5\" }}." << dict
808  << QStringLiteral(".a&amp;b . .a&b .") << NoError;
809 
810  QTest::newRow("filter-rjust01")
811  << "{% autoescape off %}.{{ a|rjust:\"5\" }}. .{{ b|rjust:\"5\" }}.{% "
812  "endautoescape %}"
813  << dict << QStringLiteral(". a&b. . a&b.") << NoError;
814 
815  QTest::newRow("filter-rjust02")
816  << ".{{ a|rjust:\"5\" }}. .{{ b|rjust:\"5\" }}." << dict
817  << QStringLiteral(". a&amp;b. . a&b.") << NoError;
818 
819  QTest::newRow("filter-center01")
820  << "{% autoescape off %}.{{ a|center:\"5\" }}. .{{ b|center:\"5\" "
821  "}}.{% "
822  "endautoescape %}"
823  << dict << QStringLiteral(". a&b . . a&b .") << NoError;
824 
825  QTest::newRow("filter-center02")
826  << ".{{ a|center:\"5\" }}. .{{ b|center:\"5\" }}." << dict
827  << QStringLiteral(". a&amp;b . . a&b .") << NoError;
828 
829  dict.clear();
830  dict.insert(QStringLiteral("a"), QStringLiteral("x&y"));
831  dict.insert(QStringLiteral("b"),
832  QVariant::fromValue(markSafe(QStringLiteral("x&amp;y"))));
833 
834  QTest::newRow("filter-cut01")
835  << "{% autoescape off %}{{ a|cut:\"x\" }} {{ "
836  "b|cut:\"x\" }}{% endautoescape %}"
837  << dict << QStringLiteral("&y &amp;y") << NoError;
838  QTest::newRow("filter-cut02") << "{{ a|cut:\"x\" }} {{ b|cut:\"x\" }}" << dict
839  << QStringLiteral("&amp;y &amp;y") << NoError;
840  QTest::newRow("filter-cut03")
841  << "{% autoescape off %}{{ a|cut:\"&\" }} {{ "
842  "b|cut:\"&\" }}{% endautoescape %}"
843  << dict << QStringLiteral("xy xamp;y") << NoError;
844  QTest::newRow("filter-cut04") << "{{ a|cut:\"&\" }} {{ b|cut:\"&\" }}" << dict
845  << QStringLiteral("xy xamp;y") << NoError;
846 
847  // Passing ";" to cut can break existing HTML entities, so those strings
848  // are auto-escaped.
849 
850  QTest::newRow("filter-cut05")
851  << "{% autoescape off %}{{ a|cut:\";\" }} {{ "
852  "b|cut:\";\" }}{% endautoescape %}"
853  << dict << QStringLiteral("x&y x&ampy") << NoError;
854  QTest::newRow("filter-cut06")
855  << "{{ a|cut:\";\" }} {{ b|cut:\";\" }}" << dict
856  << QStringLiteral("x&amp;y x&amp;ampy") << NoError;
857 
858  // The "escape" filter works the same whether autoescape is on or off,
859  // but it has no effect on strings already marked as safe.
860 
861  dict.clear();
862  dict.insert(QStringLiteral("a"), QStringLiteral("x&y"));
863  dict.insert(QStringLiteral("b"),
864  QVariant::fromValue(markSafe(QStringLiteral("x&y"))));
865 
866  QTest::newRow("filter-escape01")
867  << QStringLiteral("{{ a|escape }} {{ b|escape }}") << dict
868  << QStringLiteral("x&amp;y x&y") << NoError;
869  QTest::newRow("filter-escape02") << QStringLiteral(
870  "{% autoescape off %}{{ a|escape }} {{ b|escape }}{% endautoescape %}")
871  << dict << QStringLiteral("x&amp;y x&y")
872  << NoError;
873 
874  // It is only applied once, regardless of the number of times it
875  // appears in a chain.
876 
877  dict.clear();
878  dict.insert(QStringLiteral("a"), QStringLiteral("x&y"));
879 
880  QTest::newRow("filter-escape03")
881  << QStringLiteral(
882  "{% autoescape off %}{{ a|escape|escape }}{% endautoescape %}")
883  << dict << QStringLiteral("x&amp;y") << NoError;
884  QTest::newRow("filter-escape04")
885  << QStringLiteral("{{ a|escape|escape }}") << dict
886  << QStringLiteral("x&amp;y") << NoError;
887 
888 
889  // Force_escape is applied immediately. It can be used to provide
890  // double-escaping, for example.
891 
892  QTest::newRow("filter-force-escape01")
893  << QStringLiteral(
894  "{% autoescape off %}{{ a|force_escape }}{% endautoescape %}")
895  << dict << QStringLiteral("x&amp;y") << NoError;
896  QTest::newRow("filter-force-escape02")
897  << QStringLiteral("{{ a|force_escape }}") << dict
898  << QStringLiteral("x&amp;y") << NoError;
899 
900  QTest::newRow("filter-force-escape03")
901  << QStringLiteral("{% autoescape off %}{{ a|force_escape|force_escape "
902  "}}{% endautoescape %}")
903  << dict << QStringLiteral("x&amp;amp;y") << NoError;
904  QTest::newRow("filter-force-escape04")
905  << QStringLiteral("{{ a|force_escape|force_escape }}") << dict
906  << QStringLiteral("x&amp;amp;y") << NoError;
907 
908  // Because the result of force_escape is "safe", an additional
909  // escape filter has no effect.
910 
911  QTest::newRow("filter-force-escape05") << QStringLiteral(
912  "{% autoescape off %}{{ a|force_escape|escape }}{% endautoescape %}")
913  << dict << QStringLiteral("x&amp;y")
914  << NoError;
915  QTest::newRow("filter-force-escape06")
916  << QStringLiteral("{{ a|force_escape|escape }}") << dict
917  << QStringLiteral("x&amp;y") << NoError;
918  QTest::newRow("filter-force-escape07") << QStringLiteral(
919  "{% autoescape off %}{{ a|escape|force_escape }}{% endautoescape %}")
920  << dict << QStringLiteral("x&amp;y")
921  << NoError;
922  QTest::newRow("filter-force-escape08")
923  << QStringLiteral("{{ a|escape|force_escape }}") << dict
924  << QStringLiteral("x&amp;y") << NoError;
925 
926  // The contents in "linebreaks" and "linebreaksbr" are escaped
927  // according to the current autoescape setting.
928 
929  dict.clear();
930  dict.insert(QStringLiteral("a"), QStringLiteral("x&\ny"));
931  dict.insert(QStringLiteral("b"), markSafe(QStringLiteral("x&\ny")));
932 
933  QTest::newRow("filter-linebreaks01")
934  << QStringLiteral("{{ a|linebreaks }} {{ b|linebreaks }}") << dict
935  << QStringLiteral("<p>x&amp;<br />y</p> <p>x&<br />y</p>") << NoError;
936  QTest::newRow("filter-linebreaks02")
937  << QStringLiteral("{% autoescape off %}{{ a|linebreaks }} {{ "
938  "b|linebreaks }}{% endautoescape %}")
939  << dict << QStringLiteral("<p>x&<br />y</p> <p>x&<br />y</p>") << NoError;
940  QTest::newRow("filter-linebreaksbr01")
941  << QStringLiteral("{{ a|linebreaksbr }} {{ b|linebreaksbr }}") << dict
942  << QStringLiteral("x&amp;<br />y x&<br />y") << NoError;
943  QTest::newRow("filter-linebreaksbr02")
944  << QStringLiteral("{% autoescape off %}{{ a|linebreaksbr }} {{ "
945  "b|linebreaksbr }}{% endautoescape %}")
946  << dict << QStringLiteral("x&<br />y x&<br />y") << NoError;
947 
948  dict.clear();
949  dict.insert(QStringLiteral("a"), QStringLiteral("<b>hello</b>"));
950 
951  QTest::newRow("filter-safe01")
952  << QStringLiteral("{{ a }} -- {{ a|safe }}") << dict
953  << QStringLiteral("&lt;b&gt;hello&lt;/b&gt; -- <b>hello</b>") << NoError;
954  QTest::newRow("filter-safe02")
955  << QStringLiteral(
956  "{% autoescape off %}{{ a }} -- {{ a|safe }}{% endautoescape %}")
957  << dict << QStringLiteral("<b>hello</b> -- <b>hello</b>") << NoError;
958 
959  dict.clear();
960  dict.insert(QStringLiteral("a"),
961  QVariantList{QStringLiteral("&"), QStringLiteral("<")});
962 
963  QTest::newRow("filter-safeseq01")
964  << "{{ a|join:\", \" }} -- {{ a|safeseq|join:\", \" }}" << dict
965  << QStringLiteral("&amp;, &lt; -- &, <") << NoError;
966  QTest::newRow("filter-safeseq02")
967  << "{% autoescape off %}{{ a|join:\", \" "
968  "}} -- {{ a|safeseq|join:\", \" "
969  "}}{% endautoescape %}"
970  << dict << QStringLiteral("&, < -- &, <") << NoError;
971 
972  dict.clear();
973  dict.insert(QStringLiteral("a"), QStringLiteral("<a>x</a> <p><b>y</b></p>"));
974  dict.insert(QStringLiteral("b"), QVariant::fromValue(markSafe(QStringLiteral(
975  "<a>x</a> <p><b>y</b></p>"))));
976 
977  QTest::newRow("filter-removetags01")
978  << "{{ a|removetags:\"a b\" }} {{ b|removetags:\"a b\" }}" << dict
979  << QStringLiteral("x &lt;p&gt;y&lt;/p&gt; x <p>y</p>") << NoError;
980  QTest::newRow("filter-removetags02")
981  << "{% autoescape off %}{{ a|removetags:\"a b\" }} {{ b|removetags:\"a "
982  "b\" }}{% endautoescape %}"
983  << dict << QStringLiteral("x <p>y</p> x <p>y</p>") << NoError;
984  QTest::newRow("filter-striptags01")
985  << QStringLiteral("{{ a|striptags }} {{ b|striptags }}") << dict
986  << QStringLiteral("x y x y") << NoError;
987  QTest::newRow("filter-striptags02")
988  << QStringLiteral("{% autoescape off %}{{ a|striptags }} {{ b|striptags "
989  "}}{% endautoescape %}")
990  << dict << QStringLiteral("x y x y") << NoError;
991 
992  dict.clear();
993  dict.insert(QStringLiteral("fs_int_mib"), 1048576);
994 
995  QTest::newRow("filter-filesizeformat01")
996  << QStringLiteral("{{ fs_int_mib|filesizeformat }}") << dict
997  << QStringLiteral("1.05 MB") << NoError;
998 
999  QTest::newRow("filter-filesizeformat02")
1000  << QStringLiteral("{{ fs_int_mib|filesizeformat:\"2\" }}") << dict
1001  << QStringLiteral("1.00 MiB") << NoError;
1002 
1003  QTest::newRow("filter-filesizeformat03")
1004  << QStringLiteral("{{ fs_int_mib|filesizeformat:\"10,3\" }}") << dict
1005  << QStringLiteral("1.049 MB") << NoError;
1006 
1007  QTest::newRow("filter-filesizeformat04")
1008  << QStringLiteral("{{ fs_int_mib|filesizeformat:\"10,2,1024\" }}") << dict
1009  << QStringLiteral("1.07 GB") << NoError;
1010 
1011  dict.clear();
1012  dict.insert(QStringLiteral("fs_float_mib"), 1024.5);
1013 
1014  QTest::newRow("filter-filesizeformat05")
1015  << QStringLiteral("{{ fs_float_mib|filesizeformat:\"10,2,1024\" }}") << dict
1016  << QStringLiteral("1.05 MB") << NoError;
1017 
1018  dict.clear();
1019  dict.insert(QStringLiteral("fs_string_mib"), QStringLiteral("1024.5"));
1020 
1021  QTest::newRow("filter-filesizeformat06")
1022  << QStringLiteral("{{ fs_string_mib|filesizeformat:\"10,2,1024\" }}") << dict
1023  << QStringLiteral("1.05 MB") << NoError;
1024 
1025  dict.clear();
1026  dict.insert(QStringLiteral("fs_bytes"), 999);
1027  dict.insert(QStringLiteral("fs_kb"), 1000);
1028  dict.insert(QStringLiteral("fs_10kb"), 10 * 1000);
1029  dict.insert(QStringLiteral("fs_1000kb"), 1000 * 1000 -1);
1030  dict.insert(QStringLiteral("fs_mb"), 1000 * 1000);
1031  dict.insert(QStringLiteral("fs_50mb"), 1000 * 1000 * 50);
1032  dict.insert(QStringLiteral("fs_1000mb"), 1000 * 1000 * 1000 - 1);
1033  dict.insert(QStringLiteral("fs_gb"), 1000 * 1000 * 1000);
1034  dict.insert(QStringLiteral("fs_tb"), static_cast<double>(1000.0 * 1000.0 * 1000.0 * 1000.0));
1035  dict.insert(QStringLiteral("fs_pb"), static_cast<double>(1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0));
1036  dict.insert(QStringLiteral("fs_eb"), static_cast<double>(1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 2000.0));
1037  dict.insert(QStringLiteral("fs_zb"), static_cast<double>(1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0));
1038  dict.insert(QStringLiteral("fs_yb"), static_cast<double>(1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0));
1039  dict.insert(QStringLiteral("fs_2000yb"), static_cast<double>(1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 2000.0));
1040  dict.insert(QStringLiteral("fs_0b1"), 0.1);
1041  dict.insert(QStringLiteral("fs_0b2"), QString(QChar(0x03B1)));
1042  dict.insert(QStringLiteral("fs_neg_1"), -100);
1043  dict.insert(QStringLiteral("fs_neg_2"), -1000 * 1000 *50);
1044 
1045  // fixes tests on MSVC2013
1046  QString fsInput;
1047  fsInput = QStringLiteral("{{ fs_bytes|filesizeformat }} {{ fs_kb|filesizeformat }} {{ fs_10kb|filesizeformat }} {{ fs_1000kb|filesizeformat }} ");
1048  fsInput += QStringLiteral("{{ fs_mb|filesizeformat }} {{ fs_50mb|filesizeformat }} {{ fs_1000mb|filesizeformat }} {{ fs_gb|filesizeformat }} ");
1049  fsInput += QStringLiteral("{{ fs_tb|filesizeformat }} {{ fs_pb|filesizeformat }} {{ fs_eb|filesizeformat }} {{ fs_zb|filesizeformat }} ");
1050  fsInput += QStringLiteral("{{ fs_yb|filesizeformat }} {{ fs_2000yb|filesizeformat }} {{ fs_0b1|filesizeformat }} {{ fs_0b2|filesizeformat }} ");
1051  fsInput += QStringLiteral("{{ fs_neg_1|filesizeformat }} {{ fs_neg_2|filesizeformat }}");
1052 
1053  QString fsExpect;
1054  fsExpect = QStringLiteral("999 bytes 1.00 KB 10.00 KB 1000.00 KB ");
1055  fsExpect += QStringLiteral("1.00 MB 50.00 MB 1000.00 MB 1.00 GB ");
1056  fsExpect += QStringLiteral("1.00 TB 1.00 PB 2.00 EB 1.00 ZB ");
1057  fsExpect += QStringLiteral("1.00 YB 2000.00 YB 0 bytes 0 bytes ");
1058  fsExpect += QStringLiteral("-100 bytes -50.00 MB");
1059 
1060  QTest::newRow("filter-filesizeformat07")
1061  << fsInput
1062  << dict
1063  << fsExpect
1064  << NoError;
1065 }
1066 
1067 void TestFilters::testListFilters_data()
1068 {
1069  QTest::addColumn<QString>("input");
1070  QTest::addColumn<Dict>("dict");
1071  QTest::addColumn<QString>("output");
1072  QTest::addColumn<Cutelee::Error>("error");
1073 
1074  Dict dict;
1075 
1076  dict.insert(QStringLiteral("a"),
1077  QVariantList{QStringLiteral("a&b"), QStringLiteral("x")});
1078  dict.insert(QStringLiteral("b"),
1079  QVariantList{QVariant::fromValue(markSafe(QStringLiteral("a&b"))),
1080  QStringLiteral("x")});
1081 
1082  QTest::newRow("filter-first01")
1083  << QStringLiteral("{{ a|first }} {{ b|first }}") << dict
1084  << QStringLiteral("a&amp;b a&b") << NoError;
1085  QTest::newRow("filter-first02") << QStringLiteral(
1086  "{% autoescape off %}{{ a|first }} {{ b|first }}{% endautoescape %}")
1087  << dict << QStringLiteral("a&b a&b")
1088  << NoError;
1089 
1090  dict.clear();
1091  dict.insert(QStringLiteral("a"),
1092  QVariantList{QStringLiteral("x"), QStringLiteral("a&b")});
1093  dict.insert(QStringLiteral("b"),
1094  QVariantList{QStringLiteral("x"), QVariant::fromValue(markSafe(
1095  QStringLiteral("a&b")))});
1096 
1097  QTest::newRow("filter-last01")
1098  << QStringLiteral("{{ a|last }} {{ b|last }}") << dict
1099  << QStringLiteral("a&amp;b a&b") << NoError;
1100  QTest::newRow("filter-last02")
1101  << QStringLiteral(
1102  "{% autoescape off %}{{ a|last }} {{ b|last }}{% endautoescape %}")
1103  << dict << QStringLiteral("a&b a&b") << NoError;
1104 
1105  dict.clear();
1106  dict.insert(QStringLiteral("a"),
1107  QVariantList{QStringLiteral("a&b"), QStringLiteral("a&b")});
1108  dict.insert(QStringLiteral("b"),
1109  QVariantList()
1110  << QVariant::fromValue(markSafe(QStringLiteral("a&b")))
1111  << QVariant::fromValue(markSafe(QStringLiteral("a&b"))));
1112  QTest::newRow("filter-random01")
1113  << QStringLiteral("{{ a|random }} {{ b|random }}") << dict
1114  << QStringLiteral("a&amp;b a&b") << NoError;
1115  QTest::newRow("filter-random02") << QStringLiteral(
1116  "{% autoescape off %}{{ a|random }} {{ b|random }}{% endautoescape %}")
1117  << dict << QStringLiteral("a&b a&b")
1118  << NoError;
1119 
1120  dict.clear();
1121  dict.insert(QStringLiteral("empty_list"), QVariantList());
1122  QTest::newRow("filter-random03") << QStringLiteral("{{empty_list|random}}")
1123  << dict << QStringLiteral("") << NoError;
1124  QTest::newRow("filter-random04")
1125  << QStringLiteral("{{|random}}") << dict << QStringLiteral("") << NoError;
1126 
1127  dict.clear();
1128  dict.insert(QStringLiteral("a"), QStringLiteral("a&b"));
1129  dict.insert(QStringLiteral("b"),
1130  QVariant::fromValue(markSafe(QStringLiteral("a&b"))));
1131 
1132  QTest::newRow("filter-slice01")
1133  << "{{ a|slice:\"1:3\" }} {{ b|slice:\"1:3\" }}" << dict
1134  << QStringLiteral("&amp;b &b") << NoError;
1135  QTest::newRow("filter-slice02")
1136  << "{% autoescape off %}{{ a|slice:\"1:3\" }} {{ b|slice:\"1:3\" }}{% "
1137  "endautoescape %}"
1138  << dict << QStringLiteral("&b &b") << NoError;
1139 
1140  dict.clear();
1141  QTest::newRow("filter-slice03")
1142  << "{{xx|slice}}" << dict << QStringLiteral("") << NoError;
1143  QTest::newRow("filter-slice04")
1144  << "{{|slice}}" << dict << QStringLiteral("") << NoError;
1145 
1146  dict.clear();
1147  QVariantList sublist{QStringLiteral("<y")};
1148  dict.insert(QStringLiteral("a"),
1149  QVariantList{QStringLiteral("x>"), QVariant(sublist)});
1150 
1151  QTest::newRow("filter-unordered_list01")
1152  << QStringLiteral("{{ a|unordered_list }}") << dict
1153  << "\t<li>x&gt;\n\t<ul>\n\t\t<li>&lt;y</li>\n\t</ul>\n\t</li>" << NoError;
1154  QTest::newRow("filter-unordered_list02")
1155  << QStringLiteral(
1156  "{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}")
1157  << dict << "\t<li>x>\n\t<ul>\n\t\t<li><y</li>\n\t</ul>\n\t</li>"
1158  << NoError;
1159 
1160  dict.clear();
1161  sublist = {markSafe(QStringLiteral("<y"))};
1162  dict.insert(QStringLiteral("a"),
1163  QVariantList{QStringLiteral("x>"), QVariant(sublist)});
1164 
1165  QTest::newRow("filter-unordered_list03")
1166  << QStringLiteral("{{ a|unordered_list }}") << dict
1167  << "\t<li>x&gt;\n\t<ul>\n\t\t<li><y</li>\n\t</ul>\n\t</li>" << NoError;
1168  QTest::newRow("filter-unordered_list04")
1169  << QStringLiteral(
1170  "{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}")
1171  << dict << "\t<li>x>\n\t<ul>\n\t\t<li><y</li>\n\t</ul>\n\t</li>"
1172  << NoError;
1173 
1174  dict.clear();
1175  sublist = {QStringLiteral("<y")};
1176  dict.insert(QStringLiteral("a"),
1177  QVariantList{QStringLiteral("x>"), QVariant(sublist)});
1178 
1179  QTest::newRow("filter-unordered_list05")
1180  << QStringLiteral(
1181  "{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}")
1182  << dict << "\t<li>x>\n\t<ul>\n\t\t<li><y</li>\n\t</ul>\n\t</li>"
1183  << NoError;
1184 
1185  // length filter.
1186  dict.clear();
1187  dict.insert(
1188  QStringLiteral("list"),
1189  QVariantList{QStringLiteral("4"), QVariant(), true, QVariantHash()});
1190 
1191  QTest::newRow("length01") << QStringLiteral("{{ list|length }}") << dict
1192  << QStringLiteral("4") << NoError;
1193 
1194  dict.clear();
1195  dict.insert(QStringLiteral("list"), QVariantList());
1196 
1197  QTest::newRow("length02") << QStringLiteral("{{ list|length }}") << dict
1198  << QStringLiteral("0") << NoError;
1199 
1200  dict.clear();
1201  dict.insert(QStringLiteral("string"), QStringLiteral(""));
1202 
1203  QTest::newRow("length03") << QStringLiteral("{{ string|length }}") << dict
1204  << QStringLiteral("0") << NoError;
1205 
1206  dict.clear();
1207  dict.insert(QStringLiteral("string"), QStringLiteral("django"));
1208 
1209  QTest::newRow("length04") << QStringLiteral("{{ string|length }}") << dict
1210  << QStringLiteral("6") << NoError;
1211 
1212  // Invalid uses that should fail silently.
1213 
1214  dict.clear();
1215  dict.insert(QStringLiteral("int"), 7);
1216 
1217  QTest::newRow("length05")
1218  << QStringLiteral("{{ int|length }}") << dict << QString() << NoError;
1219 
1220  dict.clear();
1221  dict.insert(QStringLiteral("None"), QVariant());
1222 
1223  QTest::newRow("length06")
1224  << QStringLiteral("{{ None|length }}") << dict << QString() << NoError;
1225 
1226  // length_is filter.
1227 
1228  dict.clear();
1229  dict.insert(
1230  QStringLiteral("some_list"),
1231  QVariantList{QStringLiteral("4"), QVariant(), true, QVariantHash()});
1232 
1233  QTest::newRow("length_is01")
1234  << "{% if some_list|length_is:\"4\" %}Four{% endif %}" << dict
1235  << QStringLiteral("Four") << NoError;
1236 
1237  dict.clear();
1238  dict.insert(
1239  QStringLiteral("some_list"),
1240  QVariantList{QStringLiteral("4"), QVariant(), true, QVariantHash(), 17});
1241 
1242  QTest::newRow("length_is02")
1243  << "{% if some_list|length_is:\"4\" %}Four{% else %}Not Four{% endif %}"
1244  << dict << QStringLiteral("Not Four") << NoError;
1245 
1246  dict.clear();
1247  dict.insert(QStringLiteral("mystring"), QStringLiteral("word"));
1248 
1249  QTest::newRow("length_is03")
1250  << "{% if mystring|length_is:\"4\" %}Four{% endif %}" << dict
1251  << QStringLiteral("Four") << NoError;
1252 
1253  dict.clear();
1254  dict.insert(QStringLiteral("mystring"), QStringLiteral("Python"));
1255 
1256  QTest::newRow("length_is04")
1257  << "{% if mystring|length_is:\"4\" %}Four{% else %}Not Four{% endif %}"
1258  << dict << QStringLiteral("Not Four") << NoError;
1259 
1260  dict.clear();
1261  dict.insert(QStringLiteral("mystring"), QStringLiteral(""));
1262 
1263  QTest::newRow("length_is05")
1264  << "{% if mystring|length_is:\"4\" %}Four{% else %}Not Four{% endif %}"
1265  << dict << QStringLiteral("Not Four") << NoError;
1266 
1267  dict.clear();
1268  dict.insert(QStringLiteral("var"), QStringLiteral("django"));
1269 
1270  QTest::newRow("length_is06") << QStringLiteral(
1271  "{% with var|length as my_length %}{{ my_length }}{% endwith %}")
1272  << dict << QStringLiteral("6") << NoError;
1273 
1274  // Boolean return value from length_is should not be coerced to a string
1275 
1276  dict.clear();
1277  QTest::newRow("length_is07")
1278  << "{% if \"X\"|length_is:0 %}Length is 0{% "
1279  "else %}Length not 0{% endif %}"
1280  << dict << QStringLiteral("Length not 0") << NoError;
1281  QTest::newRow("length_is08")
1282  << "{% if \"X\"|length_is:1 %}Length is 1{% "
1283  "else %}Length not 1{% endif %}"
1284  << dict << QStringLiteral("Length is 1") << NoError;
1285 
1286  // Invalid uses that should fail silently.
1287 
1288  dict.clear();
1289  dict.insert(QStringLiteral("var"), QStringLiteral("django"));
1290 
1291  QTest::newRow("length_is09")
1292  << "{{ var|length_is:\"fish\" }}" << dict << QString() << NoError;
1293 
1294  dict.clear();
1295  dict.insert(QStringLiteral("int"), 7);
1296 
1297  QTest::newRow("length_is10")
1298  << "{{ int|length_is:\"1\" }}" << dict << QString() << NoError;
1299 
1300  dict.clear();
1301  dict.insert(QStringLiteral("none"), QVariant());
1302 
1303  QTest::newRow("length_is11")
1304  << "{{ none|length_is:\"1\" }}" << dict << QString() << NoError;
1305 
1306  dict.clear();
1307  dict.insert(QStringLiteral("a"), QVariantList{QStringLiteral("alpha"),
1308  QStringLiteral("beta & me")});
1309 
1310  QTest::newRow("join01") << "{{ a|join:\", \" }}" << dict
1311  << QStringLiteral("alpha, beta &amp; me") << NoError;
1312  QTest::newRow("join02")
1313  << "{% autoescape off %}{{ a|join:\", \" }}{% endautoescape %}" << dict
1314  << QStringLiteral("alpha, beta & me") << NoError;
1315  QTest::newRow("join03") << "{{ a|join:\" &amp; \" }}" << dict
1316  << QStringLiteral("alpha &amp; beta &amp; me")
1317  << NoError;
1318  QTest::newRow("join04")
1319  << "{% autoescape off %}{{ a|join:\" &amp; \" }}{% endautoescape %}"
1320  << dict << QStringLiteral("alpha &amp; beta & me") << NoError;
1321 
1322  // Test that joining with unsafe joiners don't result in unsafe strings
1323  // (#11377)
1324  dict.insert(QStringLiteral("var"), QStringLiteral(" & "));
1325  QTest::newRow("join05") << "{{ a|join:var }}" << dict
1326  << QStringLiteral("alpha &amp; beta &amp; me")
1327  << NoError;
1328  dict.insert(QStringLiteral("var"), Cutelee::markSafe(QStringLiteral(" & ")));
1329  QTest::newRow("join06") << "{{ a|join:var }}" << dict
1330  << QStringLiteral("alpha & beta &amp; me") << NoError;
1331  dict.insert(QStringLiteral("a"), QVariantList{QStringLiteral("Alpha"),
1332  QStringLiteral("Beta & Me")});
1333  dict.insert(QStringLiteral("var"), QStringLiteral(" & "));
1334  QTest::newRow("join07") << "{{ a|join:var|lower }}" << dict
1335  << QStringLiteral("alpha &amp; beta &amp; me")
1336  << NoError;
1337 
1338  dict.insert(QStringLiteral("a"), QVariantList{QStringLiteral("Alpha"),
1339  QStringLiteral("Beta & Me")});
1340  dict.insert(QStringLiteral("var"), Cutelee::markSafe(QStringLiteral(" & ")));
1341  QTest::newRow("join08") << "{{ a|join:var|lower }}" << dict
1342  << QStringLiteral("alpha & beta &amp; me") << NoError;
1343 
1344  // arguments to filters are intended to be used unescaped.
1345  // dict.clear();
1346  // dict.insert( QStringLiteral("a"), QVariantList() <<
1347  // QString::fromLatin1( "alpha" ) << QString::fromLatin1( "beta & me" ) );
1348  // dict.insert( QStringLiteral("var"), QStringLiteral(" & ") );
1349  //
1350  // QTest::newRow( "join05" ) << QString::fromLatin1( "{{ a|join:var }}" )
1351  // <<
1352  // dict << QString::fromLatin1( "alpha &amp; beta &amp; me" ) << NoError;
1353  //
1354  // dict.clear();
1355  // dict.insert( QStringLiteral("a"), QVariantList() <<
1356  // QString::fromLatin1( "alpha" ) << QString::fromLatin1( "beta & me" ) );
1357  // dict.insert( QStringLiteral("var"), QVariant::fromValue( markSafe(
1358  // QString::fromLatin1( " & " ) ) ) );
1359  //
1360  // QTest::newRow( "join06" ) << QString::fromLatin1( "{{ a|join:var }}" )
1361  // <<
1362  // dict << QString::fromLatin1( "alpha & beta &amp; me" ) << NoError;
1363  //
1364  // dict.clear();
1365  // dict.insert( QStringLiteral("a"), QVariantList() <<
1366  // QString::fromLatin1( "Alpha" ) << QString::fromLatin1( "Beta & me" ) );
1367  // dict.insert( QStringLiteral("var"), QStringLiteral(" & ") );
1368  //
1369  // QTest::newRow( "join07" ) << QString::fromLatin1( "{{ a|join:var|lower
1370  // }}" ) << dict << QString::fromLatin1( "alpha &amp; beta &amp; me" ) <<
1371  // NoError;
1372  //
1373  // dict.clear();
1374  // dict.insert( QStringLiteral("a"), QVariantList() <<
1375  // QString::fromLatin1( "Alpha" ) << QString::fromLatin1( "Beta & me" ) );
1376  // dict.insert( QStringLiteral("var"), QVariant::fromValue( markSafe(
1377  // QString::fromLatin1( " & " ) ) ) );
1378  //
1379  // QTest::newRow( "join08" ) << QString::fromLatin1( "{{ a|join:var|lower
1380  // }}" ) << dict << QString::fromLatin1( "alpha & beta &amp; me" ) <<
1381  // NoError;
1382 
1383  dict.clear();
1384 
1385  QVariantList mapList;
1386  const auto cities
1387  = QStringList{QStringLiteral("London"), QStringLiteral("Berlin"),
1388  QStringLiteral("Paris"), QStringLiteral("Dublin")};
1389  Q_FOREACH (const QString &city, cities) {
1390  QVariantHash map;
1391  map.insert(QStringLiteral("city"), city);
1392  mapList << map;
1393  }
1394 
1395  dict.insert(QStringLiteral("mapList"), mapList);
1396 
1397  QTest::newRow("dictsort01")
1398  << "{% with mapList|dictsort:'city' as result %}{% for item in result "
1399  "%}{{ item.city }},{% endfor %}{% endwith %}"
1400  << dict << "Berlin,Dublin,London,Paris," << NoError;
1401 
1402  {
1403  // Test duplication works
1404  QVariantHash map;
1405  map.insert(QStringLiteral("city"), QStringLiteral("Berlin"));
1406  mapList << map;
1407  }
1408  dict.insert(QStringLiteral("mapList"), mapList);
1409 
1410  QTest::newRow("dictsort02")
1411  << "{% with mapList|dictsort:'city' as result %}{% for item in result "
1412  "%}{{ item.city }},{% endfor %}{% endwith %}"
1413  << dict << "Berlin,Berlin,Dublin,London,Paris," << NoError;
1414 
1415  dict.clear();
1416 
1417  QVariantList listList;
1418 
1419  const auto countries
1420  = QStringList{QStringLiteral("England"), QStringLiteral("Germany"),
1421  QStringLiteral("France"), QStringLiteral("Ireland")};
1422 
1423  const auto languages
1424  = QStringList{QStringLiteral("English"), QStringLiteral("German"),
1425  QStringLiteral("French"), QStringLiteral("Irish")};
1426 
1427  for (auto i = 0; i < cities.size(); ++i) {
1428  listList << QVariant(
1429  QVariantList{cities.at(i), countries.at(i), languages.at(i)});
1430  }
1431 
1432  dict.insert(QStringLiteral("listList"), listList);
1433 
1434  QTest::newRow("dictsort03")
1435  << "{% with listList|dictsort:'0' as result %}{% for item in result "
1436  "%}{{ "
1437  "item.0 }};{{ item.1 }};{{ item.2 }},{% endfor %}{% endwith %}"
1438  << dict
1439  << "Berlin;Germany;German,Dublin;Ireland;Irish,London;England;"
1440  "English,Paris;France;French,"
1441  << NoError;
1442 
1443  QTest::newRow("dictsort04")
1444  << "{% with listList|dictsort:'1' as result %}{% for item in result "
1445  "%}{{ "
1446  "item.0 }};{{ item.1 }};{{ item.2 }},{% endfor %}{% endwith %}"
1447  << dict
1448  << "London;England;English,Paris;France;French,Berlin;Germany;"
1449  "German,Dublin;Ireland;Irish,"
1450  << NoError;
1451 }
1452 
1453 void TestFilters::testLogicFilters_data()
1454 {
1455  QTest::addColumn<QString>("input");
1456  QTest::addColumn<Dict>("dict");
1457  QTest::addColumn<QString>("output");
1458  QTest::addColumn<Cutelee::Error>("error");
1459 
1460  Dict dict;
1461 
1462  // Literal string arguments to the default filter are always treated as
1463  // safe strings, regardless of the auto-escaping state.
1464 
1465  // Note: we have to use {"a": ""} here, otherwise the invalid template
1466  // variable string interferes with the test result.
1467 
1468  dict.insert(QStringLiteral("a"), QStringLiteral(""));
1469 
1470  QTest::newRow("filter-default01")
1471  << "{{ a|default:\"x<\" }}" << dict << QStringLiteral("x<") << NoError;
1472  QTest::newRow("filter-default02")
1473  << "{% autoescape off %}{{ a|default:\"x<\" }}{% endautoescape %}" << dict
1474  << QStringLiteral("x<") << NoError;
1475 
1476  dict.clear();
1477  dict.insert(QStringLiteral("a"),
1478  QVariant::fromValue(markSafe(QStringLiteral("x>"))));
1479 
1480  QTest::newRow("filter-default03")
1481  << "{{ a|default:\"x<\" }}" << dict << QStringLiteral("x>") << NoError;
1482  QTest::newRow("filter-default04")
1483  << "{% autoescape off %}{{ a|default:\"x<\" }}{% endautoescape %}" << dict
1484  << QStringLiteral("x>") << NoError;
1485 
1486  dict.clear();
1487  dict.insert(QStringLiteral("a"), QVariant());
1488 
1489  QTest::newRow("filter-default_if_none01")
1490  << "{{ a|default:\"x<\" }}" << dict << QStringLiteral("x<") << NoError;
1491  QTest::newRow("filter-default_if_none02")
1492  << "{% autoescape off %}{{ a|default:\"x<\" }}{% endautoescape %}" << dict
1493  << QStringLiteral("x<") << NoError;
1494 }
1495 
1496 void TestFilters::testMiscFilters_data()
1497 {
1498  QTest::addColumn<QString>("input");
1499  QTest::addColumn<Dict>("dict");
1500  QTest::addColumn<QString>("output");
1501  QTest::addColumn<Cutelee::Error>("error");
1502 
1503  Dict dict;
1504 
1505  //
1506  // // {"a": "<1-800-call-me>", "b": mark_safe("<1-800-call-me>")
1507  // QTest::newRow( "filter-phone2numeric01") << QString::fromLatin1( "{{
1508  // a|phone2numeric }} {{ b|phone2numeric }}" ) << dict <<
1509  // QString::fromLatin1( "&lt;1-800-2255-63&gt; <1-800-2255-63>" ) <<
1510  // NoError;
1511  //
1512  // // {"a": "<1-800-call-me>", "b": mark_safe("<1-800-call-me>")
1513  // QTest::newRow( "filter-phone2numeric02") << QString::fromLatin1( "{%
1514  // autoescape off %}{{ a|phone2numeric }} {{ b|phone2numeric }}{%
1515  // endautoescape %}" ) << dict << QString::fromLatin1( "<1-800-2255-63>
1516  // <1-800-2255-63>" ) << NoError;
1517  //
1518  // Chaining a bunch of safeness-preserving filters should not alter
1519  // the safe status either way.
1520 
1521  dict.insert(QStringLiteral("a"), QStringLiteral("a < b"));
1522  dict.insert(QStringLiteral("b"),
1523  QVariant::fromValue(markSafe(QStringLiteral("a < b"))));
1524 
1525  QTest::newRow("chaining01")
1526  << "{{ a|capfirst|center:\"7\" }}.{{ b|capfirst|center:\"7\" }}" << dict
1527  << QStringLiteral(" A &lt; b . A < b ") << NoError;
1528  QTest::newRow("chaining02")
1529  << "{% autoescape off %}{{ a|capfirst|center:\"7\" }}.{{ "
1530  "b|capfirst|center:\"7\" }}{% endautoescape %}"
1531  << dict << QStringLiteral(" A < b . A < b ") << NoError;
1532 
1533  // Using a filter that forces a string back to unsafe:
1534 
1535  QTest::newRow("chaining03")
1536  << "{{ a|cut:\"b\"|capfirst }}.{{ b|cut:\"b\"|capfirst }}" << dict
1537  << QStringLiteral("A &lt; .A < ") << NoError;
1538  QTest::newRow("chaining04")
1539  << "{% autoescape off %}{{ a|cut:\"b\"|capfirst }}.{{ "
1540  "b|cut:\"b\"|capfirst }}{% endautoescape %}"
1541  << dict << QStringLiteral("A < .A < ") << NoError;
1542 
1543  dict.clear();
1544  dict.insert(QStringLiteral("a"), QStringLiteral("a < b"));
1545 
1546  // Using a filter that forces safeness does not lead to double-escaping
1547 
1548  QTest::newRow("chaining05") << QStringLiteral("{{ a|escape|capfirst }}")
1549  << dict << QStringLiteral("A &lt; b") << NoError;
1550  QTest::newRow("chaining06") << QStringLiteral(
1551  "{% autoescape off %}{{ a|escape|capfirst }}{% endautoescape %}")
1552  << dict << QStringLiteral("A &lt; b") << NoError;
1553 
1554  // Force to safe, then back (also showing why using force_escape too
1555  // early in a chain can lead to unexpected results).
1556 
1557  QTest::newRow("chaining07") << "{{ a|force_escape|cut:\";\" }}" << dict
1558  << QStringLiteral("a &amp;lt b") << NoError;
1559  QTest::newRow("chaining08")
1560  << "{% autoescape off %}{{ a|force_escape|cut:\";\" }}{% endautoescape "
1561  "%}"
1562  << dict << QStringLiteral("a &lt b") << NoError;
1563  QTest::newRow("chaining09") << "{{ a|cut:\";\"|force_escape }}" << dict
1564  << QStringLiteral("a &lt; b") << NoError;
1565  QTest::newRow("chaining10")
1566  << "{% autoescape off %}{{ a|cut:\";\"|force_escape }}{% endautoescape "
1567  "%}"
1568  << dict << QStringLiteral("a &lt; b") << NoError;
1569  QTest::newRow("chaining11")
1570  << "{{ a|cut:\"b\"|safe }}" << dict << QStringLiteral("a < ") << NoError;
1571  QTest::newRow("chaining12")
1572  << "{% autoescape off %}{{ a|cut:\"b\"|safe }}{% endautoescape %}" << dict
1573  << QStringLiteral("a < ") << NoError;
1574  QTest::newRow("chaining13") << QStringLiteral("{{ a|safe|force_escape }}")
1575  << dict << QStringLiteral("a &lt; b") << NoError;
1576  QTest::newRow("chaining14") << QStringLiteral(
1577  "{% autoescape off %}{{ a|safe|force_escape }}{% endautoescape %}")
1578  << dict << QStringLiteral("a &lt; b") << NoError;
1579 
1580  // // Filters decorated with stringfilter still respect is_safe.
1581  //
1582  // // {"unsafe": UnsafeClass()
1583  // QTest::newRow( "autoescape-stringfilter01") << QString::fromLatin1( "{{
1584  // unsafe|capfirst }}" ) << dict << QString::fromLatin1( "You &amp; me" )
1585  // <<
1586  // NoError;
1587  //
1588  // // {"unsafe": UnsafeClass()
1589  // QTest::newRow( "autoescape-stringfilter02") << QString::fromLatin1( "{%
1590  // autoescape off %}{{ unsafe|capfirst }}{% endautoescape %}" ) << dict <<
1591  // QString::fromLatin1( "You & me" ) << NoError;
1592  //
1593  // // {"safe": SafeClass()
1594  // QTest::newRow( "autoescape-stringfilter03") << QString::fromLatin1( "{{
1595  // safe|capfirst }}" ) << dict << QString::fromLatin1( "You &gt; me" ) <<
1596  // NoError;
1597  //
1598  // // {"safe": SafeClass()
1599  // QTest::newRow( "autoescape-stringfilter04") << QString::fromLatin1( "{%
1600  // autoescape off %}{{ safe|capfirst }}{% endautoescape %}" ) << dict <<
1601  // QString::fromLatin1( "You &gt; me" ) << NoError;
1602  //
1603 }
1604 
1605 void TestFilters::testIntegerFilters_data()
1606 {
1607  QTest::addColumn<QString>("input");
1608  QTest::addColumn<Dict>("dict");
1609  QTest::addColumn<QString>("output");
1610  QTest::addColumn<Cutelee::Error>("error");
1611 
1612  Dict dict;
1613 
1614  dict.insert(QStringLiteral("i"), 2000);
1615 
1616  QTest::newRow("add01") << QStringLiteral("{{ i|add:5 }}") << dict
1617  << QStringLiteral("2005") << NoError;
1618  QTest::newRow("add02") << QStringLiteral("{{ i|add:\"napis\" }}") << dict
1619  << QStringLiteral("2000") << NoError;
1620 
1621  dict.clear();
1622  dict.insert(QStringLiteral("i"), QStringLiteral("not_an_int"));
1623 
1624  QTest::newRow("add03") << QStringLiteral("{{ i|add:16 }}") << dict
1625  << QStringLiteral("not_an_int") << NoError;
1626  QTest::newRow("add04") << QStringLiteral("{{ i|add:\"16\" }}") << dict
1627  << QStringLiteral("not_an_int16") << NoError;
1628 
1629  dict.clear();
1630  dict.insert(QStringLiteral("l1"), QVariantList{1, 2});
1631  dict.insert(QStringLiteral("l2"), QVariantList{3, 4});
1632 
1633  QTest::newRow("add05") << QStringLiteral("{{ l1|add:l2 }}") << dict
1634  << QStringLiteral("[1, 2, 3, 4]") << NoError;
1635  // QTest::newRow( "add06" ) << QString::fromLatin1( "{{ t1|add:t2 }}" ) <<
1636  // dict << QString::fromLatin1( "2005" ) << NoError;
1637 
1638  // QTest::newRow( "add07" ) << QString::fromLatin1( "{{ d|add:t }}" ) <<
1639  // dict
1640  // << QString::fromLatin1( "2005" ) << NoError;
1641 
1642  QTest::newRow("add08") << QStringLiteral("{{ 1|add:2 }}") << dict
1643  << QStringLiteral("3") << NoError;
1644 
1645  QTest::newRow("filter-getdigit01") << QStringLiteral("{{ 123|get_digit:1 }}")
1646  << dict << QStringLiteral("3") << NoError;
1647  QTest::newRow("filter-getdigit02") << QStringLiteral("{{ 123|get_digit:2 }}")
1648  << dict << QStringLiteral("2") << NoError;
1649  QTest::newRow("filter-getdigit03") << QStringLiteral("{{ 123|get_digit:3 }}")
1650  << dict << QStringLiteral("1") << NoError;
1651  QTest::newRow("filter-getdigit04")
1652  << QStringLiteral("{{ 123|get_digit:4 }}") << dict
1653  << QStringLiteral("123") << NoError;
1654 }
1655 
1656 QTEST_MAIN(TestFilters)
1657 #include "testfilters.moc"
1658 
1659 #endif
QHash::iterator insert(const Key &key, const T &value)
The Context class holds the context to render a Template with.
Definition: context.h:118
Cutelee::SafeString markSafe(const Cutelee::SafeString &input)
Definition: util.cpp:90
The Cutelee namespace holds all public Cutelee API.
Definition: Mainpage.dox:7
QString path() const const
QDir absoluteDir() const const
Cutelee::Engine is the main entry point for creating Cutelee Templates.
Definition: engine.h:120
Utility functions used throughout Cutelee.
void clear()
QVariant fromValue(const T &value)
The InMemoryTemplateLoader loads Templates set dynamically in memory.
void insert(int i, const T &value)
QString applicationDirPath()
QDateTime currentDateTimeUtc()