Felipe97/llama-cpp-compiled
01.1k
1#include <string>2#include <iostream>3#include <random>4#include <cstdlib>5 6#include "json.h"7#include "subproc.h"8 9#include "jinja/runtime.h"10#include "jinja/parser.h"11#include "jinja/lexer.h"12#include "jinja/utils.h"13#include "jinja/caps.h"14 15#include "testing.h"16 17using json = common_json;18 19static void test_template(testing & t, const std::string & name, const std::string & tmpl, const json & vars, const std::string & expect);20 21static void test_whitespace_control(testing & t);22static void test_conditionals(testing & t);23static void test_loops(testing & t);24static void test_expressions(testing & t);25static void test_set_statement(testing & t);26static void test_filters(testing & t);27static void test_literals(testing & t);28static void test_comments(testing & t);29static void test_macros(testing & t);30static void test_namespace(testing & t);31static void test_tests(testing & t);32static void test_string_methods(testing & t);33static void test_array_methods(testing & t);34static void test_object_methods(testing & t);35static void test_hasher(testing & t);36static void test_stats(testing & t);37static void test_caps(testing & t);38static void test_string_parts(testing & t);39static void test_fuzzing(testing & t);40 41static bool g_python_mode = false;42 43int main(int argc, char *argv[]) {44 testing t(std::cout);45 t.verbose = true;46 47 // usage: test-jinja [-py] [filter_regex]48 // -py : enable python mode (use python jinja2 for rendering expected output)49 // only use this for cross-checking, not for correctness50 // note: the implementation of this flag is basic, only intented to be used by maintainers51 52 for (int i = 1; i < argc; i++) {53 std::string arg = argv[i];54 if (arg == "-py") {55 g_python_mode = true;56 } else {57 t.set_filter(arg);58 }59 }60 61 t.test("whitespace control", test_whitespace_control);62 t.test("conditionals", test_conditionals);63 t.test("loops", test_loops);64 t.test("expressions", test_expressions);65 t.test("set statement", test_set_statement);66 t.test("filters", test_filters);67 t.test("literals", test_literals);68 t.test("comments", test_comments);69 t.test("macros", test_macros);70 t.test("namespace", test_namespace);71 t.test("tests", test_tests);72 t.test("string methods", test_string_methods);73 t.test("array methods", test_array_methods);74 t.test("object methods", test_object_methods);75 if (!g_python_mode) {76 t.test("hasher", test_hasher);77 t.test("stats", test_stats);78 t.test("caps", test_caps);79 t.test("string parts", test_string_parts);80 t.test("fuzzing", test_fuzzing);81 }82 83 return t.summary();84}85 86static void test_whitespace_control(testing & t) {87 test_template(t, "trim_blocks removes newline after tag",88 "{% if true %}\n"89 "hello\n"90 "{% endif %}\n",91 json::object(),92 "hello\n"93 );94 95 test_template(t, "lstrip_blocks removes leading whitespace",96 " {% if true %}\n"97 " hello\n"98 " {% endif %}\n",99 json::object(),100 " hello\n"101 );102 103 test_template(t, "for loop with trim_blocks",104 "{% for i in items %}\n"105 "{{ i }}\n"106 "{% endfor %}\n",107 {{"items", json::array({1, 2, 3})}},108 "1\n2\n3\n"109 );110 111 test_template(t, "explicit strip both",112 " {%- if true -%} \n"113 "hello\n"114 " {%- endif -%} \n",115 json::object(),116 "hello"117 );118 119 test_template(t, "expression whitespace control",120 " {{- 'hello' -}} \n",121 json::object(),122 "hello"123 );124 125 test_template(t, "inline block no newline",126 "{% if true %}yes{% endif %}",127 json::object(),128 "yes"129 );130}131 132static void test_conditionals(testing & t) {133 test_template(t, "if true",134 "{% if cond %}yes{% endif %}",135 {{"cond", true}},136 "yes"137 );138 139 test_template(t, "if false",140 "{% if cond %}yes{% endif %}",141 {{"cond", false}},142 ""143 );144 145 test_template(t, "if else",146 "{% if cond %}yes{% else %}no{% endif %}",147 {{"cond", false}},148 "no"149 );150 151 test_template(t, "if elif else",152 "{% if a %}A{% elif b %}B{% else %}C{% endif %}",153 {{"a", false}, {"b", true}},154 "B"155 );156 157 test_template(t, "nested if",158 "{% if outer %}{% if inner %}both{% endif %}{% endif %}",159 {{"outer", true}, {"inner", true}},160 "both"161 );162 163 test_template(t, "comparison operators",164 "{% if x > 5 %}big{% endif %}",165 {{"x", 10}},166 "big"167 );168 169 test_template(t, "object comparison",170 "{% if {0: 1, none: 2, 1.0: 3, '0': 4, true: 5} == {false: 1, none: 2, 1: 5, '0': 4} %}equal{% endif %}",171 json::object(),172 "equal"173 );174 175 test_template(t, "array comparison",176 "{% if [0, 1.0, false] == [false, 1, 0.0] %}equal{% endif %}",177 json::object(),178 "equal"179 );180 181 test_template(t, "logical and",182 "{% if a and b %}both{% endif %}",183 {{"a", true}, {"b", true}},184 "both"185 );186 187 test_template(t, "logical or",188 "{% if a or b %}either{% endif %}",189 {{"a", false}, {"b", true}},190 "either"191 );192 193 test_template(t, "logical not",194 "{% if not a %}negated{% endif %}",195 {{"a", false}},196 "negated"197 );198 199 test_template(t, "in operator (element in array)",200 "{% if 'x' in items %}found{% endif %}",201 {{"items", json::array({"x", "y"})}},202 "found"203 );204 205 test_template(t, "in operator (substring)",206 "{% if 'bc' in 'abcd' %}found{% endif %}",207 json::object(),208 "found"209 );210 211 test_template(t, "in operator (object key)",212 "{% if 'key' in obj %}found{% endif %}",213 {{"obj", {{"key", 1}, {"other", 2}}}},214 "found"215 );216 217 test_template(t, "is defined",218 "{% if x is defined %}yes{% else %}no{% endif %}",219 {{"x", 1}},220 "yes"221 );222 223 test_template(t, "is not defined",224 "{% if y is not defined %}yes{% else %}no{% endif %}",225 json::object(),226 "yes"227 );228 229 test_template(t, "is undefined falsy",230 "{{ 'yes' if not y else 'no' }}",231 json::object(),232 "yes"233 );234 235 test_template(t, "is undefined attribute falsy",236 "{{ 'yes' if not y.x else 'no' }}",237 {{"y", true}},238 "yes"239 );240 241 test_template(t, "is undefined key falsy",242 "{{ 'yes' if not y['x'] else 'no' }}",243 {{"y", json::array({nullptr})}},244 "yes"245 );246 247 test_template(t, "is empty array falsy",248 "{{ 'yes' if not y else 'no' }}",249 {{"y", json::array()}},250 "yes"251 );252 253 test_template(t, "is empty object falsy",254 "{{ 'yes' if not y else 'no' }}",255 {{"y", json::object()}},256 "yes"257 );258 259 test_template(t, "is empty string falsy",260 "{{ 'yes' if not y else 'no' }}",261 {{"y", ""}},262 "yes"263 );264 265 test_template(t, "is 0 falsy",266 "{{ 'yes' if not y else 'no' }}",267 {{"y", 0}},268 "yes"269 );270 271 test_template(t, "is 0.0 falsy",272 "{{ 'yes' if not y else 'no' }}",273 {{"y", 0.0}},274 "yes"275 );276 277 test_template(t, "is non-empty array truthy",278 "{{ 'yes' if y else 'no' }}",279 {{"y", json::array({""})}},280 "yes"281 );282 283 test_template(t, "is non-empty object truthy",284 "{{ 'yes' if y else 'no' }}",285 {{"y", json::array({"x", false})}},286 "yes"287 );288 289 test_template(t, "is non-empty string truthy",290 "{{ 'yes' if y else 'no' }}",291 {{"y", "0"}},292 "yes"293 );294 295 test_template(t, "is 1 truthy",296 "{{ 'yes' if y else 'no' }}",297 {{"y", 1}},298 "yes"299 );300 301 test_template(t, "is 1.0 truthy",302 "{{ 'yes' if y else 'no' }}",303 {{"y", 1.0}},304 "yes"305 );306}307 308static void test_loops(testing & t) {309 test_template(t, "simple for",310 "{% for i in items %}{{ i }}{% endfor %}",311 {{"items", json::array({1, 2, 3})}},312 "123"313 );314 315 test_template(t, "loop.index",316 "{% for i in items %}{{ loop.index }}{% endfor %}",317 {{"items", json::array({"a", "b", "c"})}},318 "123"319 );320 321 test_template(t, "loop.index0",322 "{% for i in items %}{{ loop.index0 }}{% endfor %}",323 {{"items", json::array({"a", "b", "c"})}},324 "012"325 );326 327 test_template(t, "loop.first and loop.last",328 "{% for i in items %}{% if loop.first %}[{% endif %}{{ i }}{% if loop.last %}]{% endif %}{% endfor %}",329 {{"items", json::array({1, 2, 3})}},330 "[123]"331 );332 333 test_template(t, "loop.length",334 "{% for i in items %}{{ loop.length }}{% endfor %}",335 {{"items", json::array({"a", "b"})}},336 "22"337 );338 339 test_template(t, "for over dict items",340 "{% for k, v in data.items() %}{{ k }}={{ v }} {% endfor %}",341 {{"data", {{"x", 1}, {"y", 2}}}},342 "x=1 y=2 "343 );344 345 test_template(t, "for else empty",346 "{% for i in items %}{{ i }}{% else %}empty{% endfor %}",347 {{"items", json::array()}},348 "empty"349 );350 351 test_template(t, "for undefined empty",352 "{% for i in items %}{{ i }}{% else %}empty{% endfor %}",353 json::object(),354 "empty"355 );356 357 test_template(t, "nested for",358 "{% for i in a %}{% for j in b %}{{ i }}{{ j }}{% endfor %}{% endfor %}",359 {{"a", json::array({1, 2})}, {"b", json::array({"x", "y"})}},360 "1x1y2x2y"361 );362 363 test_template(t, "for with range",364 "{% for i in range(3) %}{{ i }}{% endfor %}",365 json::object(),366 "012"367 );368}369 370static void test_expressions(testing & t) {371 test_template(t, "simple variable",372 "{{ x }}",373 {{"x", 42}},374 "42"375 );376 377 test_template(t, "none in object",378 "{{ x in {'low': 1, 'high': 2} }}",379 {{"x", nullptr}},380 "False"381 );382 383 test_template(t, "none not in object",384 "{{ x not in {'low': 1, 'high': 2} }}",385 {{"x", nullptr}},386 "True"387 );388 389 test_template(t, "none in array",390 "{{ x in [1, none, 3] }}",391 {{"x", nullptr}},392 "True"393 );394 395 test_template(t, "dot notation",396 "{{ user.name }}",397 {{"user", {{"name", "Bob"}}}},398 "Bob"399 );400 401 test_template(t, "dot notation (integer property)",402 "{{ {10: 'Bob'}.10 }}",403 json::object(),404 "Bob"405 );406 407 test_template(t, "dot notation (array index)",408 "{{ user.10 }}",409 {{"user", json::array({"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"})}},410 "k"411 );412 413 test_template(t, "negative float (not dot notation)",414 "{{ -1.0 }}",415 json::object(),416 "-1.0"417 );418 419 test_template(t, "bracket notation",420 "{{ user['name'] }}",421 {{"user", {{"name", "Bob"}}}},422 "Bob"423 );424 425 test_template(t, "empty computed member defaults to undefined",426 "{{ a[]|default('fallback') }}",427 {{"a", {{"name", "Bob"}}}},428 "fallback"429 );430 431 test_template(t, "empty computed member is undefined",432 "{{ a[] is undefined }}",433 {{"a", {{"name", "Bob"}}}},434 "True"435 );436 437 test_template(t, "undefined computed member is undefined",438 "{{ a[undefined] is undefined }}",439 {{"a", {{"name", "Bob"}}}},440 "True"441 );442 443 test_template(t, "array access",444 "{{ items[1] }}",445 {{"items", json::array({"a", "b", "c"})}},446 "b"447 );448 449 test_template(t, "array negative access",450 "{{ items[-1] }}",451 {{"items", json::array({"a", "b", "c"})}},452 "c"453 );454 455 test_template(t, "array slice",456 "{{ items[1:-1]|string }}",457 {{"items", json::array({"a", "b", "c"})}},458 "['b']"459 );460 461 test_template(t, "array slice step",462 "{{ items[::2]|string }}",463 {{"items", json::array({"a", "b", "c"})}},464 "['a', 'c']"465 );466 467 test_template(t, "tuple slice",468 "{{ ('a', 'b', 'c')[::-1]|string }}",469 json::object(),470 "('c', 'b', 'a')"471 );472 473 test_template(t, "string slice negative step",474 "{{ 'abcdef'[::-2] }}",475 json::object(),476 "fdb"477 );478 479 test_template(t, "string slice negative start and step",480 "{{ 'abcdef'[-1:1:-1] }}",481 json::object(),482 "fedc"483 );484 485 test_template(t, "string slice negative start, stop and step",486 "{{ 'abcdef'[-1:-5:-1] }}",487 json::object(),488 "fedc"489 );490 491 test_template(t, "arithmetic",492 "{{ (a + b) * c }}",493 {{"a", 2}, {"b", 3}, {"c", 4}},494 "20"495 );496 497 test_template(t, "string concat ~",498 "{{ 'hello' ~ ' ' ~ 'world' }}",499 json::object(),500 "hello world"501 );502 503 test_template(t, "string repetition",504 "{{ 'ab' * 3 }}",505 json::object(),506 "ababab"507 );508 509 test_template(t, "reversed string repetition",510 "{{ 3 * 'ab' }}",511 json::object(),512 "ababab"513 );514 515 test_template(t, "ternary",516 "{{ 'yes' if cond else 'no' }}",517 {{"cond", true}},518 "yes"519 );520}521 522static void test_set_statement(testing & t) {523 test_template(t, "simple set",524 "{% set x = 5 %}{{ x }}",525 json::object(),526 "5"527 );528 529 test_template(t, "set with expression",530 "{% set x = a + b %}{{ x }}",531 {{"a", 10}, {"b", 20}},532 "30"533 );534 535 test_template(t, "set list",536 "{% set items = [1, 2, 3] %}{{ items|length }}",537 json::object(),538 "3"539 );540 541 test_template(t, "set dict",542 "{% set d = {'a': 1} %}{{ d.a }}",543 json::object(),544 "1"545 );546 547 test_template(t, "set dict with mixed type keys",548 "{% set d = {0: 1, none: 2, 1.0: 3, '0': 4, (0, 0): 5, false: 6, 1: 7} %}{{ d[(0, 0)] + d[0] + d[none] + d['0'] + d[false] + d[1.0] + d[1] }}",549 json::object(),550 "37"551 );552 553 test_template(t, "print dict with mixed type keys",554 "{% set d = {0: 1, none: 2, 1.0: 3, '0': 4, (0, 0): 5, true: 6} %}{{ d|string }}",555 json::object(),556 "{0: 1, None: 2, 1.0: 6, '0': 4, (0, 0): 5}"557 );558 559 test_template(t, "print array with mixed types",560 "{% set d = [0, none, 1.0, '0', true, (0, 0)] %}{{ d|string }}",561 json::object(),562 "[0, None, 1.0, '0', True, (0, 0)]"563 );564 565 test_template(t, "object member assignment with mixed key types",566 "{% set d = namespace() %}{% set d.a = 123 %}{{ d['a'] == 123 }}",567 json::object(),568 "True"569 );570 571 test_template(t, "tuple unpacking",572 "{% set t = (1, 2, 3) %}{% set a, b, c = t %}{{ a + b + c }}",573 json::object(),574 "6"575 );576}577 578static void test_filters(testing & t) {579 test_template(t, "upper",580 "{{ 'hello'|upper }}",581 json::object(),582 "HELLO"583 );584 585 test_template(t, "lower",586 "{{ 'HELLO'|lower }}",587 json::object(),588 "hello"589 );590 591 test_template(t, "upper array",592 "{{ items|upper }}",593 {{"items", json::array({"hello", "world"})}},594 "['HELLO', 'WORLD']"595 );596 597 test_template(t, "upper dict",598 "{{ items|upper }}",599 {{"items", {{"hello", "world"}}}},600 "{'HELLO': 'WORLD'}"601 );602 603 test_template(t, "capitalize",604 "{{ 'heLlo World'|capitalize }}",605 json::object(),606 "Hello world"607 );608 609 test_template(t, "title",610 "{{ 'hello world'|title }}",611 json::object(),612 "Hello World"613 );614 615 test_template(t, "trim",616 "{{ ' \r\n\thello\t\n\r '|trim }}",617 json::object(),618 "hello"619 );620 621 test_template(t, "trim chars",622 "{{ 'xyxhelloxyx'|trim('xy') }}",623 json::object(),624 "hello"625 );626 627 test_template(t, "length string",628 "{{ 'hello'|length }}",629 json::object(),630 "5"631 );632 633 test_template(t, "replace",634 "{{ 'hello world'|replace('world', 'jinja') }}",635 json::object(),636 "hello jinja"637 );638 639 test_template(t, "length (count alias) list",640 "{{ items|count }}",641 {{"items", json::array({1, 2, 3})}},642 "3"643 );644 645 test_template(t, "first",646 "{{ items|first }}",647 {{"items", json::array({10, 20, 30})}},648 "10"649 );650 651 test_template(t, "last",652 "{{ items|last }}",653 {{"items", json::array({10, 20, 30})}},654 "30"655 );656 657 test_template(t, "reverse",658 "{% for i in items|reverse %}{{ i }}{% endfor %}",659 {{"items", json::array({1, 2, 3})}},660 "321"661 );662 663 test_template(t, "sort",664 "{% for i in items|sort %}{{ i }}{% endfor %}",665 {{"items", json::array({3, 1, 2})}},666 "123"667 );668 669 test_template(t, "sort reverse",670 "{% for i in items|sort(true) %}{{ i }}{% endfor %}",671 {{"items", json::array({3, 1, 2})}},672 "321"673 );674 675 test_template(t, "sort with attribute",676 "{{ items|sort(attribute='name')|join(attribute='age') }}",677 {{"items", json::array({678 json({{"name", "c"}, {"age", 3}}),679 json({{"name", "a"}, {"age", 1}}),680 json({{"name", "b"}, {"age", 2}}),681 })}},682 "123"683 );684 685 test_template(t, "sort with numeric attribute",686 "{{ items|sort(attribute=0)|join(attribute=1) }}",687 {{"items", json::array({688 json::array({3, "z"}),689 json::array({1, "x"}),690 json::array({2, "y"}),691 })}},692 "xyz"693 );694 695 test_template(t, "join",696 "{{ items|join(', ') }}",697 {{"items", json::array({"a", "b", "c"})}},698 "a, b, c"699 );700 701 test_template(t, "join default separator",702 "{{ items|join }}",703 {{"items", json::array({"x", "y", "z"})}},704 "xyz"705 );706 707 test_template(t, "abs",708 "{{ -5|abs }}",709 json::object(),710 "5"711 );712 713 test_template(t, "int from string",714 "{{ '42'|int }}",715 json::object(),716 "42"717 );718 719 test_template(t, "int from string with default",720 "{{ ''|int(1) }}",721 json::object(),722 "1"723 );724 725 test_template(t, "int from string with base",726 "{{ '11'|int(base=2) }}",727 json::object(),728 "3"729 );730 731 test_template(t, "float from string",732 "{{ '3.14'|float }}",733 json::object(),734 "3.14"735 );736 737 test_template(t, "default with value",738 "{{ x|default('fallback') }}",739 {{"x", "actual"}},740 "actual"741 );742 743 test_template(t, "default without value",744 "{{ y|default('fallback') }}",745 json::object(),746 "fallback"747 );748 749 test_template(t, "default (d alias) with falsy value",750 "{{ ''|d('fallback', true) }}",751 json::object(),752 "fallback"753 );754 755 test_template(t, "tojson ensure_ascii=true",756 "{{ data|tojson(ensure_ascii=true) }}",757 {{"data", "\u2713"}},758 "\"\\u2713\""759 );760 761 test_template(t, "tojson ensure_ascii=true nested object",762 "{{ data|tojson(ensure_ascii=true) }}",763 {{"data", {764 {"text", "\u2713"},765 {"items", json::array({"é", {{"snowman", "☃"}}})}766 }}},767 "{\"text\": \"\\u2713\", \"items\": [\"\\u00e9\", {\"snowman\": \"\\u2603\"}]}"768 );769 770 test_template(t, "tojson ensure_ascii=true indent=2",771 "{{ data|tojson(ensure_ascii=true, indent=2) }}",772 {{"data", {773 {"text", "\u2713"},774 {"nested", {{"accent", "é"}}}775 }}},776 "{\n \"text\": \"\\u2713\",\n \"nested\": {\n \"accent\": \"\\u00e9\"\n }\n}"777 );778 779 test_template(t, "tojson ensure_ascii=true preserves existing escapes",780 "{{ data|tojson(ensure_ascii=true) }}",781 {{"data", {782 {"emoji", "😀"},783 {"line", "a\nb"}784 }}},785 "{\"emoji\": \"\\ud83d\\ude00\", \"line\": \"a\\nb\"}"786 );787 788 test_template(t, "tojson sort_keys=true",789 "{{ data|tojson(sort_keys=true) }}",790 {{"data", {{"b", 2}, {"a", 1}}}},791 "{\"a\": 1, \"b\": 2}"792 );793 794 test_template(t, "tojson",795 "{{ data|tojson }}",796 {{"data", {{"a", 1}, {"b", json::array({1, 2})}}}},797 "{\"a\": 1, \"b\": [1, 2]}"798 );799 800 test_template(t, "tojson indent=4",801 "{{ data|tojson(indent=4) }}",802 {{"data", {{"a", 1}, {"b", json::array({1, 2})}}}},803 "{\n \"a\": 1,\n \"b\": [\n 1,\n 2\n ]\n}"804 );805 806 test_template(t, "tojson separators=(',',':')",807 "{{ data|tojson(separators=(',',':')) }}",808 {{"data", {{"a", 1}, {"b", json::array({1, 2})}}}},809 "{\"a\":1,\"b\":[1,2]}"810 );811 812 test_template(t, "tojson separators=(',',': ') indent=2",813 "{{ data|tojson(separators=(',',': '), indent=2) }}",814 {{"data", {{"a", 1}, {"b", json::array({1, 2})}}}},815 "{\n \"a\": 1,\n \"b\": [\n 1,\n 2\n ]\n}"816 );817 818 test_template(t, "indent",819 "{{ data|indent(2) }}",820 {{ "data", "foo\nbar" }},821 "foo\n bar"822 );823 824 test_template(t, "indent first only",825 "{{ data|indent(width=3,first=true) }}",826 {{ "data", "foo\nbar" }},827 " foo\n bar"828 );829 830 test_template(t, "indent blank lines and first line",831 "{{ data|indent(width=5,blank=true,first=true) }}",832 {{ "data", "foo\n\nbar" }},833 " foo\n \n bar"834 );835 836 test_template(t, "indent with default width",837 "{{ data|indent() }}",838 {{ "data", "foo\nbar" }},839 "foo\n bar"840 );841 842 test_template(t, "indent with no newline",843 "{{ data|indent }}",844 {{ "data", "foo" }},845 "foo"846 );847 848 test_template(t, "indent with trailing newline",849 "{{ data|indent(blank=true) }}",850 {{ "data", "foo\n" }},851 "foo\n "852 );853 854 test_template(t, "indent with string",855 "{{ data|indent(width='>>>>') }}",856 {{ "data", "foo\nbar" }},857 "foo\n>>>>bar"858 );859 860 test_template(t, "chained filters",861 "{{ ' HELLO '|trim|lower }}",862 json::object(),863 "hello"864 );865 866 test_template(t, "int filter on integer is identity",867 "{{ value|int }}",868 {{"value", 7}},869 "7"870 );871 872 test_template(t, "none to string",873 "{{ x|string }}",874 {{"x", nullptr}},875 "None"876 );877}878 879static void test_literals(testing & t) {880 test_template(t, "integer",881 "{{ 42 }}",882 json::object(),883 "42"884 );885 886 test_template(t, "float",887 "{{ 3.14 }}",888 json::object(),889 "3.14"890 );891 892 test_template(t, "string",893 "{{ 'hello' }}",894 json::object(),895 "hello"896 );897 898 test_template(t, "boolean true",899 "{{ true }}",900 json::object(),901 "True"902 );903 904 test_template(t, "boolean false",905 "{{ false }}",906 json::object(),907 "False"908 );909 910 test_template(t, "none",911 "{% if x is none %}null{% endif %}",912 {{"x", nullptr}},913 "null"914 );915 916 test_template(t, "list literal",917 "{% for i in [1, 2, 3] %}{{ i }}{% endfor %}",918 json::object(),919 "123"920 );921 922 test_template(t, "dict literal",923 "{% set d = {'a': 1} %}{{ d.a }}",924 json::object(),925 "1"926 );927 928 test_template(t, "integer|abs",929 "{{ -42 | abs }}",930 json::object(),931 "42"932 );933 934 test_template(t, "integer|float",935 "{{ 42 | float }}",936 json::object(),937 "42.0"938 );939 940 test_template(t, "integer|tojson",941 "{{ 42 | tojson }}",942 json::object(),943 "42"944 );945 946 test_template(t, "float|abs",947 "{{ -3.14 | abs }}",948 json::object(),949 "3.14"950 );951 952 test_template(t, "float|int",953 "{{ 3.14 | int }}",954 json::object(),955 "3"956 );957 958 test_template(t, "float|tojson",959 "{{ 3.14 | tojson }}",960 json::object(),961 "3.14"962 );963 964 test_template(t, "string|tojson",965 "{{ 'hello' | tojson }}",966 json::object(),967 "\"hello\""968 );969 970 test_template(t, "boolean|int",971 "{{ true | int }}",972 json::object(),973 "1"974 );975 976 test_template(t, "boolean|float",977 "{{ true | float }}",978 json::object(),979 "1.0"980 );981 982 test_template(t, "boolean|tojson",983 "{{ true | tojson }}",984 json::object(),985 "true"986 );987}988 989static void test_comments(testing & t) {990 test_template(t, "inline comment",991 "before{# comment #}after",992 json::object(),993 "beforeafter"994 );995 996 test_template(t, "comment ignores code",997 "{% set x = 1 %}{# {% set x = 999 %} #}{{ x }}",998 json::object(),999 "1"1000 );1001}1002 1003static void test_macros(testing & t) {1004 test_template(t, "simple macro",1005 "{% macro greet(name) %}Hello {{ name }}{% endmacro %}{{ greet('World') }}",1006 json::object(),1007 "Hello World"1008 );1009 1010 test_template(t, "macro default arg",1011 "{% macro greet(name='Guest') %}Hi {{ name }}{% endmacro %}{{ greet() }}",1012 json::object(),1013 "Hi Guest"1014 );1015 1016 test_template(t, "macro kwargs input",1017 "{% macro my_func(a, b=False) %}{% if b %}{{ a }}{% else %}nope{% endif %}{% endmacro %}{{ my_func(1, b=True) }}",1018 json::object(),1019 "1"1020 );1021 1022 test_template(t, "macro with multiple args",1023 "{% macro add(a, b, c=0) %}{{ a + b + c }}{% endmacro %}{{ add(1, 2) }},{{ add(1, 2, 3) }},{{ add(1, b=10) }},{{ add(1, 2, c=5) }}",1024 json::object(),1025 "3,6,11,8"1026 );1027 1028 test_template(t, "macro with kwarg out-of-order input",1029 "{% macro greet(first, last, greeting='Hello') %}{{ greeting }}, {{ first }} {{ last }}{% endmacro %}{{ greet(last='Smith', first='John') }},{{ greet(last='Doe', greeting='Hi', first='Jane') }}",1030 json::object(),1031 "Hello, John Smith,Hi, Jane Doe"1032 );1033 1034 test_template(t, "macro with caller",1035 "\1036{%- macro nest_dict(o, i, ff='') %}\n\1037 {{- caller(ff) }}\n\1038 {%- for k, v in o|items %}\n\1039 {{- i + k + ': ' }}\n\1040 {%- if v is mapping %}\n\1041 {{- '{' }}\n\1042 {% call(f) nest_dict(v, i + ' ') %}\n\1043 {{- 'fail' if ff is undefined }}\n\1044 {%- endcall %}\n\1045 {{- i + '}' }}\n\1046 {% else %}\n\1047 {{- v|string }}\n\1048 {% endif %}\n\1049 {%- endfor %}\n\1050{%- endmacro %}\n\1051{%- call(f) nest_dict({'root1': 1, 'root2': {'nest1': 1, 'nest2': {'nest3': 2}}}, ' ', 'Dict') %}\n\1052 {{- 'fail' if ff is defined }}\n\1053 {{- f + ' {' }}\n\1054{% endcall %}\n\1055{{- '}' }}",1056 json::object(),1057 "Dict {\n root1: 1\n root2: {\n nest1: 1\n nest2: {\n nest3: 2\n }\n }\n}"1058 );1059}1060 1061static void test_namespace(testing & t) {1062 test_template(t, "namespace counter",1063 "{% set ns = namespace(count=0) %}{% for i in range(3) %}{% set ns.count = ns.count + 1 %}{% endfor %}{{ ns.count }}",1064 json::object(),1065 "3"1066 );1067}1068 1069static void test_tests(testing & t) {1070 test_template(t, "is odd",1071 "{% if 3 is odd %}yes{% endif %}",1072 json::object(),1073 "yes"1074 );1075 1076 test_template(t, "is even",1077 "{% if 4 is even %}yes{% endif %}",1078 json::object(),1079 "yes"1080 );1081 1082 test_template(t, "is false",1083 "{{ 'yes' if x is false }}",1084 {{"x", false}},1085 "yes"1086 );1087 1088 test_template(t, "is true",1089 "{{ 'yes' if x is true }}",1090 {{"x", true}},1091 "yes"1092 );1093 1094 test_template(t, "string is false",1095 "{{ 'yes' if x is false else 'no' }}",1096 {{"x", ""}},1097 "no"1098 );1099 1100 test_template(t, "is divisibleby",1101 "{{ 'yes' if x is divisibleby(2) }}",1102 {{"x", 2}},1103 "yes"1104 );1105 1106 test_template(t, "is eq",1107 "{{ 'yes' if 3 is eq(3) }}",1108 json::object(),1109 "yes"1110 );1111 1112 test_template(t, "is not equalto",1113 "{{ 'yes' if 3 is not equalto(4) }}",1114 json::object(),1115 "yes"1116 );1117 1118 test_template(t, "is ge",1119 "{{ 'yes' if 3 is ge(3) }}",1120 json::object(),1121 "yes"1122 );1123 1124 test_template(t, "is gt",1125 "{{ 'yes' if 3 is gt(2) }}",1126 json::object(),1127 "yes"1128 );1129 1130 test_template(t, "is greaterthan",1131 "{{ 'yes' if 3 is greaterthan(2) }}",1132 json::object(),1133 "yes"1134 );1135 1136 test_template(t, "is lt",1137 "{{ 'yes' if 2 is lt(3) }}",1138 json::object(),1139 "yes"1140 );1141 1142 test_template(t, "is lessthan",1143 "{{ 'yes' if 2 is lessthan(3) }}",1144 json::object(),1145 "yes"1146 );1147 1148 test_template(t, "is ne",1149 "{{ 'yes' if 2 is ne(3) }}",1150 json::object(),1151 "yes"1152 );1153 1154 test_template(t, "is lower",1155 "{{ 'yes' if 'lowercase' is lower }}",1156 json::object(),1157 "yes"1158 );1159 1160 test_template(t, "is upper",1161 "{{ 'yes' if 'UPPERCASE' is upper }}",1162 json::object(),1163 "yes"1164 );1165 1166 test_template(t, "is sameas",1167 "{{ 'yes' if x is sameas(false) }}",1168 {{"x", false}},1169 "yes"1170 );1171 1172 test_template(t, "is boolean",1173 "{{ 'yes' if x is boolean }}",1174 {{"x", true}},1175 "yes"1176 );1177 1178 test_template(t, "is callable",1179 "{{ 'yes' if ''.strip is callable }}",1180 json::object(),1181 "yes"1182 );1183 1184 test_template(t, "is escaped",1185 "{{ 'yes' if 'foo'|safe is escaped }}",1186 json::object(),1187 "yes"1188 );1189 1190 test_template(t, "is filter",1191 "{{ 'yes' if 'trim' is filter }}",1192 json::object(),1193 "yes"1194 );1195 1196 test_template(t, "is float",1197 "{{ 'yes' if x is float }}",1198 {{"x", 1.1}},1199 "yes"1200 );