1893 lines · cpp
1//===- llvm/unittest/Support/MustacheTest.cpp ----------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// Test conforming to Mustache 1.4.2 spec found here:10// https://github.com/mustache/spec11//12//===----------------------------------------------------------------------===//13 14#include "llvm/Support/Mustache.h"15#include "llvm/Support/raw_ostream.h"16#include "gtest/gtest.h"17 18using namespace llvm;19using namespace llvm::mustache;20using namespace llvm::json;21 22TEST(MustacheInterpolation, NoInterpolation) {23 // Mustache-free templates should render as-is.24 Value D = {};25 BumpPtrAllocator Allocator;26 StringSaver Saver(Allocator);27 MustacheContext Ctx(Allocator, Saver);28 Template T("Hello from {Mustache}!\n", Ctx);29 std::string Out;30 raw_string_ostream OS(Out);31 T.render(D, OS);32 EXPECT_EQ("Hello from {Mustache}!\n", Out);33}34 35TEST(MustacheInterpolation, BasicInterpolation) {36 // Unadorned tags should interpolate content into the template.37 Value D = Object{{"subject", "World"}};38 BumpPtrAllocator Allocator;39 StringSaver Saver(Allocator);40 MustacheContext Ctx(Allocator, Saver);41 Template T("Hello, {{subject}}!", Ctx);42 std::string Out;43 raw_string_ostream OS(Out);44 T.render(D, OS);45 EXPECT_EQ("Hello, World!", Out);46}47 48TEST(MustacheInterpolation, NoReinterpolation) {49 // Interpolated tag output should not be re-interpolated.50 Value D = Object{{"template", "{{planet}}"}, {"planet", "Earth"}};51 BumpPtrAllocator Allocator;52 StringSaver Saver(Allocator);53 MustacheContext Ctx(Allocator, Saver);54 Template T("{{template}}: {{planet}}", Ctx);55 std::string Out;56 raw_string_ostream OS(Out);57 T.render(D, OS);58 EXPECT_EQ("{{planet}}: Earth", Out);59}60 61TEST(MustacheInterpolation, HTMLEscaping) {62 // Interpolated tag output should not be re-interpolated.63 Value D = Object{64 {"forbidden", "& \" < >"},65 };66 BumpPtrAllocator Allocator;67 StringSaver Saver(Allocator);68 MustacheContext Ctx(Allocator, Saver);69 Template T("These characters should be HTML escaped: {{forbidden}}\n", Ctx);70 std::string Out;71 raw_string_ostream OS(Out);72 T.render(D, OS);73 EXPECT_EQ("These characters should be HTML escaped: & " < >\n",74 Out);75}76 77TEST(MustacheInterpolation, Ampersand) {78 // Interpolated tag output should not be re-interpolated.79 Value D = Object{80 {"forbidden", "& \" < >"},81 };82 BumpPtrAllocator Allocator;83 StringSaver Saver(Allocator);84 MustacheContext Ctx(Allocator, Saver);85 Template T("These characters should not be HTML escaped: {{&forbidden}}\n",86 Ctx);87 std::string Out;88 raw_string_ostream OS(Out);89 T.render(D, OS);90 EXPECT_EQ("These characters should not be HTML escaped: & \" < >\n", Out);91}92 93TEST(MustacheInterpolation, BasicIntegerInterpolation) {94 // Integers should interpolate seamlessly.95 Value D = Object{{"mph", 85}};96 BumpPtrAllocator Allocator;97 StringSaver Saver(Allocator);98 MustacheContext Ctx(Allocator, Saver);99 Template T("{{mph}} miles an hour!", Ctx);100 std::string Out;101 raw_string_ostream OS(Out);102 T.render(D, OS);103 EXPECT_EQ("85 miles an hour!", Out);104}105 106TEST(MustacheInterpolation, AmpersandIntegerInterpolation) {107 // Integers should interpolate seamlessly.108 Value D = Object{{"mph", 85}};109 BumpPtrAllocator Allocator;110 StringSaver Saver(Allocator);111 MustacheContext Ctx(Allocator, Saver);112 Template T("{{&mph}} miles an hour!", Ctx);113 std::string Out;114 raw_string_ostream OS(Out);115 T.render(D, OS);116 EXPECT_EQ("85 miles an hour!", Out);117}118 119TEST(MustacheInterpolation, BasicDecimalInterpolation) {120 // Decimals should interpolate seamlessly with proper significance.121 Value D = Object{{"power", 1.21}};122 BumpPtrAllocator Allocator;123 StringSaver Saver(Allocator);124 MustacheContext Ctx(Allocator, Saver);125 Template T("{{power}} jiggawatts!", Ctx);126 std::string Out;127 raw_string_ostream OS(Out);128 T.render(D, OS);129 EXPECT_EQ("1.21 jiggawatts!", Out);130}131 132TEST(MustacheInterpolation, BasicNullInterpolation) {133 // Nulls should interpolate as the empty string.134 Value D = Object{{"cannot", nullptr}};135 BumpPtrAllocator Allocator;136 StringSaver Saver(Allocator);137 MustacheContext Ctx(Allocator, Saver);138 Template T("I ({{cannot}}) be seen!", Ctx);139 std::string Out;140 raw_string_ostream OS(Out);141 T.render(D, OS);142 EXPECT_EQ("I () be seen!", Out);143}144 145TEST(MustacheInterpolation, AmpersandNullInterpolation) {146 // Nulls should interpolate as the empty string.147 Value D = Object{{"cannot", nullptr}};148 BumpPtrAllocator Allocator;149 StringSaver Saver(Allocator);150 MustacheContext Ctx(Allocator, Saver);151 Template T("I ({{&cannot}}) be seen!", Ctx);152 std::string Out;153 raw_string_ostream OS(Out);154 T.render(D, OS);155 EXPECT_EQ("I () be seen!", Out);156}157 158TEST(MustacheInterpolation, BasicContextMissInterpolation) {159 // Failed context lookups should default to empty strings.160 Value D = Object{};161 BumpPtrAllocator Allocator;162 StringSaver Saver(Allocator);163 MustacheContext Ctx(Allocator, Saver);164 Template T("I ({{cannot}}) be seen!", Ctx);165 std::string Out;166 raw_string_ostream OS(Out);167 T.render(D, OS);168 EXPECT_EQ("I () be seen!", Out);169}170 171TEST(MustacheInterpolation, DottedNamesBasicInterpolation) {172 // Dotted names should be considered a form of shorthand for sections.173 Value D = Object{{"person", Object{{"name", "Joe"}}}};174 BumpPtrAllocator Allocator;175 StringSaver Saver(Allocator);176 MustacheContext Ctx(Allocator, Saver);177 Template T("{{person.name}} == {{#person}}{{name}}{{/person}}", Ctx);178 std::string Out;179 raw_string_ostream OS(Out);180 T.render(D, OS);181 EXPECT_EQ("Joe == Joe", Out);182}183 184TEST(MustacheInterpolation, DottedNamesAmpersandInterpolation) {185 // Dotted names should be considered a form of shorthand for sections.186 Value D = Object{{"person", Object{{"name", "Joe"}}}};187 BumpPtrAllocator Allocator;188 StringSaver Saver(Allocator);189 MustacheContext Ctx(Allocator, Saver);190 Template T("{{&person.name}} == {{#person}}{{&name}}{{/person}}", Ctx);191 std::string Out;192 raw_string_ostream OS(Out);193 T.render(D, OS);194 EXPECT_EQ("Joe == Joe", Out);195}196 197TEST(MustacheInterpolation, DottedNamesArbitraryDepth) {198 // Dotted names should be functional to any level of nesting.199 Value D = Object{200 {"a",201 Object{{"b",202 Object{{"c",203 Object{{"d",204 Object{{"e", Object{{"name", "Phil"}}}}}}}}}}}};205 BumpPtrAllocator Allocator;206 StringSaver Saver(Allocator);207 MustacheContext Ctx(Allocator, Saver);208 Template T("{{a.b.c.d.e.name}}", Ctx);209 std::string Out;210 raw_string_ostream OS(Out);211 T.render(D, OS);212 EXPECT_EQ("Phil", Out);213}214 215TEST(MustacheInterpolation, DottedNamesBrokenChains) {216 // Any falsey value prior to the last part of the name should yield ''.217 Value D = Object{{"a", Object{}}};218 BumpPtrAllocator Allocator;219 StringSaver Saver(Allocator);220 MustacheContext Ctx(Allocator, Saver);221 Template T("{{a.b.c}} == ", Ctx);222 std::string Out;223 raw_string_ostream OS(Out);224 T.render(D, OS);225 EXPECT_EQ(" == ", Out);226}227 228TEST(MustacheInterpolation, DottedNamesBrokenChainResolution) {229 // Each part of a dotted name should resolve only against its parent.230 Value D =231 Object{{"a", Object{{"b", Object{}}}}, {"c", Object{{"name", "Jim"}}}};232 BumpPtrAllocator Allocator;233 StringSaver Saver(Allocator);234 MustacheContext Ctx(Allocator, Saver);235 Template T("{{a.b.c.name}} == ", Ctx);236 std::string Out;237 raw_string_ostream OS(Out);238 T.render(D, OS);239 EXPECT_EQ(" == ", Out);240}241 242TEST(MustacheInterpolation, DottedNamesInitialResolution) {243 // The first part of a dotted name should resolve as any other name.244 Value D = Object{245 {"a",246 Object{247 {"b",248 Object{{"c",249 Object{{"d", Object{{"e", Object{{"name", "Phil"}}}}}}}}}}},250 {"b",251 Object{{"c", Object{{"d", Object{{"e", Object{{"name", "Wrong"}}}}}}}}}};252 BumpPtrAllocator Allocator;253 StringSaver Saver(Allocator);254 MustacheContext Ctx(Allocator, Saver);255 Template T("{{#a}}{{b.c.d.e.name}}{{/a}}", Ctx);256 std::string Out;257 raw_string_ostream OS(Out);258 T.render(D, OS);259 EXPECT_EQ("Phil", Out);260}261 262TEST(MustacheInterpolation, DottedNamesContextPrecedence) {263 // Dotted names should be resolved against former resolutions.264 Value D =265 Object{{"a", Object{{"b", Object{}}}}, {"b", Object{{"c", "ERROR"}}}};266 BumpPtrAllocator Allocator;267 StringSaver Saver(Allocator);268 MustacheContext Ctx(Allocator, Saver);269 Template T("{{#a}}{{b.c}}{{/a}}", Ctx);270 std::string Out;271 raw_string_ostream OS(Out);272 T.render(D, OS);273 EXPECT_EQ("", Out);274}275 276TEST(MustacheInterpolation, DottedNamesAreNotSingleKeys) {277 // Dotted names shall not be parsed as single, atomic keys278 Value D = Object{{"a.b", "c"}};279 BumpPtrAllocator Allocator;280 StringSaver Saver(Allocator);281 MustacheContext Ctx(Allocator, Saver);282 Template T("{{a.b}}", Ctx);283 std::string Out;284 raw_string_ostream OS(Out);285 T.render(D, OS);286 EXPECT_EQ("", Out);287}288 289TEST(MustacheInterpolation, DottedNamesNoMasking) {290 // Dotted Names in a given context are unavailable due to dot splitting291 Value D = Object{{"a.b", "c"}, {"a", Object{{"b", "d"}}}};292 BumpPtrAllocator Allocator;293 StringSaver Saver(Allocator);294 MustacheContext Ctx(Allocator, Saver);295 Template T("{{a.b}}", Ctx);296 std::string Out;297 raw_string_ostream OS(Out);298 T.render(D, OS);299 EXPECT_EQ("d", Out);300}301 302TEST(MustacheInterpolation, ImplicitIteratorsBasicInterpolation) {303 // Unadorned tags should interpolate content into the template.304 Value D = "world";305 BumpPtrAllocator Allocator;306 StringSaver Saver(Allocator);307 MustacheContext Ctx(Allocator, Saver);308 Template T("Hello, {{.}}!\n", Ctx);309 std::string Out;310 raw_string_ostream OS(Out);311 T.render(D, OS);312 EXPECT_EQ("Hello, world!\n", Out);313}314 315TEST(MustacheInterpolation, ImplicitIteratorsAmersand) {316 // Basic interpolation should be HTML escaped.317 Value D = "& \" < >";318 BumpPtrAllocator Allocator;319 StringSaver Saver(Allocator);320 MustacheContext Ctx(Allocator, Saver);321 Template T("These characters should not be HTML escaped: {{&.}}\n", Ctx);322 std::string Out;323 raw_string_ostream OS(Out);324 T.render(D, OS);325 EXPECT_EQ("These characters should not be HTML escaped: & \" < >\n", Out);326}327 328TEST(MustacheInterpolation, ImplicitIteratorsInteger) {329 // Integers should interpolate seamlessly.330 Value D = 85;331 BumpPtrAllocator Allocator;332 StringSaver Saver(Allocator);333 MustacheContext Ctx(Allocator, Saver);334 Template T("{{.}} miles an hour!\n", Ctx);335 std::string Out;336 raw_string_ostream OS(Out);337 T.render(D, OS);338 EXPECT_EQ("85 miles an hour!\n", Out);339}340 341TEST(MustacheInterpolation, InterpolationSurroundingWhitespace) {342 // Interpolation should not alter surrounding whitespace.343 Value D = Object{{"string", "---"}};344 BumpPtrAllocator Allocator;345 StringSaver Saver(Allocator);346 MustacheContext Ctx(Allocator, Saver);347 Template T("| {{string}} |", Ctx);348 std::string Out;349 raw_string_ostream OS(Out);350 T.render(D, OS);351 EXPECT_EQ("| --- |", Out);352}353 354TEST(MustacheInterpolation, AmersandSurroundingWhitespace) {355 // Interpolation should not alter surrounding whitespace.356 Value D = Object{{"string", "---"}};357 BumpPtrAllocator Allocator;358 StringSaver Saver(Allocator);359 MustacheContext Ctx(Allocator, Saver);360 Template T("| {{&string}} |", Ctx);361 std::string Out;362 raw_string_ostream OS(Out);363 T.render(D, OS);364 EXPECT_EQ("| --- |", Out);365}366 367TEST(MustacheInterpolation, StandaloneInterpolationWithWhitespace) {368 // Standalone interpolation should not alter surrounding whitespace.369 Value D = Object{{"string", "---"}};370 BumpPtrAllocator Allocator;371 StringSaver Saver(Allocator);372 MustacheContext Ctx(Allocator, Saver);373 Template T(" {{string}}\n", Ctx);374 std::string Out;375 raw_string_ostream OS(Out);376 T.render(D, OS);377 EXPECT_EQ(" ---\n", Out);378}379 380TEST(MustacheInterpolation, StandaloneAmpersandWithWhitespace) {381 // Standalone interpolation should not alter surrounding whitespace.382 Value D = Object{{"string", "---"}};383 BumpPtrAllocator Allocator;384 StringSaver Saver(Allocator);385 MustacheContext Ctx(Allocator, Saver);386 Template T(" {{&string}}\n", Ctx);387 std::string Out;388 raw_string_ostream OS(Out);389 T.render(D, OS);390 EXPECT_EQ(" ---\n", Out);391}392 393TEST(MustacheInterpolation, InterpolationWithPadding) {394 // Superfluous in-tag whitespace should be ignored.395 Value D = Object{{"string", "---"}};396 BumpPtrAllocator Allocator;397 StringSaver Saver(Allocator);398 MustacheContext Ctx(Allocator, Saver);399 Template T("|{{ string }}|", Ctx);400 std::string Out;401 raw_string_ostream OS(Out);402 T.render(D, OS);403 EXPECT_EQ("|---|", Out);404}405 406TEST(MustacheInterpolation, AmpersandWithPadding) {407 // Superfluous in-tag whitespace should be ignored.408 Value D = Object{{"string", "---"}};409 BumpPtrAllocator Allocator;410 StringSaver Saver(Allocator);411 MustacheContext Ctx(Allocator, Saver);412 Template T("|{{& string }}|", Ctx);413 std::string Out;414 raw_string_ostream OS(Out);415 T.render(D, OS);416 EXPECT_EQ("|---|", Out);417}418 419TEST(MustacheInterpolation, InterpolationWithPaddingAndNewlines) {420 // Superfluous in-tag whitespace should be ignored.421 Value D = Object{{"string", "---"}};422 BumpPtrAllocator Allocator;423 StringSaver Saver(Allocator);424 MustacheContext Ctx(Allocator, Saver);425 Template T("|{{ string \n\n\n }}|", Ctx);426 std::string Out;427 raw_string_ostream OS(Out);428 T.render(D, OS);429 EXPECT_EQ("|---|", Out);430}431 432TEST(MustacheSections, Truthy) {433 Value D = Object{{"boolean", true}};434 BumpPtrAllocator Allocator;435 StringSaver Saver(Allocator);436 MustacheContext Ctx(Allocator, Saver);437 Template T("{{#boolean}}This should be rendered.{{/boolean}}", Ctx);438 std::string Out;439 raw_string_ostream OS(Out);440 T.render(D, OS);441 EXPECT_EQ("This should be rendered.", Out);442}443 444TEST(MustacheSections, Falsey) {445 Value D = Object{{"boolean", false}};446 BumpPtrAllocator Allocator;447 StringSaver Saver(Allocator);448 MustacheContext Ctx(Allocator, Saver);449 Template T("{{#boolean}}This should not be rendered.{{/boolean}}", Ctx);450 std::string Out;451 raw_string_ostream OS(Out);452 T.render(D, OS);453 EXPECT_EQ("", Out);454}455 456TEST(MustacheInterpolation, IsFalseyNull) {457 // Mustache-free templates should render as-is.458 Value D = Object{{"boolean", nullptr}};459 BumpPtrAllocator Allocator;460 StringSaver Saver(Allocator);461 MustacheContext Ctx(Allocator, Saver);462 Template T("Hello, {{#boolean}}World{{/boolean}}", Ctx);463 std::string Out;464 raw_string_ostream OS(Out);465 T.render(D, OS);466 EXPECT_EQ("Hello, ", Out);467}468 469TEST(MustacheInterpolation, IsFalseyArray) {470 // Mustache-free templates should render as-is.471 Value D = Object{{"boolean", Array()}};472 BumpPtrAllocator Allocator;473 StringSaver Saver(Allocator);474 MustacheContext Ctx(Allocator, Saver);475 Template T("Hello, {{#boolean}}World{{/boolean}}", Ctx);476 std::string Out;477 raw_string_ostream OS(Out);478 T.render(D, OS);479 EXPECT_EQ("Hello, ", Out);480}481 482TEST(MustacheInterpolation, IsFalseyObject) {483 // Mustache-free templates should render as-is.484 Value D = Object{{"boolean", Object{}}};485 BumpPtrAllocator Allocator;486 StringSaver Saver(Allocator);487 MustacheContext Ctx(Allocator, Saver);488 Template T("Hello, {{#boolean}}World{{/boolean}}", Ctx);489 std::string Out;490 raw_string_ostream OS(Out);491 T.render(D, OS);492 EXPECT_EQ("Hello, World", Out);493}494 495TEST(MustacheInterpolation, DoubleRendering) {496 // Mustache-free templates should render as-is.497 Value D1 = Object{{"subject", "World"}};498 BumpPtrAllocator Allocator;499 StringSaver Saver(Allocator);500 MustacheContext Ctx(Allocator, Saver);501 Template T("Hello, {{subject}}!", Ctx);502 std::string Out1;503 raw_string_ostream OS1(Out1);504 T.render(D1, OS1);505 EXPECT_EQ("Hello, World!", Out1);506 std::string Out2;507 raw_string_ostream OS2(Out2);508 Value D2 = Object{{"subject", "New World"}};509 T.render(D2, OS2);510 EXPECT_EQ("Hello, New World!", Out2);511}512 513TEST(MustacheSections, NullIsFalsey) {514 Value D = Object{{"null", nullptr}};515 BumpPtrAllocator Allocator;516 StringSaver Saver(Allocator);517 MustacheContext Ctx(Allocator, Saver);518 Template T("{{#null}}This should not be rendered.{{/null}}", Ctx);519 std::string Out;520 raw_string_ostream OS(Out);521 T.render(D, OS);522 EXPECT_EQ("", Out);523}524 525TEST(MustacheSections, Context) {526 Value D = Object{{"context", Object{{"name", "Joe"}}}};527 BumpPtrAllocator Allocator;528 StringSaver Saver(Allocator);529 MustacheContext Ctx(Allocator, Saver);530 Template T("{{#context}}Hi {{name}}.{{/context}}", Ctx);531 std::string Out;532 raw_string_ostream OS(Out);533 T.render(D, OS);534 EXPECT_EQ("Hi Joe.", Out);535}536 537TEST(MustacheSections, ParentContexts) {538 Value D = Object{{"a", "foo"},539 {"b", "wrong"},540 {"sec", Object{{"b", "bar"}}},541 {"c", Object{{"d", "baz"}}}};542 BumpPtrAllocator Allocator;543 StringSaver Saver(Allocator);544 MustacheContext Ctx(Allocator, Saver);545 Template T("{{#sec}}{{a}}, {{b}}, {{c.d}}{{/sec}}", Ctx);546 std::string Out;547 raw_string_ostream OS(Out);548 T.render(D, OS);549 EXPECT_EQ("foo, bar, baz", Out);550}551 552TEST(MustacheSections, VariableTest) {553 Value D = Object{{"foo", "bar"}};554 BumpPtrAllocator Allocator;555 StringSaver Saver(Allocator);556 MustacheContext Ctx(Allocator, Saver);557 Template T("{{#foo}}{{.}} is {{foo}}{{/foo}}", Ctx);558 std::string Out;559 raw_string_ostream OS(Out);560 T.render(D, OS);561 EXPECT_EQ("bar is bar", Out);562}563 564TEST(MustacheSections, ListContexts) {565 Value D = Object{566 {"tops",567 Array{Object{568 {"tname", Object{{"upper", "A"}, {"lower", "a"}}},569 {"middles",570 Array{Object{{"mname", "1"},571 {"bottoms", Array{Object{{"bname", "x"}},572 Object{{"bname", "y"}}}}}}}}}}};573 BumpPtrAllocator Allocator;574 StringSaver Saver(Allocator);575 MustacheContext Ctx(Allocator, Saver);576 Template T("{{#tops}}"577 "{{#middles}}"578 "{{tname.lower}}{{mname}}."579 "{{#bottoms}}"580 "{{tname.upper}}{{mname}}{{bname}}."581 "{{/bottoms}}"582 "{{/middles}}"583 "{{/tops}}",584 Ctx);585 std::string Out;586 raw_string_ostream OS(Out);587 T.render(D, OS);588 EXPECT_EQ("a1.A1x.A1y.", Out);589}590 591TEST(MustacheSections, DeeplyNestedContexts) {592 Value D = Object{593 {"a", Object{{"one", 1}}},594 {"b", Object{{"two", 2}}},595 {"c", Object{{"three", 3}, {"d", Object{{"four", 4}, {"five", 5}}}}}};596 BumpPtrAllocator Allocator;597 StringSaver Saver(Allocator);598 MustacheContext Ctx(Allocator, Saver);599 Template T(600 "{{#a}}\n{{one}}\n{{#b}}\n{{one}}{{two}}{{one}}\n{{#c}}\n{{one}}{{two}}{{"601 "three}}{{two}}{{one}}\n{{#d}}\n{{one}}{{two}}{{three}}{{four}}{{three}}{"602 "{two}}{{one}}\n{{#five}}\n{{one}}{{two}}{{three}}{{four}}{{five}}{{four}"603 "}{{three}}{{two}}{{one}}\n{{one}}{{two}}{{three}}{{four}}{{.}}6{{.}}{{"604 "four}}{{three}}{{two}}{{one}}\n{{one}}{{two}}{{three}}{{four}}{{five}}{{"605 "four}}{{three}}{{two}}{{one}}\n{{/"606 "five}}\n{{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}}\n{{/"607 "d}}\n{{one}}{{two}}{{three}}{{two}}{{one}}\n{{/"608 "c}}\n{{one}}{{two}}{{one}}\n{{/b}}\n{{one}}\n{{/a}}\n",609 Ctx);610 std::string Out;611 raw_string_ostream OS(Out);612 T.render(D, OS);613 EXPECT_EQ("1\n121\n12321\n1234321\n123454321\n12345654321\n123454321\n1234321"614 "\n12321\n121\n1\n",615 Out);616}617 618TEST(MustacheSections, List) {619 Value D = Object{{"list", Array{Object{{"item", 1}}, Object{{"item", 2}},620 Object{{"item", 3}}}}};621 BumpPtrAllocator Allocator;622 StringSaver Saver(Allocator);623 MustacheContext Ctx(Allocator, Saver);624 Template T("{{#list}}{{item}}{{/list}}", Ctx);625 std::string Out;626 raw_string_ostream OS(Out);627 T.render(D, OS);628 EXPECT_EQ("123", Out);629}630 631TEST(MustacheSections, EmptyList) {632 Value D = Object{{"list", Array{}}};633 BumpPtrAllocator Allocator;634 StringSaver Saver(Allocator);635 MustacheContext Ctx(Allocator, Saver);636 Template T("{{#list}}Yay lists!{{/list}}", Ctx);637 std::string Out;638 raw_string_ostream OS(Out);639 T.render(D, OS);640 EXPECT_EQ("", Out);641}642 643TEST(MustacheSections, Doubled) {644 Value D = Object{{"bool", true}, {"two", "second"}};645 BumpPtrAllocator Allocator;646 StringSaver Saver(Allocator);647 MustacheContext Ctx(Allocator, Saver);648 Template T("{{#bool}}\n* first\n{{/bool}}\n* "649 "{{two}}\n{{#bool}}\n* third\n{{/bool}}\n",650 Ctx);651 std::string Out;652 raw_string_ostream OS(Out);653 T.render(D, OS);654 EXPECT_EQ("* first\n* second\n* third\n", Out);655}656 657TEST(MustacheSections, NestedTruthy) {658 Value D = Object{{"bool", true}};659 BumpPtrAllocator Allocator;660 StringSaver Saver(Allocator);661 MustacheContext Ctx(Allocator, Saver);662 Template T("| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |", Ctx);663 std::string Out;664 raw_string_ostream OS(Out);665 T.render(D, OS);666 EXPECT_EQ("| A B C D E |", Out);667}668 669TEST(MustacheSections, NestedFalsey) {670 Value D = Object{{"bool", false}};671 BumpPtrAllocator Allocator;672 StringSaver Saver(Allocator);673 MustacheContext Ctx(Allocator, Saver);674 Template T("| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |", Ctx);675 std::string Out;676 raw_string_ostream OS(Out);677 T.render(D, OS);678 EXPECT_EQ("| A E |", Out);679}680 681TEST(MustacheSections, ContextMisses) {682 Value D = Object{};683 BumpPtrAllocator Allocator;684 StringSaver Saver(Allocator);685 MustacheContext Ctx(Allocator, Saver);686 Template T("[{{#missing}}Found key 'missing'!{{/missing}}]", Ctx);687 std::string Out;688 raw_string_ostream OS(Out);689 T.render(D, OS);690 EXPECT_EQ("[]", Out);691}692 693TEST(MustacheSections, ImplicitIteratorString) {694 Value D = Object{{"list", Array{"a", "b", "c", "d", "e"}}};695 BumpPtrAllocator Allocator;696 StringSaver Saver(Allocator);697 MustacheContext Ctx(Allocator, Saver);698 Template T("{{#list}}({{.}}){{/list}}", Ctx);699 std::string Out;700 raw_string_ostream OS(Out);701 T.render(D, OS);702 EXPECT_EQ("(a)(b)(c)(d)(e)", Out);703}704 705TEST(MustacheSections, ImplicitIteratorInteger) {706 Value D = Object{{"list", Array{1, 2, 3, 4, 5}}};707 BumpPtrAllocator Allocator;708 StringSaver Saver(Allocator);709 MustacheContext Ctx(Allocator, Saver);710 Template T("{{#list}}({{.}}){{/list}}", Ctx);711 std::string Out;712 raw_string_ostream OS(Out);713 T.render(D, OS);714 EXPECT_EQ("(1)(2)(3)(4)(5)", Out);715}716 717TEST(MustacheSections, ImplicitIteratorArray) {718 Value D = Object{{"list", Array{Array{1, 2, 3}, Array{"a", "b", "c"}}}};719 BumpPtrAllocator Allocator;720 StringSaver Saver(Allocator);721 MustacheContext Ctx(Allocator, Saver);722 Template T("{{#list}}({{#.}}{{.}}{{/.}}){{/list}}", Ctx);723 std::string Out;724 raw_string_ostream OS(Out);725 T.render(D, OS);726 EXPECT_EQ("(123)(abc)", Out);727}728 729TEST(MustacheSections, ImplicitIteratorHTMLEscaping) {730 Value D = Object{{"list", Array{"&", "\"", "<", ">"}}};731 BumpPtrAllocator Allocator;732 StringSaver Saver(Allocator);733 MustacheContext Ctx(Allocator, Saver);734 Template T("{{#list}}({{.}}){{/list}}", Ctx);735 std::string Out;736 raw_string_ostream OS(Out);737 T.render(D, OS);738 EXPECT_EQ("(&)(")(<)(>)", Out);739}740 741TEST(MustacheSections, ImplicitIteratorAmpersand) {742 Value D = Object{{"list", Array{"&", "\"", "<", ">"}}};743 BumpPtrAllocator Allocator;744 StringSaver Saver(Allocator);745 MustacheContext Ctx(Allocator, Saver);746 Template T("{{#list}}({{&.}}){{/list}}", Ctx);747 std::string Out;748 raw_string_ostream OS(Out);749 T.render(D, OS);750 EXPECT_EQ("(&)(\")(<)(>)", Out);751}752 753TEST(MustacheSections, ImplicitIteratorRootLevel) {754 Value D = Array{Object{{"value", "a"}}, Object{{"value", "b"}}};755 BumpPtrAllocator Allocator;756 StringSaver Saver(Allocator);757 MustacheContext Ctx(Allocator, Saver);758 Template T("{{#.}}({{value}}){{/.}}", Ctx);759 std::string Out;760 raw_string_ostream OS(Out);761 T.render(D, OS);762 EXPECT_EQ("(a)(b)", Out);763}764 765TEST(MustacheSections, DottedNamesTruthy) {766 Value D = Object{{"a", Object{{"b", Object{{"c", true}}}}}};767 BumpPtrAllocator Allocator;768 StringSaver Saver(Allocator);769 MustacheContext Ctx(Allocator, Saver);770 Template T("{{#a.b.c}}Here{{/a.b.c}} == Here", Ctx);771 std::string Out;772 raw_string_ostream OS(Out);773 T.render(D, OS);774 EXPECT_EQ("Here == Here", Out);775}776 777TEST(MustacheSections, DottedNamesFalsey) {778 Value D = Object{{"a", Object{{"b", Object{{"c", false}}}}}};779 BumpPtrAllocator Allocator;780 StringSaver Saver(Allocator);781 MustacheContext Ctx(Allocator, Saver);782 Template T("{{#a.b.c}}Here{{/a.b.c}} == ", Ctx);783 std::string Out;784 raw_string_ostream OS(Out);785 T.render(D, OS);786 EXPECT_EQ(" == ", Out);787}788 789TEST(MustacheSections, DottedNamesBrokenChains) {790 Value D = Object{{"a", Object{{}}}};791 BumpPtrAllocator Allocator;792 StringSaver Saver(Allocator);793 MustacheContext Ctx(Allocator, Saver);794 Template T("{{#a.b.c}}Here{{/a.b.c}} == ", Ctx);795 std::string Out;796 raw_string_ostream OS(Out);797 T.render(D, OS);798 EXPECT_EQ(" == ", Out);799}800 801TEST(MustacheSections, SurroundingWhitespace) {802 Value D = Object{{"boolean", true}};803 BumpPtrAllocator Allocator;804 StringSaver Saver(Allocator);805 MustacheContext Ctx(Allocator, Saver);806 Template T(" | {{#boolean}}\t|\t{{/boolean}} | \n", Ctx);807 std::string Out;808 raw_string_ostream OS(Out);809 T.render(D, OS);810 EXPECT_EQ(" | \t|\t | \n", Out);811}812 813TEST(MustacheSections, InternalWhitespace) {814 Value D = Object{{"boolean", true}};815 BumpPtrAllocator Allocator;816 StringSaver Saver(Allocator);817 MustacheContext Ctx(Allocator, Saver);818 Template T(" | {{#boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n",819 Ctx);820 std::string Out;821 raw_string_ostream OS(Out);822 T.render(D, OS);823 EXPECT_EQ(" | \n | \n", Out);824}825 826TEST(MustacheSections, IndentedInlineSections) {827 Value D = Object{{"boolean", true}};828 BumpPtrAllocator Allocator;829 StringSaver Saver(Allocator);830 MustacheContext Ctx(Allocator, Saver);831 Template T(" {{#boolean}}YES{{/boolean}}\n {{#boolean}}GOOD{{/boolean}}\n",832 Ctx);833 std::string Out;834 raw_string_ostream OS(Out);835 T.render(D, OS);836 EXPECT_EQ(" YES\n GOOD\n", Out);837}838 839TEST(MustacheSections, StandaloneLines) {840 Value D = Object{{"boolean", true}};841 BumpPtrAllocator Allocator;842 StringSaver Saver(Allocator);843 MustacheContext Ctx(Allocator, Saver);844 Template T("| This Is\n{{#boolean}}\n|\n{{/boolean}}\n| A Line\n", Ctx);845 std::string Out;846 raw_string_ostream OS(Out);847 T.render(D, OS);848 EXPECT_EQ("| This Is\n|\n| A Line\n", Out);849}850 851TEST(MustacheSections, IndentedStandaloneLines) {852 Value D = Object{{"boolean", true}};853 BumpPtrAllocator Allocator;854 StringSaver Saver(Allocator);855 MustacheContext Ctx(Allocator, Saver);856 Template T("| This Is\n {{#boolean}}\n|\n {{/boolean}}\n| A Line\n", Ctx);857 std::string Out;858 raw_string_ostream OS(Out);859 T.render(D, OS);860 EXPECT_EQ("| This Is\n|\n| A Line\n", Out);861}862 863TEST(MustacheSections, StandaloneLineEndings) {864 Value D = Object{{"boolean", true}};865 BumpPtrAllocator Allocator;866 StringSaver Saver(Allocator);867 MustacheContext Ctx(Allocator, Saver);868 Template T("|\r\n{{#boolean}}\r\n{{/boolean}}\r\n|", Ctx);869 std::string Out;870 raw_string_ostream OS(Out);871 T.render(D, OS);872 EXPECT_EQ("|\r\n|", Out);873}874 875TEST(MustacheSections, StandaloneWithoutPreviousLine) {876 Value D = Object{{"boolean", true}};877 BumpPtrAllocator Allocator;878 StringSaver Saver(Allocator);879 MustacheContext Ctx(Allocator, Saver);880 Template T(" {{#boolean}}\n#{{/boolean}}\n/", Ctx);881 std::string Out;882 raw_string_ostream OS(Out);883 T.render(D, OS);884 EXPECT_EQ("#\n/", Out);885}886 887TEST(MustacheSections, StandaloneWithoutNewline) {888 Value D = Object{{"boolean", true}};889 BumpPtrAllocator Allocator;890 StringSaver Saver(Allocator);891 MustacheContext Ctx(Allocator, Saver);892 Template T("#{{#boolean}}\n/\n {{/boolean}}", Ctx);893 std::string Out;894 raw_string_ostream OS(Out);895 T.render(D, OS);896 EXPECT_EQ("#\n/\n", Out);897}898 899TEST(MustacheSections, Padding) {900 Value D = Object{{"boolean", true}};901 BumpPtrAllocator Allocator;902 StringSaver Saver(Allocator);903 MustacheContext Ctx(Allocator, Saver);904 Template T("|{{# boolean }}={{/ boolean }}|", Ctx);905 std::string Out;906 raw_string_ostream OS(Out);907 T.render(D, OS);908 EXPECT_EQ("|=|", Out);909}910 911TEST(MustacheInvertedSections, Falsey) {912 Value D = Object{{"boolean", false}};913 BumpPtrAllocator Allocator;914 StringSaver Saver(Allocator);915 MustacheContext Ctx(Allocator, Saver);916 Template T("{{^boolean}}This should be rendered.{{/boolean}}", Ctx);917 std::string Out;918 raw_string_ostream OS(Out);919 T.render(D, OS);920 EXPECT_EQ("This should be rendered.", Out);921}922 923TEST(MustacheInvertedSections, Truthy) {924 Value D = Object{{"boolean", true}};925 BumpPtrAllocator Allocator;926 StringSaver Saver(Allocator);927 MustacheContext Ctx(Allocator, Saver);928 Template T("{{^boolean}}This should not be rendered.{{/boolean}}", Ctx);929 std::string Out;930 raw_string_ostream OS(Out);931 T.render(D, OS);932 EXPECT_EQ("", Out);933}934 935TEST(MustacheInvertedSections, NullIsFalsey) {936 Value D = Object{{"null", nullptr}};937 BumpPtrAllocator Allocator;938 StringSaver Saver(Allocator);939 MustacheContext Ctx(Allocator, Saver);940 Template T("{{^null}}This should be rendered.{{/null}}", Ctx);941 std::string Out;942 raw_string_ostream OS(Out);943 T.render(D, OS);944 EXPECT_EQ("This should be rendered.", Out);945}946 947TEST(MustacheInvertedSections, Context) {948 Value D = Object{{"context", Object{{"name", "Joe"}}}};949 BumpPtrAllocator Allocator;950 StringSaver Saver(Allocator);951 MustacheContext Ctx(Allocator, Saver);952 Template T("{{^context}}Hi {{name}}.{{/context}}", Ctx);953 std::string Out;954 raw_string_ostream OS(Out);955 T.render(D, OS);956 EXPECT_EQ("", Out);957}958 959TEST(MustacheInvertedSections, List) {960 Value D = Object{961 {"list", Array{Object{{"n", 1}}, Object{{"n", 2}}, Object{{"n", 3}}}}};962 BumpPtrAllocator Allocator;963 StringSaver Saver(Allocator);964 MustacheContext Ctx(Allocator, Saver);965 Template T("{{^list}}{{n}}{{/list}}", Ctx);966 std::string Out;967 raw_string_ostream OS(Out);968 T.render(D, OS);969 EXPECT_EQ("", Out);970}971 972TEST(MustacheInvertedSections, EmptyList) {973 Value D = Object{{"list", Array{}}};974 BumpPtrAllocator Allocator;975 StringSaver Saver(Allocator);976 MustacheContext Ctx(Allocator, Saver);977 Template T("{{^list}}Yay lists!{{/list}}", Ctx);978 std::string Out;979 raw_string_ostream OS(Out);980 T.render(D, OS);981 EXPECT_EQ("Yay lists!", Out);982}983 984TEST(MustacheInvertedSections, Doubled) {985 Value D = Object{{"bool", false}, {"two", "second"}};986 BumpPtrAllocator Allocator;987 StringSaver Saver(Allocator);988 MustacheContext Ctx(Allocator, Saver);989 Template T("{{^bool}}\n* first\n{{/bool}}\n* "990 "{{two}}\n{{^bool}}\n* third\n{{/bool}}\n",991 Ctx);992 std::string Out;993 raw_string_ostream OS(Out);994 T.render(D, OS);995 EXPECT_EQ("* first\n* second\n* third\n", Out);996}997 998TEST(MustacheInvertedSections, NestedFalsey) {999 Value D = Object{{"bool", false}};1000 BumpPtrAllocator Allocator;1001 StringSaver Saver(Allocator);1002 MustacheContext Ctx(Allocator, Saver);1003 Template T("| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |", Ctx);1004 std::string Out;1005 raw_string_ostream OS(Out);1006 T.render(D, OS);1007 EXPECT_EQ("| A B C D E |", Out);1008}1009 1010TEST(MustacheInvertedSections, NestedTruthy) {1011 Value D = Object{{"bool", true}};1012 BumpPtrAllocator Allocator;1013 StringSaver Saver(Allocator);1014 MustacheContext Ctx(Allocator, Saver);1015 Template T("| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |", Ctx);1016 std::string Out;1017 raw_string_ostream OS(Out);1018 T.render(D, OS);1019 EXPECT_EQ("| A E |", Out);1020}1021 1022TEST(MustacheInvertedSections, ContextMisses) {1023 Value D = Object{};1024 BumpPtrAllocator Allocator;1025 StringSaver Saver(Allocator);1026 MustacheContext Ctx(Allocator, Saver);1027 Template T("[{{^missing}}Cannot find key 'missing'!{{/missing}}]", Ctx);1028 std::string Out;1029 raw_string_ostream OS(Out);1030 T.render(D, OS);1031 EXPECT_EQ("[Cannot find key 'missing'!]", Out);1032}1033 1034TEST(MustacheInvertedSections, DottedNamesTruthy) {1035 Value D = Object{{"a", Object{{"b", Object{{"c", true}}}}}};1036 BumpPtrAllocator Allocator;1037 StringSaver Saver(Allocator);1038 MustacheContext Ctx(Allocator, Saver);1039 Template T("{{^a.b.c}}Not Here{{/a.b.c}} == ", Ctx);1040 std::string Out;1041 raw_string_ostream OS(Out);1042 T.render(D, OS);1043 EXPECT_EQ(" == ", Out);1044}1045 1046TEST(MustacheInvertedSections, DottedNamesFalsey) {1047 Value D = Object{{"a", Object{{"b", Object{{"c", false}}}}}};1048 BumpPtrAllocator Allocator;1049 StringSaver Saver(Allocator);1050 MustacheContext Ctx(Allocator, Saver);1051 Template T("{{^a.b.c}}Not Here{{/a.b.c}} == Not Here", Ctx);1052 std::string Out;1053 raw_string_ostream OS(Out);1054 T.render(D, OS);1055 EXPECT_EQ("Not Here == Not Here", Out);1056}1057 1058TEST(MustacheInvertedSections, DottedNamesBrokenChains) {1059 Value D = Object{{"a", Object{}}};1060 BumpPtrAllocator Allocator;1061 StringSaver Saver(Allocator);1062 MustacheContext Ctx(Allocator, Saver);1063 Template T("{{^a.b.c}}Not Here{{/a.b.c}} == Not Here", Ctx);1064 std::string Out;1065 raw_string_ostream OS(Out);1066 T.render(D, OS);1067 EXPECT_EQ("Not Here == Not Here", Out);1068}1069 1070TEST(MustacheInvertedSections, SurroundingWhitespace) {1071 Value D = Object{{"boolean", false}};1072 BumpPtrAllocator Allocator;1073 StringSaver Saver(Allocator);1074 MustacheContext Ctx(Allocator, Saver);1075 Template T(" | {{^boolean}}\t|\t{{/boolean}} | \n", Ctx);1076 std::string Out;1077 raw_string_ostream OS(Out);1078 T.render(D, OS);1079 EXPECT_EQ(" | \t|\t | \n", Out);1080}1081 1082TEST(MustacheInvertedSections, InternalWhitespace) {1083 Value D = Object{{"boolean", false}};1084 BumpPtrAllocator Allocator;1085 StringSaver Saver(Allocator);1086 MustacheContext Ctx(Allocator, Saver);1087 Template T(" | {{^boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n",1088 Ctx);1089 std::string Out;1090 raw_string_ostream OS(Out);1091 T.render(D, OS);1092 EXPECT_EQ(" | \n | \n", Out);1093}1094 1095TEST(MustacheInvertedSections, IndentedInlineSections) {1096 Value D = Object{{"boolean", false}};1097 BumpPtrAllocator Allocator;1098 StringSaver Saver(Allocator);1099 MustacheContext Ctx(Allocator, Saver);1100 Template T(" {{^boolean}}NO{{/boolean}}\n {{^boolean}}WAY{{/boolean}}\n",1101 Ctx);1102 std::string Out;1103 raw_string_ostream OS(Out);1104 T.render(D, OS);1105 EXPECT_EQ(" NO\n WAY\n", Out);1106}1107 1108TEST(MustacheInvertedSections, StandaloneLines) {1109 Value D = Object{{"boolean", false}};1110 BumpPtrAllocator Allocator;1111 StringSaver Saver(Allocator);1112 MustacheContext Ctx(Allocator, Saver);1113 Template T("| This Is\n{{^boolean}}\n|\n{{/boolean}}\n| A Line\n", Ctx);1114 std::string Out;1115 raw_string_ostream OS(Out);1116 T.render(D, OS);1117 EXPECT_EQ("| This Is\n|\n| A Line\n", Out);1118}1119 1120TEST(MustacheInvertedSections, StandaloneIndentedLines) {1121 Value D = Object{{"boolean", false}};1122 BumpPtrAllocator Allocator;1123 StringSaver Saver(Allocator);1124 MustacheContext Ctx(Allocator, Saver);1125 Template T("| This Is\n {{^boolean}}\n|\n {{/boolean}}\n| A Line\n", Ctx);1126 std::string Out;1127 raw_string_ostream OS(Out);1128 T.render(D, OS);1129 EXPECT_EQ("| This Is\n|\n| A Line\n", Out);1130}1131 1132TEST(MustacheInvertedSections, StandaloneLineEndings) {1133 Value D = Object{{"boolean", false}};1134 BumpPtrAllocator Allocator;1135 StringSaver Saver(Allocator);1136 MustacheContext Ctx(Allocator, Saver);1137 Template T("|\r\n{{^boolean}}\r\n{{/boolean}}\r\n|", Ctx);1138 std::string Out;1139 raw_string_ostream OS(Out);1140 T.render(D, OS);1141 EXPECT_EQ("|\r\n|", Out);1142}1143 1144TEST(MustacheInvertedSections, StandaloneWithoutPreviousLine) {1145 Value D = Object{{"boolean", false}};1146 BumpPtrAllocator Allocator;1147 StringSaver Saver(Allocator);1148 MustacheContext Ctx(Allocator, Saver);1149 Template T(" {{^boolean}}\n^{{/boolean}}\n/", Ctx);1150 std::string Out;1151 raw_string_ostream OS(Out);1152 T.render(D, OS);1153 EXPECT_EQ("^\n/", Out);1154}1155 1156TEST(MustacheInvertedSections, StandaloneWithoutNewline) {1157 Value D = Object{{"boolean", false}};1158 BumpPtrAllocator Allocator;1159 StringSaver Saver(Allocator);1160 MustacheContext Ctx(Allocator, Saver);1161 Template T("^{{^boolean}}\n/\n {{/boolean}}", Ctx);1162 std::string Out;1163 raw_string_ostream OS(Out);1164 T.render(D, OS);1165 EXPECT_EQ("^\n/\n", Out);1166}1167 1168TEST(MustacheInvertedSections, Padding) {1169 Value D = Object{{"boolean", false}};1170 BumpPtrAllocator Allocator;1171 StringSaver Saver(Allocator);1172 MustacheContext Ctx(Allocator, Saver);1173 Template T("|{{^ boolean }}={{/ boolean }}|", Ctx);1174 std::string Out;1175 raw_string_ostream OS(Out);1176 T.render(D, OS);1177 EXPECT_EQ("|=|", Out);1178}1179 1180TEST(MustachePartials, BasicBehavior) {1181 Value D = Object{};1182 BumpPtrAllocator Allocator;1183 StringSaver Saver(Allocator);1184 MustacheContext Ctx(Allocator, Saver);1185 Template T("{{>text}}", Ctx);1186 T.registerPartial("text", "from partial");1187 std::string Out;1188 raw_string_ostream OS(Out);1189 T.render(D, OS);1190 EXPECT_EQ("from partial", Out);1191}1192 1193TEST(MustachePartials, FailedLookup) {1194 Value D = Object{};1195 BumpPtrAllocator Allocator;1196 StringSaver Saver(Allocator);1197 MustacheContext Ctx(Allocator, Saver);1198 Template T("{{>text}}", Ctx);1199 std::string Out;1200 raw_string_ostream OS(Out);1201 T.render(D, OS);1202 EXPECT_EQ("", Out);1203}1204 1205TEST(MustachePartials, Context) {1206 Value D = Object{{"text", "content"}};1207 BumpPtrAllocator Allocator;1208 StringSaver Saver(Allocator);1209 MustacheContext Ctx(Allocator, Saver);1210 Template T("{{>partial}}", Ctx);1211 T.registerPartial("partial", "*{{text}}*");1212 std::string Out;1213 raw_string_ostream OS(Out);1214 T.render(D, OS);1215 EXPECT_EQ("*content*", Out);1216}1217 1218TEST(MustachePartials, Recursion) {1219 Value D =1220 Object{{"content", "X"},1221 {"nodes", Array{Object{{"content", "Y"}, {"nodes", Array{}}}}}};1222 BumpPtrAllocator Allocator;1223 StringSaver Saver(Allocator);1224 MustacheContext Ctx(Allocator, Saver);1225 Template T("{{>node}}", Ctx);1226 T.registerPartial("node", "{{content}}({{#nodes}}{{>node}}{{/nodes}})");1227 std::string Out;1228 raw_string_ostream OS(Out);1229 T.render(D, OS);1230 EXPECT_EQ("X(Y())", Out);1231}1232 1233TEST(MustachePartials, Nested) {1234 Value D = Object{{"a", "hello"}, {"b", "world"}};1235 BumpPtrAllocator Allocator;1236 StringSaver Saver(Allocator);1237 MustacheContext Ctx(Allocator, Saver);1238 Template T("{{>outer}}", Ctx);1239 T.registerPartial("outer", "*{{a}} {{>inner}}*");1240 T.registerPartial("inner", "{{b}}!");1241 std::string Out;1242 raw_string_ostream OS(Out);1243 T.render(D, OS);1244 EXPECT_EQ("*hello world!*", Out);1245}1246 1247TEST(MustachePartials, SurroundingWhitespace) {1248 Value D = Object{};1249 BumpPtrAllocator Allocator;1250 StringSaver Saver(Allocator);1251 MustacheContext Ctx(Allocator, Saver);1252 Template T("| {{>partial}} |", Ctx);1253 T.registerPartial("partial", "\t|\t");1254 std::string Out;1255 raw_string_ostream OS(Out);1256 T.render(D, OS);1257 EXPECT_EQ("| \t|\t |", Out);1258}1259 1260TEST(MustachePartials, InlineIndentation) {1261 Value D = Object{{"data", "|"}};1262 BumpPtrAllocator Allocator;1263 StringSaver Saver(Allocator);1264 MustacheContext Ctx(Allocator, Saver);1265 Template T(" {{data}} {{> partial}}\n", Ctx);1266 T.registerPartial("partial", "<\n<");1267 std::string Out;1268 raw_string_ostream OS(Out);1269 T.render(D, OS);1270 EXPECT_EQ(" | <\n<\n", Out);1271}1272 1273TEST(MustachePartials, PaddingWhitespace) {1274 Value D = Object{{"boolean", true}};1275 BumpPtrAllocator Allocator;1276 StringSaver Saver(Allocator);1277 MustacheContext Ctx(Allocator, Saver);1278 Template T("|{{> partial }}|", Ctx);1279 T.registerPartial("partial", "[]");1280 std::string Out;1281 raw_string_ostream OS(Out);1282 T.render(D, OS);1283 EXPECT_EQ("|[]|", Out);1284}1285 1286TEST(MustachePartials, StandaloneIndentation) {1287 BumpPtrAllocator Allocator;1288 StringSaver Saver(Allocator);1289 MustacheContext Ctx(Allocator, Saver);1290 mustache::Template T("\\\n {{>partial}}\n/\n", Ctx);1291 T.registerPartial("partial", "|\n{{{content}}}\n|\n");1292 std::string O;1293 raw_string_ostream OS(O);1294 Value DataContext = Object{{"content", "<\n->"}};1295 T.render(DataContext, OS);1296 EXPECT_EQ("\\\n |\n <\n->\n |\n/\n", OS.str());1297}1298 1299TEST(MustacheLambdas, BasicInterpolation) {1300 Value D = Object{};1301 BumpPtrAllocator Allocator;1302 StringSaver Saver(Allocator);1303 MustacheContext Ctx(Allocator, Saver);1304 Template T("Hello, {{lambda}}!", Ctx);1305 Lambda L = []() -> llvm::json::Value { return "World"; };1306 T.registerLambda("lambda", L);1307 std::string Out;1308 raw_string_ostream OS(Out);1309 T.render(D, OS);1310 EXPECT_EQ("Hello, World!", Out);1311}1312 1313TEST(MustacheLambdas, InterpolationExpansion) {1314 Value D = Object{{"planet", "World"}};1315 BumpPtrAllocator Allocator;1316 StringSaver Saver(Allocator);1317 MustacheContext Ctx(Allocator, Saver);1318 Template T("Hello, {{lambda}}!", Ctx);1319 Lambda L = []() -> llvm::json::Value { return "{{planet}}"; };1320 T.registerLambda("lambda", L);1321 std::string Out;1322 raw_string_ostream OS(Out);1323 T.render(D, OS);1324 EXPECT_EQ("Hello, World!", Out);1325}1326 1327TEST(MustacheLambdas, BasicMultipleCalls) {1328 Value D = Object{};1329 BumpPtrAllocator Allocator;1330 StringSaver Saver(Allocator);1331 MustacheContext Ctx(Allocator, Saver);1332 Template T("{{lambda}} == {{lambda}} == {{lambda}}", Ctx);1333 int I = 0;1334 Lambda L = [&I]() -> llvm::json::Value {1335 I += 1;1336 return I;1337 };1338 T.registerLambda("lambda", L);1339 std::string Out;1340 raw_string_ostream OS(Out);1341 T.render(D, OS);1342 EXPECT_EQ("1 == 2 == 3", Out);1343}1344 1345TEST(MustacheLambdas, Escaping) {1346 Value D = Object{};1347 BumpPtrAllocator Allocator;1348 StringSaver Saver(Allocator);1349 MustacheContext Ctx(Allocator, Saver);1350 Template T("<{{lambda}}{{&lambda}}", Ctx);1351 Lambda L = []() -> llvm::json::Value { return ">"; };1352 T.registerLambda("lambda", L);1353 std::string Out;1354 raw_string_ostream OS(Out);1355 T.render(D, OS);1356 EXPECT_EQ("<>>", Out);1357}1358 1359TEST(MustacheLambdas, Sections) {1360 Value D = Object{};1361 BumpPtrAllocator Allocator;1362 StringSaver Saver(Allocator);1363 MustacheContext Ctx(Allocator, Saver);1364 Template T("<{{#lambda}}{{x}}{{/lambda}}>", Ctx);1365 SectionLambda L = [](StringRef Text) -> llvm::json::Value {1366 if (Text == "{{x}}") {1367 return "yes";1368 }1369 return "no";1370 };1371 T.registerLambda("lambda", L);1372 std::string Out;1373 raw_string_ostream OS(Out);1374 T.render(D, OS);1375 EXPECT_EQ("<yes>", Out);1376}1377 1378TEST(MustacheLambdas, SectionExpansion) {1379 Value D = Object{1380 {"planet", "Earth"},1381 };1382 BumpPtrAllocator Allocator;1383 StringSaver Saver(Allocator);1384 MustacheContext Ctx(Allocator, Saver);1385 Template T("<{{#lambda}}-{{/lambda}}>", Ctx);1386 SectionLambda L = [](StringRef Text) -> llvm::json::Value {1387 SmallString<128> Result;1388 Result += Text;1389 Result += "{{planet}}";1390 Result += Text;1391 return Result;1392 };1393 T.registerLambda("lambda", L);1394 std::string Out;1395 raw_string_ostream OS(Out);1396 T.render(D, OS);1397 EXPECT_EQ("<-Earth->", Out);1398}1399 1400TEST(MustacheLambdas, SectionsMultipleCalls) {1401 Value D = Object{};1402 BumpPtrAllocator Allocator;1403 StringSaver Saver(Allocator);1404 MustacheContext Ctx(Allocator, Saver);1405 Template T("{{#lambda}}FILE{{/lambda}} != {{#lambda}}LINE{{/lambda}}", Ctx);1406 SectionLambda L = [](StringRef Text) -> llvm::json::Value {1407 SmallString<128> Result;1408 Result += "__";1409 Result += Text;1410 Result += "__";1411 return Result;1412 };1413 T.registerLambda("lambda", L);1414 std::string Out;1415 raw_string_ostream OS(Out);1416 T.render(D, OS);1417 EXPECT_EQ("__FILE__ != __LINE__", Out);1418}1419 1420TEST(MustacheLambdas, InvertedSections) {1421 Value D = Object{{"static", "static"}};1422 BumpPtrAllocator Allocator;1423 StringSaver Saver(Allocator);1424 MustacheContext Ctx(Allocator, Saver);1425 Template T("<{{^lambda}}{{static}}{{/lambda}}>", Ctx);1426 SectionLambda L = [](StringRef Text) -> llvm::json::Value { return false; };1427 T.registerLambda("lambda", L);1428 std::string Out;1429 raw_string_ostream OS(Out);1430 T.render(D, OS);1431 EXPECT_EQ("<>", Out);1432}1433 1434TEST(MustacheComments, Inline) {1435 // Comment blocks should be removed from the template.1436 Value D = {};1437 BumpPtrAllocator Allocator;1438 StringSaver Saver(Allocator);1439 MustacheContext Ctx(Allocator, Saver);1440 Template T("12345{{! Comment Block! }}67890", Ctx);1441 std::string Out;1442 raw_string_ostream OS(Out);1443 T.render(D, OS);1444 EXPECT_EQ("1234567890", Out);1445}1446 1447TEST(MustacheComments, Multiline) {1448 // Multiline comments should be permitted.1449 Value D = {};1450 BumpPtrAllocator Allocator;1451 StringSaver Saver(Allocator);1452 MustacheContext Ctx(Allocator, Saver);1453 Template T("12345{{!\n This is a\n multi-line comment...\n}}67890\n", Ctx);1454 std::string Out;1455 raw_string_ostream OS(Out);1456 T.render(D, OS);1457 EXPECT_EQ("1234567890\n", Out);1458}1459 1460TEST(MustacheComments, Standalone) {1461 // All standalone comment lines should be removed.1462 Value D = {};1463 BumpPtrAllocator Allocator;1464 StringSaver Saver(Allocator);1465 MustacheContext Ctx(Allocator, Saver);1466 Template T("Begin.\n{{! Comment Block! }}\nEnd.\n", Ctx);1467 std::string Out;1468 raw_string_ostream OS(Out);1469 T.render(D, OS);1470 EXPECT_EQ("Begin.\nEnd.\n", Out);1471}1472 1473TEST(MustacheComments, IndentedStandalone) {1474 // All standalone comment lines should be removed.1475 Value D = {};1476 BumpPtrAllocator Allocator;1477 StringSaver Saver(Allocator);1478 MustacheContext Ctx(Allocator, Saver);1479 Template T("Begin.\n {{! Indented Comment Block! }}\nEnd.\n", Ctx);1480 std::string Out;1481 raw_string_ostream OS(Out);1482 T.render(D, OS);1483 EXPECT_EQ("Begin.\nEnd.\n", Out);1484}1485 1486TEST(MustacheComments, StandaloneLineEndings) {1487 // "\r\n" should be considered a newline for standalone tags.1488 Value D = {};1489 BumpPtrAllocator Allocator;1490 StringSaver Saver(Allocator);1491 MustacheContext Ctx(Allocator, Saver);1492 Template T("|\r\n{{! Standalone Comment }}\r\n|", Ctx);1493 std::string Out;1494 raw_string_ostream OS(Out);1495 T.render(D, OS);1496 EXPECT_EQ("|\r\n|", Out);1497}1498 1499TEST(MustacheComments, StandaloneWithoutPreviousLine) {1500 // Standalone tags should not require a newline to precede them.1501 Value D = {};1502 BumpPtrAllocator Allocator;1503 StringSaver Saver(Allocator);1504 MustacheContext Ctx(Allocator, Saver);1505 Template T(" {{! I'm Still Standalone }}\n!", Ctx);1506 std::string Out;1507 raw_string_ostream OS(Out);1508 T.render(D, OS);1509 EXPECT_EQ("!", Out);1510}1511 1512TEST(MustacheComments, StandaloneWithoutNewline) {1513 // Standalone tags should not require a newline to follow them.1514 Value D = {};1515 BumpPtrAllocator Allocator;1516 StringSaver Saver(Allocator);1517 MustacheContext Ctx(Allocator, Saver);1518 Template T("!\n {{! I'm Still Standalone }}", Ctx);1519 std::string Out;1520 raw_string_ostream OS(Out);1521 T.render(D, OS);1522 EXPECT_EQ("!\n", Out);1523}1524 1525TEST(MustacheComments, MultilineStandalone) {1526 // All standalone comment lines should be removed.1527 Value D = {};1528 BumpPtrAllocator Allocator;1529 StringSaver Saver(Allocator);1530 MustacheContext Ctx(Allocator, Saver);1531 Template T("Begin.\n{{!\nSomething's going on here...\n}}\nEnd.\n", Ctx);1532 std::string Out;1533 raw_string_ostream OS(Out);1534 T.render(D, OS);1535 EXPECT_EQ("Begin.\nEnd.\n", Out);1536}1537 1538TEST(MustacheComments, IndentedMultilineStandalone) {1539 // All standalone comment lines should be removed.1540 Value D = {};1541 BumpPtrAllocator Allocator;1542 StringSaver Saver(Allocator);1543 MustacheContext Ctx(Allocator, Saver);1544 Template T("Begin.\n {{!\n Something's going on here...\n }}\nEnd.\n",1545 Ctx);1546 std::string Out;1547 raw_string_ostream OS(Out);1548 T.render(D, OS);1549 EXPECT_EQ("Begin.\nEnd.\n", Out);1550}1551 1552TEST(MustacheComments, IndentedInline) {1553 // Inline comments should not strip whitespace.1554 Value D = {};1555 BumpPtrAllocator Allocator;1556 StringSaver Saver(Allocator);1557 MustacheContext Ctx(Allocator, Saver);1558 Template T(" 12 {{! 34 }}\n", Ctx);1559 std::string Out;1560 raw_string_ostream OS(Out);1561 T.render(D, OS);1562 EXPECT_EQ(" 12 \n", Out);1563}1564 1565TEST(MustacheComments, SurroundingWhitespace) {1566 // Comment removal should preserve surrounding whitespace.1567 Value D = {};1568 BumpPtrAllocator Allocator;1569 StringSaver Saver(Allocator);1570 MustacheContext Ctx(Allocator, Saver);1571 Template T("12345 {{! Comment Block! }} 67890", Ctx);1572 std::string Out;1573 raw_string_ostream OS(Out);1574 T.render(D, OS);1575 EXPECT_EQ("12345 67890", Out);1576}1577 1578TEST(MustacheComments, VariableNameCollision) {1579 // Comments must never render, even if a variable with the same name exists.1580 Value D = Object{1581 {"! comment", 1}, {"! comment ", 2}, {"!comment", 3}, {"comment", 4}};1582 BumpPtrAllocator Allocator;1583 StringSaver Saver(Allocator);1584 MustacheContext Ctx(Allocator, Saver);1585 Template T("comments never show: >{{! comment }}<", Ctx);1586 std::string Out;1587 raw_string_ostream OS(Out);1588 T.render(D, OS);1589 EXPECT_EQ("comments never show: ><", Out);1590}1591 1592// XFAIL: The following tests for the Triple Mustache feature are expected to1593// fail. The assertions have been inverted from EXPECT_EQ to EXPECT_NE to allow1594// them to pass against the current implementation. Once Triple Mustache is1595// implemented, these assertions should be changed back to EXPECT_EQ.1596TEST(MustacheTripleMustache, Basic) {1597 Value D = Object{{"subject", "<b>World</b>"}};1598 BumpPtrAllocator Allocator;1599 StringSaver Saver(Allocator);1600 MustacheContext Ctx(Allocator, Saver);1601 Template T("Hello, {{{subject}}}!", Ctx);1602 std::string Out;1603 raw_string_ostream OS(Out);1604 T.render(D, OS);1605 EXPECT_EQ("Hello, <b>World</b>!", Out);1606}1607 1608TEST(MustacheTripleMustache, IntegerInterpolation) {1609 Value D = Object{{"mph", 85}};1610 BumpPtrAllocator Allocator;1611 StringSaver Saver(Allocator);1612 MustacheContext Ctx(Allocator, Saver);1613 Template T("{{{mph}}} miles an hour!", Ctx);1614 std::string Out;1615 raw_string_ostream OS(Out);1616 T.render(D, OS);1617 EXPECT_EQ("85 miles an hour!", Out);1618}1619 1620TEST(MustacheTripleMustache, DecimalInterpolation) {1621 Value D = Object{{"power", 1.21}};1622 BumpPtrAllocator Allocator;1623 StringSaver Saver(Allocator);1624 MustacheContext Ctx(Allocator, Saver);1625 Template T("{{{power}}} jiggawatts!", Ctx);1626 std::string Out;1627 raw_string_ostream OS(Out);1628 T.render(D, OS);1629 EXPECT_EQ("1.21 jiggawatts!", Out);1630}1631 1632TEST(MustacheTripleMustache, NullInterpolation) {1633 Value D = Object{{"cannot", nullptr}};1634 BumpPtrAllocator Allocator;1635 StringSaver Saver(Allocator);1636 MustacheContext Ctx(Allocator, Saver);1637 Template T("I ({{{cannot}}}) be seen!", Ctx);1638 std::string Out;1639 raw_string_ostream OS(Out);1640 T.render(D, OS);1641 EXPECT_EQ("I () be seen!", Out);1642}1643 1644TEST(MustacheTripleMustache, ContextMissInterpolation) {1645 Value D = Object{};1646 BumpPtrAllocator Allocator;1647 StringSaver Saver(Allocator);1648 MustacheContext Ctx(Allocator, Saver);1649 Template T("I ({{{cannot}}}) be seen!", Ctx);1650 std::string Out;1651 raw_string_ostream OS(Out);1652 T.render(D, OS);1653 EXPECT_EQ("I () be seen!", Out);1654}1655 1656TEST(MustacheTripleMustache, DottedNames) {1657 Value D = Object{{"person", Object{{"name", "<b>Joe</b>"}}}};1658 BumpPtrAllocator Allocator;1659 StringSaver Saver(Allocator);1660 MustacheContext Ctx(Allocator, Saver);1661 Template T("{{{person.name}}}", Ctx);1662 std::string Out;1663 raw_string_ostream OS(Out);1664 T.render(D, OS);1665 EXPECT_EQ("<b>Joe</b>", Out);1666}1667 1668TEST(MustacheTripleMustache, ImplicitIterator) {1669 Value D = Object{{"list", Array{"<a>", "<b>"}}};1670 BumpPtrAllocator Allocator;1671 StringSaver Saver(Allocator);1672 MustacheContext Ctx(Allocator, Saver);1673 Template T("{{#list}}({{{.}}}){{/list}}", Ctx);1674 std::string Out;1675 raw_string_ostream OS(Out);1676 T.render(D, OS);1677 EXPECT_EQ("(<a>)(<b>)", Out);1678}1679 1680TEST(MustacheTripleMustache, SurroundingWhitespace) {1681 Value D = Object{{"string", "---"}};1682 BumpPtrAllocator Allocator;1683 StringSaver Saver(Allocator);1684 MustacheContext Ctx(Allocator, Saver);1685 Template T("| {{{string}}} |", Ctx);1686 std::string Out;1687 raw_string_ostream OS(Out);1688 T.render(D, OS);1689 EXPECT_EQ("| --- |", Out);1690}1691 1692TEST(MustacheTripleMustache, Standalone) {1693 Value D = Object{{"string", "---"}};1694 BumpPtrAllocator Allocator;1695 StringSaver Saver(Allocator);1696 MustacheContext Ctx(Allocator, Saver);1697 Template T(" {{{string}}}\n", Ctx);1698 std::string Out;1699 raw_string_ostream OS(Out);1700 T.render(D, OS);1701 EXPECT_EQ(" ---\n", Out);1702}1703 1704TEST(MustacheTripleMustache, WithPadding) {1705 Value D = Object{{"string", "---"}};1706 BumpPtrAllocator Allocator;1707 StringSaver Saver(Allocator);1708 MustacheContext Ctx(Allocator, Saver);1709 Template T("|{{{ string }}}|", Ctx);1710 std::string Out;1711 raw_string_ostream OS(Out);1712 T.render(D, OS);1713 EXPECT_EQ("|---|", Out);1714}1715 1716TEST(MustacheDelimiters, PairBehavior) {1717 Value D = Object{{"text", "Hey!"}};1718 BumpPtrAllocator Allocator;1719 StringSaver Saver(Allocator);1720 MustacheContext Ctx(Allocator, Saver);1721 Template T("{{=<% %>=}}(<%text%>)", Ctx);1722 std::string Out;1723 raw_string_ostream OS(Out);1724 T.render(D, OS);1725 EXPECT_EQ("(Hey!)", Out);1726}1727 1728TEST(MustacheDelimiters, SpecialCharacters) {1729 Value D = Object{{"text", "It worked!"}};1730 BumpPtrAllocator Allocator;1731 StringSaver Saver(Allocator);1732 MustacheContext Ctx(Allocator, Saver);1733 Template T("({{=[ ]=}}[text])", Ctx);1734 std::string Out;1735 raw_string_ostream OS(Out);1736 T.render(D, OS);1737 EXPECT_EQ("(It worked!)", Out);1738}1739 1740TEST(MustacheDelimiters, Sections) {1741 Value D = Object{{"section", true}, {"data", "I got interpolated."}};1742 BumpPtrAllocator Allocator;1743 StringSaver Saver(Allocator);1744 MustacheContext Ctx(Allocator, Saver);1745 Template T("[\n{{#section}}\n {{data}}\n |data|\n{{/section}}\n\n{{= "1746 "| | =}}\n|#section|\n {{data}}\n |data|\n|/section|\n]\n",1747 Ctx);1748 std::string Out;1749 raw_string_ostream OS(Out);1750 T.render(D, OS);1751 EXPECT_EQ("[\n I got interpolated.\n |data|\n\n {{data}}\n I got "1752 "interpolated.\n]\n",1753 Out);1754}1755 1756TEST(MustacheDelimiters, InvertedSections) {1757 Value D = Object{{"section", false}, {"data", "I got interpolated."}};1758 BumpPtrAllocator Allocator;1759 StringSaver Saver(Allocator);1760 MustacheContext Ctx(Allocator, Saver);1761 Template T("[\n{{^section}}\n {{data}}\n |data|\n{{/section}}\n\n{{= "1762 "| | =}}\n|^section|\n {{data}}\n |data|\n|/section|\n]\n",1763 Ctx);1764 std::string Out;1765 raw_string_ostream OS(Out);1766 T.render(D, OS);1767 EXPECT_EQ("[\n I got interpolated.\n |data|\n\n {{data}}\n I got "1768 "interpolated.\n]\n",1769 Out);1770}1771 1772TEST(MustacheDelimiters, PartialInheritence) {1773 Value D = Object{{"value", "yes"}};1774 BumpPtrAllocator Allocator;1775 StringSaver Saver(Allocator);1776 MustacheContext Ctx(Allocator, Saver);1777 Template T("[ {{>include}} ]\n{{= | | =}}\n[ |>include| ]\n", Ctx);1778 T.registerPartial("include", ".{{value}}.");1779 std::string Out;1780 raw_string_ostream OS(Out);1781 T.render(D, OS);1782 EXPECT_EQ("[ .yes. ]\n[ .yes. ]\n", Out);1783}1784 1785TEST(MustacheDelimiters, PostPartialBehavior) {1786 Value D = Object{{"value", "yes"}};1787 BumpPtrAllocator Allocator;1788 StringSaver Saver(Allocator);1789 MustacheContext Ctx(Allocator, Saver);1790 Template T("[ {{>include}} ]\n[ .{{value}}. .|value|. ]\n", Ctx);1791 T.registerPartial("include", ".{{value}}. {{= | | =}} .|value|.");1792 std::string Out;1793 raw_string_ostream OS(Out);1794 T.render(D, OS);1795 EXPECT_EQ("[ .yes. .yes. ]\n[ .yes. .|value|. ]\n", Out);1796}1797 1798TEST(MustacheDelimiters, SurroundingWhitespace) {1799 Value D = Object{};1800 BumpPtrAllocator Allocator;1801 StringSaver Saver(Allocator);1802 MustacheContext Ctx(Allocator, Saver);1803 Template T("| {{=@ @=}} |", Ctx);1804 std::string Out;1805 raw_string_ostream OS(Out);1806 T.render(D, OS);1807 EXPECT_EQ("| |", Out);1808}1809 1810TEST(MustacheDelimiters, OutlyingWhitespaceInline) {1811 Value D = Object{};1812 BumpPtrAllocator Allocator;1813 StringSaver Saver(Allocator);1814 MustacheContext Ctx(Allocator, Saver);1815 Template T(" | {{=@ @=}}\n", Ctx);1816 std::string Out;1817 raw_string_ostream OS(Out);1818 T.render(D, OS);1819 EXPECT_EQ(" | \n", Out);1820}1821 1822TEST(MustacheDelimiters, StandaloneTag) {1823 Value D = Object{};1824 BumpPtrAllocator Allocator;1825 StringSaver Saver(Allocator);1826 MustacheContext Ctx(Allocator, Saver);1827 Template T("Begin.\n{{=@ @=}}\nEnd.\n", Ctx);1828 std::string Out;1829 raw_string_ostream OS(Out);1830 T.render(D, OS);1831 EXPECT_EQ("Begin.\nEnd.\n", Out);1832}1833 1834TEST(MustacheDelimiters, IndentedStandaloneTag) {1835 Value D = Object{};1836 BumpPtrAllocator Allocator;1837 StringSaver Saver(Allocator);1838 MustacheContext Ctx(Allocator, Saver);1839 Template T("Begin.\n {{=@ @=}}\nEnd.\n", Ctx);1840 std::string Out;1841 raw_string_ostream OS(Out);1842 T.render(D, OS);1843 EXPECT_EQ("Begin.\nEnd.\n", Out);1844}1845 1846TEST(MustacheDelimiters, StandaloneLineEndings) {1847 Value D = Object{};1848 BumpPtrAllocator Allocator;1849 StringSaver Saver(Allocator);1850 MustacheContext Ctx(Allocator, Saver);1851 Template T("|\r\n{{= @ @ =}}\r\n|", Ctx);1852 std::string Out;1853 raw_string_ostream OS(Out);1854 T.render(D, OS);1855 EXPECT_EQ("|\r\n|", Out);1856}1857 1858TEST(MustacheDelimiters, StandaloneWithoutPreviousLine) {1859 Value D = Object{};1860 BumpPtrAllocator Allocator;1861 StringSaver Saver(Allocator);1862 MustacheContext Ctx(Allocator, Saver);1863 Template T(" {{=@ @=}}\n=", Ctx);1864 std::string Out;1865 raw_string_ostream OS(Out);1866 T.render(D, OS);1867 EXPECT_EQ("=", Out);1868}1869 1870TEST(MustacheDelimiters, StandaloneWithoutNewline) {1871 Value D = Object{};1872 BumpPtrAllocator Allocator;1873 StringSaver Saver(Allocator);1874 MustacheContext Ctx(Allocator, Saver);1875 Template T("=\n {{=@ @=}}", Ctx);1876 std::string Out;1877 raw_string_ostream OS(Out);1878 T.render(D, OS);1879 EXPECT_EQ("=\n", Out);1880}1881 1882TEST(MustacheDelimiters, PairwithPadding) {1883 Value D = Object{};1884 BumpPtrAllocator Allocator;1885 StringSaver Saver(Allocator);1886 MustacheContext Ctx(Allocator, Saver);1887 Template T("|{{= @ @ =}}|", Ctx);1888 std::string Out;1889 raw_string_ostream OS(Out);1890 T.render(D, OS);1891 EXPECT_EQ("||", Out);1892}1893