Brunobkr/llama.cpp_AlgMor24_github
ΩFFFΣLLIa • llama.cpp • AlgMor24 ██████╗ ███████╗███████╗███████╗██╗ ██╗ ██╗ █████╗ ██╔═══██╗██╔════╝██╔════╝██╔════╝██║ ██║ ██║██╔══██╗ ██║ ██║█████╗ █████╗ █████╗ ██║ ██║ ██║███████║ ██║ ██║██╔══╝ ██╔══╝ ██╔══╝ ██║ ██║ ██║██╔══██║ ╚██████╔╝██║ ██║ ███████╗███████╗███████╗██║██║ ██║ ╚═════╝ ╚═╝ ╚═╝ ╚══════╝╚══════╝╚══════╝╚═╝╚═╝ ╚═╝ High-Performance LLM / VLM Inference & Autonomous Agentic Ecosystem… See the full description on the dataset page: https://huggingface.co/datasets/Brunobkr/llama.cpp_AlgMor24_github.
03k
1#include <string>2#include <iostream>3#include <random>4#include <cstdlib>5 6#include <nlohmann/json.hpp>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 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, "string slice negative step",439 "{{ 'abcdef'[::-2] }}",440 json::object(),441 "fdb"442 );443 444 test_template(t, "string slice negative start and step",445 "{{ 'abcdef'[-1:1:-1] }}",446 json::object(),447 "fedc"448 );449 450 test_template(t, "string slice negative start, stop and step",451 "{{ 'abcdef'[-1:-5:-1] }}",452 json::object(),453 "fedc"454 );455 456 test_template(t, "arithmetic",457 "{{ (a + b) * c }}",458 {{"a", 2}, {"b", 3}, {"c", 4}},459 "20"460 );461 462 test_template(t, "string concat ~",463 "{{ 'hello' ~ ' ' ~ 'world' }}",464 json::object(),465 "hello world"466 );467 468 test_template(t, "string repetition",469 "{{ 'ab' * 3 }}",470 json::object(),471 "ababab"472 );473 474 test_template(t, "reversed string repetition",475 "{{ 3 * 'ab' }}",476 json::object(),477 "ababab"478 );479 480 test_template(t, "ternary",481 "{{ 'yes' if cond else 'no' }}",482 {{"cond", true}},483 "yes"484 );485}486 487static void test_set_statement(testing & t) {488 test_template(t, "simple set",489 "{% set x = 5 %}{{ x }}",490 json::object(),491 "5"492 );493 494 test_template(t, "set with expression",495 "{% set x = a + b %}{{ x }}",496 {{"a", 10}, {"b", 20}},497 "30"498 );499 500 test_template(t, "set list",501 "{% set items = [1, 2, 3] %}{{ items|length }}",502 json::object(),503 "3"504 );505 506 test_template(t, "set dict",507 "{% set d = {'a': 1} %}{{ d.a }}",508 json::object(),509 "1"510 );511 512 test_template(t, "set dict with mixed type keys",513 "{% 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] }}",514 json::object(),515 "37"516 );517 518 test_template(t, "print dict with mixed type keys",519 "{% set d = {0: 1, none: 2, 1.0: 3, '0': 4, (0, 0): 5, true: 6} %}{{ d|string }}",520 json::object(),521 "{0: 1, None: 2, 1.0: 6, '0': 4, (0, 0): 5}"522 );523 524 test_template(t, "print array with mixed types",525 "{% set d = [0, none, 1.0, '0', true, (0, 0)] %}{{ d|string }}",526 json::object(),527 "[0, None, 1.0, '0', True, (0, 0)]"528 );529 530 test_template(t, "object member assignment with mixed key types",531 "{% set d = namespace() %}{% set d.a = 123 %}{{ d['a'] == 123 }}",532 json::object(),533 "True"534 );535 536 test_template(t, "tuple unpacking",537 "{% set t = (1, 2, 3) %}{% set a, b, c = t %}{{ a + b + c }}",538 json::object(),539 "6"540 );541}542 543static void test_filters(testing & t) {544 test_template(t, "upper",545 "{{ 'hello'|upper }}",546 json::object(),547 "HELLO"548 );549 550 test_template(t, "lower",551 "{{ 'HELLO'|lower }}",552 json::object(),553 "hello"554 );555 556 test_template(t, "upper array",557 "{{ items|upper }}",558 {{"items", json::array({"hello", "world"})}},559 "['HELLO', 'WORLD']"560 );561 562 test_template(t, "upper dict",563 "{{ items|upper }}",564 {{"items", {{"hello", "world"}}}},565 "{'HELLO': 'WORLD'}"566 );567 568 test_template(t, "capitalize",569 "{{ 'heLlo World'|capitalize }}",570 json::object(),571 "Hello world"572 );573 574 test_template(t, "title",575 "{{ 'hello world'|title }}",576 json::object(),577 "Hello World"578 );579 580 test_template(t, "trim",581 "{{ ' \r\n\thello\t\n\r '|trim }}",582 json::object(),583 "hello"584 );585 586 test_template(t, "trim chars",587 "{{ 'xyxhelloxyx'|trim('xy') }}",588 json::object(),589 "hello"590 );591 592 test_template(t, "length string",593 "{{ 'hello'|length }}",594 json::object(),595 "5"596 );597 598 test_template(t, "replace",599 "{{ 'hello world'|replace('world', 'jinja') }}",600 json::object(),601 "hello jinja"602 );603 604 test_template(t, "length (count alias) list",605 "{{ items|count }}",606 {{"items", json::array({1, 2, 3})}},607 "3"608 );609 610 test_template(t, "first",611 "{{ items|first }}",612 {{"items", json::array({10, 20, 30})}},613 "10"614 );615 616 test_template(t, "last",617 "{{ items|last }}",618 {{"items", json::array({10, 20, 30})}},619 "30"620 );621 622 test_template(t, "reverse",623 "{% for i in items|reverse %}{{ i }}{% endfor %}",624 {{"items", json::array({1, 2, 3})}},625 "321"626 );627 628 test_template(t, "sort",629 "{% for i in items|sort %}{{ i }}{% endfor %}",630 {{"items", json::array({3, 1, 2})}},631 "123"632 );633 634 test_template(t, "sort reverse",635 "{% for i in items|sort(true) %}{{ i }}{% endfor %}",636 {{"items", json::array({3, 1, 2})}},637 "321"638 );639 640 test_template(t, "sort with attribute",641 "{{ items|sort(attribute='name')|join(attribute='age') }}",642 {{"items", json::array({643 json({{"name", "c"}, {"age", 3}}),644 json({{"name", "a"}, {"age", 1}}),645 json({{"name", "b"}, {"age", 2}}),646 })}},647 "123"648 );649 650 test_template(t, "sort with numeric attribute",651 "{{ items|sort(attribute=0)|join(attribute=1) }}",652 {{"items", json::array({653 json::array({3, "z"}),654 json::array({1, "x"}),655 json::array({2, "y"}),656 })}},657 "xyz"658 );659 660 test_template(t, "join",661 "{{ items|join(', ') }}",662 {{"items", json::array({"a", "b", "c"})}},663 "a, b, c"664 );665 666 test_template(t, "join default separator",667 "{{ items|join }}",668 {{"items", json::array({"x", "y", "z"})}},669 "xyz"670 );671 672 test_template(t, "abs",673 "{{ -5|abs }}",674 json::object(),675 "5"676 );677 678 test_template(t, "int from string",679 "{{ '42'|int }}",680 json::object(),681 "42"682 );683 684 test_template(t, "int from string with default",685 "{{ ''|int(1) }}",686 json::object(),687 "1"688 );689 690 test_template(t, "int from string with base",691 "{{ '11'|int(base=2) }}",692 json::object(),693 "3"694 );695 696 test_template(t, "float from string",697 "{{ '3.14'|float }}",698 json::object(),699 "3.14"700 );701 702 test_template(t, "default with value",703 "{{ x|default('fallback') }}",704 {{"x", "actual"}},705 "actual"706 );707 708 test_template(t, "default without value",709 "{{ y|default('fallback') }}",710 json::object(),711 "fallback"712 );713 714 test_template(t, "default (d alias) with falsy value",715 "{{ ''|d('fallback', true) }}",716 json::object(),717 "fallback"718 );719 720 test_template(t, "tojson ensure_ascii=true",721 "{{ data|tojson(ensure_ascii=true) }}",722 {{"data", "\u2713"}},723 "\"\\u2713\""724 );725 726 test_template(t, "tojson ensure_ascii=true nested object",727 "{{ data|tojson(ensure_ascii=true) }}",728 {{"data", {729 {"text", "\u2713"},730 {"items", json::array({"é", {{"snowman", "☃"}}})}731 }}},732 "{\"text\": \"\\u2713\", \"items\": [\"\\u00e9\", {\"snowman\": \"\\u2603\"}]}"733 );734 735 test_template(t, "tojson ensure_ascii=true indent=2",736 "{{ data|tojson(ensure_ascii=true, indent=2) }}",737 {{"data", {738 {"text", "\u2713"},739 {"nested", {{"accent", "é"}}}740 }}},741 "{\n \"text\": \"\\u2713\",\n \"nested\": {\n \"accent\": \"\\u00e9\"\n }\n}"742 );743 744 test_template(t, "tojson ensure_ascii=true preserves existing escapes",745 "{{ data|tojson(ensure_ascii=true) }}",746 {{"data", {747 {"emoji", "😀"},748 {"line", "a\nb"}749 }}},750 "{\"emoji\": \"\\ud83d\\ude00\", \"line\": \"a\\nb\"}"751 );752 753 test_template(t, "tojson sort_keys=true",754 "{{ data|tojson(sort_keys=true) }}",755 {{"data", {{"b", 2}, {"a", 1}}}},756 "{\"a\": 1, \"b\": 2}"757 );758 759 test_template(t, "tojson",760 "{{ data|tojson }}",761 {{"data", {{"a", 1}, {"b", json::array({1, 2})}}}},762 "{\"a\": 1, \"b\": [1, 2]}"763 );764 765 test_template(t, "tojson indent=4",766 "{{ data|tojson(indent=4) }}",767 {{"data", {{"a", 1}, {"b", json::array({1, 2})}}}},768 "{\n \"a\": 1,\n \"b\": [\n 1,\n 2\n ]\n}"769 );770 771 test_template(t, "tojson separators=(',',':')",772 "{{ data|tojson(separators=(',',':')) }}",773 {{"data", {{"a", 1}, {"b", json::array({1, 2})}}}},774 "{\"a\":1,\"b\":[1,2]}"775 );776 777 test_template(t, "tojson separators=(',',': ') indent=2",778 "{{ data|tojson(separators=(',',': '), indent=2) }}",779 {{"data", {{"a", 1}, {"b", json::array({1, 2})}}}},780 "{\n \"a\": 1,\n \"b\": [\n 1,\n 2\n ]\n}"781 );782 783 test_template(t, "indent",784 "{{ data|indent(2) }}",785 {{ "data", "foo\nbar" }},786 "foo\n bar"787 );788 789 test_template(t, "indent first only",790 "{{ data|indent(width=3,first=true) }}",791 {{ "data", "foo\nbar" }},792 " foo\n bar"793 );794 795 test_template(t, "indent blank lines and first line",796 "{{ data|indent(width=5,blank=true,first=true) }}",797 {{ "data", "foo\n\nbar" }},798 " foo\n \n bar"799 );800 801 test_template(t, "indent with default width",802 "{{ data|indent() }}",803 {{ "data", "foo\nbar" }},804 "foo\n bar"805 );806 807 test_template(t, "indent with no newline",808 "{{ data|indent }}",809 {{ "data", "foo" }},810 "foo"811 );812 813 test_template(t, "indent with trailing newline",814 "{{ data|indent(blank=true) }}",815 {{ "data", "foo\n" }},816 "foo\n "817 );818 819 test_template(t, "indent with string",820 "{{ data|indent(width='>>>>') }}",821 {{ "data", "foo\nbar" }},822 "foo\n>>>>bar"823 );824 825 test_template(t, "chained filters",826 "{{ ' HELLO '|trim|lower }}",827 json::object(),828 "hello"829 );830 831 test_template(t, "int filter on integer is identity",832 "{{ value|int }}",833 {{"value", 7}},834 "7"835 );836 837 test_template(t, "none to string",838 "{{ x|string }}",839 {{"x", nullptr}},840 "None"841 );842}843 844static void test_literals(testing & t) {845 test_template(t, "integer",846 "{{ 42 }}",847 json::object(),848 "42"849 );850 851 test_template(t, "float",852 "{{ 3.14 }}",853 json::object(),854 "3.14"855 );856 857 test_template(t, "string",858 "{{ 'hello' }}",859 json::object(),860 "hello"861 );862 863 test_template(t, "boolean true",864 "{{ true }}",865 json::object(),866 "True"867 );868 869 test_template(t, "boolean false",870 "{{ false }}",871 json::object(),872 "False"873 );874 875 test_template(t, "none",876 "{% if x is none %}null{% endif %}",877 {{"x", nullptr}},878 "null"879 );880 881 test_template(t, "list literal",882 "{% for i in [1, 2, 3] %}{{ i }}{% endfor %}",883 json::object(),884 "123"885 );886 887 test_template(t, "dict literal",888 "{% set d = {'a': 1} %}{{ d.a }}",889 json::object(),890 "1"891 );892 893 test_template(t, "integer|abs",894 "{{ -42 | abs }}",895 json::object(),896 "42"897 );898 899 test_template(t, "integer|float",900 "{{ 42 | float }}",901 json::object(),902 "42.0"903 );904 905 test_template(t, "integer|tojson",906 "{{ 42 | tojson }}",907 json::object(),908 "42"909 );910 911 test_template(t, "float|abs",912 "{{ -3.14 | abs }}",913 json::object(),914 "3.14"915 );916 917 test_template(t, "float|int",918 "{{ 3.14 | int }}",919 json::object(),920 "3"921 );922 923 test_template(t, "float|tojson",924 "{{ 3.14 | tojson }}",925 json::object(),926 "3.14"927 );928 929 test_template(t, "string|tojson",930 "{{ 'hello' | tojson }}",931 json::object(),932 "\"hello\""933 );934 935 test_template(t, "boolean|int",936 "{{ true | int }}",937 json::object(),938 "1"939 );940 941 test_template(t, "boolean|float",942 "{{ true | float }}",943 json::object(),944 "1.0"945 );946 947 test_template(t, "boolean|tojson",948 "{{ true | tojson }}",949 json::object(),950 "true"951 );952}953 954static void test_comments(testing & t) {955 test_template(t, "inline comment",956 "before{# comment #}after",957 json::object(),958 "beforeafter"959 );960 961 test_template(t, "comment ignores code",962 "{% set x = 1 %}{# {% set x = 999 %} #}{{ x }}",963 json::object(),964 "1"965 );966}967 968static void test_macros(testing & t) {969 test_template(t, "simple macro",970 "{% macro greet(name) %}Hello {{ name }}{% endmacro %}{{ greet('World') }}",971 json::object(),972 "Hello World"973 );974 975 test_template(t, "macro default arg",976 "{% macro greet(name='Guest') %}Hi {{ name }}{% endmacro %}{{ greet() }}",977 json::object(),978 "Hi Guest"979 );980 981 test_template(t, "macro kwargs input",982 "{% macro my_func(a, b=False) %}{% if b %}{{ a }}{% else %}nope{% endif %}{% endmacro %}{{ my_func(1, b=True) }}",983 json::object(),984 "1"985 );986 987 test_template(t, "macro with multiple args",988 "{% 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) }}",989 json::object(),990 "3,6,11,8"991 );992 993 test_template(t, "macro with kwarg out-of-order input",994 "{% macro greet(first, last, greeting='Hello') %}{{ greeting }}, {{ first }} {{ last }}{% endmacro %}{{ greet(last='Smith', first='John') }},{{ greet(last='Doe', greeting='Hi', first='Jane') }}",995 json::object(),996 "Hello, John Smith,Hi, Jane Doe"997 );998 999 test_template(t, "macro with caller",1000 "\1001{%- macro nest_dict(o, i, ff='') %}\n\1002 {{- caller(ff) }}\n\1003 {%- for k, v in o|items %}\n\1004 {{- i + k + ': ' }}\n\1005 {%- if v is mapping %}\n\1006 {{- '{' }}\n\1007 {% call(f) nest_dict(v, i + ' ') %}\n\1008 {{- 'fail' if ff is undefined }}\n\1009 {%- endcall %}\n\1010 {{- i + '}' }}\n\1011 {% else %}\n\1012 {{- v|string }}\n\1013 {% endif %}\n\1014 {%- endfor %}\n\1015{%- endmacro %}\n\1016{%- call(f) nest_dict({'root1': 1, 'root2': {'nest1': 1, 'nest2': {'nest3': 2}}}, ' ', 'Dict') %}\n\1017 {{- 'fail' if ff is defined }}\n\1018 {{- f + ' {' }}\n\1019{% endcall %}\n\1020{{- '}' }}",1021 json::object(),1022 "Dict {\n root1: 1\n root2: {\n nest1: 1\n nest2: {\n nest3: 2\n }\n }\n}"1023 );1024}1025 1026static void test_namespace(testing & t) {1027 test_template(t, "namespace counter",1028 "{% set ns = namespace(count=0) %}{% for i in range(3) %}{% set ns.count = ns.count + 1 %}{% endfor %}{{ ns.count }}",1029 json::object(),1030 "3"1031 );1032}1033 1034static void test_tests(testing & t) {1035 test_template(t, "is odd",1036 "{% if 3 is odd %}yes{% endif %}",1037 json::object(),1038 "yes"1039 );1040 1041 test_template(t, "is even",1042 "{% if 4 is even %}yes{% endif %}",1043 json::object(),1044 "yes"1045 );1046 1047 test_template(t, "is false",1048 "{{ 'yes' if x is false }}",1049 {{"x", false}},1050 "yes"1051 );1052 1053 test_template(t, "is true",1054 "{{ 'yes' if x is true }}",1055 {{"x", true}},1056 "yes"1057 );1058 1059 test_template(t, "string is false",1060 "{{ 'yes' if x is false else 'no' }}",1061 {{"x", ""}},1062 "no"1063 );1064 1065 test_template(t, "is divisibleby",1066 "{{ 'yes' if x is divisibleby(2) }}",1067 {{"x", 2}},1068 "yes"1069 );1070 1071 test_template(t, "is eq",1072 "{{ 'yes' if 3 is eq(3) }}",1073 json::object(),1074 "yes"1075 );1076 1077 test_template(t, "is not equalto",1078 "{{ 'yes' if 3 is not equalto(4) }}",1079 json::object(),1080 "yes"1081 );1082 1083 test_template(t, "is ge",1084 "{{ 'yes' if 3 is ge(3) }}",1085 json::object(),1086 "yes"1087 );1088 1089 test_template(t, "is gt",1090 "{{ 'yes' if 3 is gt(2) }}",1091 json::object(),1092 "yes"1093 );1094 1095 test_template(t, "is greaterthan",1096 "{{ 'yes' if 3 is greaterthan(2) }}",1097 json::object(),1098 "yes"1099 );1100 1101 test_template(t, "is lt",1102 "{{ 'yes' if 2 is lt(3) }}",1103 json::object(),1104 "yes"1105 );1106 1107 test_template(t, "is lessthan",1108 "{{ 'yes' if 2 is lessthan(3) }}",1109 json::object(),1110 "yes"1111 );1112 1113 test_template(t, "is ne",1114 "{{ 'yes' if 2 is ne(3) }}",1115 json::object(),1116 "yes"1117 );1118 1119 test_template(t, "is lower",1120 "{{ 'yes' if 'lowercase' is lower }}",1121 json::object(),1122 "yes"1123 );1124 1125 test_template(t, "is upper",1126 "{{ 'yes' if 'UPPERCASE' is upper }}",1127 json::object(),1128 "yes"1129 );1130 1131 test_template(t, "is sameas",1132 "{{ 'yes' if x is sameas(false) }}",1133 {{"x", false}},1134 "yes"1135 );1136 1137 test_template(t, "is boolean",1138 "{{ 'yes' if x is boolean }}",1139 {{"x", true}},1140 "yes"1141 );1142 1143 test_template(t, "is callable",1144 "{{ 'yes' if ''.strip is callable }}",1145 json::object(),1146 "yes"1147 );1148 1149 test_template(t, "is escaped",1150 "{{ 'yes' if 'foo'|safe is escaped }}",1151 json::object(),1152 "yes"1153 );1154 1155 test_template(t, "is filter",1156 "{{ 'yes' if 'trim' is filter }}",1157 json::object(),1158 "yes"1159 );1160 1161 test_template(t, "is float",1162 "{{ 'yes' if x is float }}",1163 {{"x", 1.1}},1164 "yes"1165 );1166 1167 test_template(t, "is integer",1168 "{{ 'yes' if x is integer }}",1169 {{"x", 1}},1170 "yes"1171 );1172 1173 test_template(t, "is sequence",1174 "{{ 'yes' if x is sequence }}",1175 {{"x", json::array({1, 2, 3})}},1176 "yes"1177 );1178 1179 test_template(t, "is test",1180 "{{ 'yes' if 'sequence' is test }}",1181 json::object(),1182 "yes"1183 );1184 1185 test_template(t, "is undefined",1186 "{{ 'yes' if x is undefined }}",1187 json::object(),1188 "yes"1189 );1190 1191 test_template(t, "is none",1192 "{% if x is none %}yes{% endif %}",1193 {{"x", nullptr}},1194 "yes"1195 );1196 1197 test_template(t, "is string",1198 "{% if x is string %}yes{% endif %}",1199 {{"x", "hello"}},1200 "yes"