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