We make the Mustache ASTNodes usable in the arena by first removing all of the memory owning data structures, like std::vector, std::unique_ptr, and SmallVector. We use standard LLVM list types to hold this data instead, and make use of a UniqueStringSaver to hold the various templates strings. Additionally, update clang-doc APIs to use the new interfaces. Future work can make better use of Twine interfaces to help avoid any intermediate copies or allocations.
1893 lines
56 KiB
C++
1893 lines
56 KiB
C++
//===- llvm/unittest/Support/MustacheTest.cpp ----------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Test conforming to Mustache 1.4.2 spec found here:
|
|
// https://github.com/mustache/spec
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Support/Mustache.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::mustache;
|
|
using namespace llvm::json;
|
|
|
|
TEST(MustacheInterpolation, NoInterpolation) {
|
|
// Mustache-free templates should render as-is.
|
|
Value D = {};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("Hello from {Mustache}!\n", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("Hello from {Mustache}!\n", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, BasicInterpolation) {
|
|
// Unadorned tags should interpolate content into the template.
|
|
Value D = Object{{"subject", "World"}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("Hello, {{subject}}!", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("Hello, World!", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, NoReinterpolation) {
|
|
// Interpolated tag output should not be re-interpolated.
|
|
Value D = Object{{"template", "{{planet}}"}, {"planet", "Earth"}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{template}}: {{planet}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("{{planet}}: Earth", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, HTMLEscaping) {
|
|
// Interpolated tag output should not be re-interpolated.
|
|
Value D = Object{
|
|
{"forbidden", "& \" < >"},
|
|
};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("These characters should be HTML escaped: {{forbidden}}\n", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("These characters should be HTML escaped: & " < >\n",
|
|
Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, Ampersand) {
|
|
// Interpolated tag output should not be re-interpolated.
|
|
Value D = Object{
|
|
{"forbidden", "& \" < >"},
|
|
};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("These characters should not be HTML escaped: {{&forbidden}}\n",
|
|
Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("These characters should not be HTML escaped: & \" < >\n", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, BasicIntegerInterpolation) {
|
|
// Integers should interpolate seamlessly.
|
|
Value D = Object{{"mph", 85}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{mph}} miles an hour!", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("85 miles an hour!", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, AmpersandIntegerInterpolation) {
|
|
// Integers should interpolate seamlessly.
|
|
Value D = Object{{"mph", 85}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{&mph}} miles an hour!", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("85 miles an hour!", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, BasicDecimalInterpolation) {
|
|
// Decimals should interpolate seamlessly with proper significance.
|
|
Value D = Object{{"power", 1.21}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{power}} jiggawatts!", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("1.21 jiggawatts!", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, BasicNullInterpolation) {
|
|
// Nulls should interpolate as the empty string.
|
|
Value D = Object{{"cannot", nullptr}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("I ({{cannot}}) be seen!", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("I () be seen!", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, AmpersandNullInterpolation) {
|
|
// Nulls should interpolate as the empty string.
|
|
Value D = Object{{"cannot", nullptr}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("I ({{&cannot}}) be seen!", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("I () be seen!", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, BasicContextMissInterpolation) {
|
|
// Failed context lookups should default to empty strings.
|
|
Value D = Object{};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("I ({{cannot}}) be seen!", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("I () be seen!", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, DottedNamesBasicInterpolation) {
|
|
// Dotted names should be considered a form of shorthand for sections.
|
|
Value D = Object{{"person", Object{{"name", "Joe"}}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{person.name}} == {{#person}}{{name}}{{/person}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("Joe == Joe", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, DottedNamesAmpersandInterpolation) {
|
|
// Dotted names should be considered a form of shorthand for sections.
|
|
Value D = Object{{"person", Object{{"name", "Joe"}}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{&person.name}} == {{#person}}{{&name}}{{/person}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("Joe == Joe", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, DottedNamesArbitraryDepth) {
|
|
// Dotted names should be functional to any level of nesting.
|
|
Value D = Object{
|
|
{"a",
|
|
Object{{"b",
|
|
Object{{"c",
|
|
Object{{"d",
|
|
Object{{"e", Object{{"name", "Phil"}}}}}}}}}}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{a.b.c.d.e.name}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("Phil", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, DottedNamesBrokenChains) {
|
|
// Any falsey value prior to the last part of the name should yield ''.
|
|
Value D = Object{{"a", Object{}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{a.b.c}} == ", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ(" == ", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, DottedNamesBrokenChainResolution) {
|
|
// Each part of a dotted name should resolve only against its parent.
|
|
Value D =
|
|
Object{{"a", Object{{"b", Object{}}}}, {"c", Object{{"name", "Jim"}}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{a.b.c.name}} == ", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ(" == ", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, DottedNamesInitialResolution) {
|
|
// The first part of a dotted name should resolve as any other name.
|
|
Value D = Object{
|
|
{"a",
|
|
Object{
|
|
{"b",
|
|
Object{{"c",
|
|
Object{{"d", Object{{"e", Object{{"name", "Phil"}}}}}}}}}}},
|
|
{"b",
|
|
Object{{"c", Object{{"d", Object{{"e", Object{{"name", "Wrong"}}}}}}}}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{#a}}{{b.c.d.e.name}}{{/a}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("Phil", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, DottedNamesContextPrecedence) {
|
|
// Dotted names should be resolved against former resolutions.
|
|
Value D =
|
|
Object{{"a", Object{{"b", Object{}}}}, {"b", Object{{"c", "ERROR"}}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{#a}}{{b.c}}{{/a}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, DottedNamesAreNotSingleKeys) {
|
|
// Dotted names shall not be parsed as single, atomic keys
|
|
Value D = Object{{"a.b", "c"}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{a.b}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, DottedNamesNoMasking) {
|
|
// Dotted Names in a given context are unavailable due to dot splitting
|
|
Value D = Object{{"a.b", "c"}, {"a", Object{{"b", "d"}}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{a.b}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("d", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, ImplicitIteratorsBasicInterpolation) {
|
|
// Unadorned tags should interpolate content into the template.
|
|
Value D = "world";
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("Hello, {{.}}!\n", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("Hello, world!\n", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, ImplicitIteratorsAmersand) {
|
|
// Basic interpolation should be HTML escaped.
|
|
Value D = "& \" < >";
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("These characters should not be HTML escaped: {{&.}}\n", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("These characters should not be HTML escaped: & \" < >\n", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, ImplicitIteratorsInteger) {
|
|
// Integers should interpolate seamlessly.
|
|
Value D = 85;
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{.}} miles an hour!\n", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("85 miles an hour!\n", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, InterpolationSurroundingWhitespace) {
|
|
// Interpolation should not alter surrounding whitespace.
|
|
Value D = Object{{"string", "---"}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("| {{string}} |", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("| --- |", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, AmersandSurroundingWhitespace) {
|
|
// Interpolation should not alter surrounding whitespace.
|
|
Value D = Object{{"string", "---"}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("| {{&string}} |", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("| --- |", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, StandaloneInterpolationWithWhitespace) {
|
|
// Standalone interpolation should not alter surrounding whitespace.
|
|
Value D = Object{{"string", "---"}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T(" {{string}}\n", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ(" ---\n", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, StandaloneAmpersandWithWhitespace) {
|
|
// Standalone interpolation should not alter surrounding whitespace.
|
|
Value D = Object{{"string", "---"}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T(" {{&string}}\n", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ(" ---\n", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, InterpolationWithPadding) {
|
|
// Superfluous in-tag whitespace should be ignored.
|
|
Value D = Object{{"string", "---"}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("|{{ string }}|", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("|---|", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, AmpersandWithPadding) {
|
|
// Superfluous in-tag whitespace should be ignored.
|
|
Value D = Object{{"string", "---"}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("|{{& string }}|", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("|---|", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, InterpolationWithPaddingAndNewlines) {
|
|
// Superfluous in-tag whitespace should be ignored.
|
|
Value D = Object{{"string", "---"}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("|{{ string \n\n\n }}|", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("|---|", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, Truthy) {
|
|
Value D = Object{{"boolean", true}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{#boolean}}This should be rendered.{{/boolean}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("This should be rendered.", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, Falsey) {
|
|
Value D = Object{{"boolean", false}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{#boolean}}This should not be rendered.{{/boolean}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, IsFalseyNull) {
|
|
// Mustache-free templates should render as-is.
|
|
Value D = Object{{"boolean", nullptr}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("Hello, {{#boolean}}World{{/boolean}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("Hello, ", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, IsFalseyArray) {
|
|
// Mustache-free templates should render as-is.
|
|
Value D = Object{{"boolean", Array()}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("Hello, {{#boolean}}World{{/boolean}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("Hello, ", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, IsFalseyObject) {
|
|
// Mustache-free templates should render as-is.
|
|
Value D = Object{{"boolean", Object{}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("Hello, {{#boolean}}World{{/boolean}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("Hello, World", Out);
|
|
}
|
|
|
|
TEST(MustacheInterpolation, DoubleRendering) {
|
|
// Mustache-free templates should render as-is.
|
|
Value D1 = Object{{"subject", "World"}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("Hello, {{subject}}!", Ctx);
|
|
std::string Out1;
|
|
raw_string_ostream OS1(Out1);
|
|
T.render(D1, OS1);
|
|
EXPECT_EQ("Hello, World!", Out1);
|
|
std::string Out2;
|
|
raw_string_ostream OS2(Out2);
|
|
Value D2 = Object{{"subject", "New World"}};
|
|
T.render(D2, OS2);
|
|
EXPECT_EQ("Hello, New World!", Out2);
|
|
}
|
|
|
|
TEST(MustacheSections, NullIsFalsey) {
|
|
Value D = Object{{"null", nullptr}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{#null}}This should not be rendered.{{/null}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, Context) {
|
|
Value D = Object{{"context", Object{{"name", "Joe"}}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{#context}}Hi {{name}}.{{/context}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("Hi Joe.", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, ParentContexts) {
|
|
Value D = Object{{"a", "foo"},
|
|
{"b", "wrong"},
|
|
{"sec", Object{{"b", "bar"}}},
|
|
{"c", Object{{"d", "baz"}}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{#sec}}{{a}}, {{b}}, {{c.d}}{{/sec}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("foo, bar, baz", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, VariableTest) {
|
|
Value D = Object{{"foo", "bar"}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{#foo}}{{.}} is {{foo}}{{/foo}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("bar is bar", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, ListContexts) {
|
|
Value D = Object{
|
|
{"tops",
|
|
Array{Object{
|
|
{"tname", Object{{"upper", "A"}, {"lower", "a"}}},
|
|
{"middles",
|
|
Array{Object{{"mname", "1"},
|
|
{"bottoms", Array{Object{{"bname", "x"}},
|
|
Object{{"bname", "y"}}}}}}}}}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{#tops}}"
|
|
"{{#middles}}"
|
|
"{{tname.lower}}{{mname}}."
|
|
"{{#bottoms}}"
|
|
"{{tname.upper}}{{mname}}{{bname}}."
|
|
"{{/bottoms}}"
|
|
"{{/middles}}"
|
|
"{{/tops}}",
|
|
Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("a1.A1x.A1y.", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, DeeplyNestedContexts) {
|
|
Value D = Object{
|
|
{"a", Object{{"one", 1}}},
|
|
{"b", Object{{"two", 2}}},
|
|
{"c", Object{{"three", 3}, {"d", Object{{"four", 4}, {"five", 5}}}}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T(
|
|
"{{#a}}\n{{one}}\n{{#b}}\n{{one}}{{two}}{{one}}\n{{#c}}\n{{one}}{{two}}{{"
|
|
"three}}{{two}}{{one}}\n{{#d}}\n{{one}}{{two}}{{three}}{{four}}{{three}}{"
|
|
"{two}}{{one}}\n{{#five}}\n{{one}}{{two}}{{three}}{{four}}{{five}}{{four}"
|
|
"}{{three}}{{two}}{{one}}\n{{one}}{{two}}{{three}}{{four}}{{.}}6{{.}}{{"
|
|
"four}}{{three}}{{two}}{{one}}\n{{one}}{{two}}{{three}}{{four}}{{five}}{{"
|
|
"four}}{{three}}{{two}}{{one}}\n{{/"
|
|
"five}}\n{{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}}\n{{/"
|
|
"d}}\n{{one}}{{two}}{{three}}{{two}}{{one}}\n{{/"
|
|
"c}}\n{{one}}{{two}}{{one}}\n{{/b}}\n{{one}}\n{{/a}}\n",
|
|
Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("1\n121\n12321\n1234321\n123454321\n12345654321\n123454321\n1234321"
|
|
"\n12321\n121\n1\n",
|
|
Out);
|
|
}
|
|
|
|
TEST(MustacheSections, List) {
|
|
Value D = Object{{"list", Array{Object{{"item", 1}}, Object{{"item", 2}},
|
|
Object{{"item", 3}}}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{#list}}{{item}}{{/list}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("123", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, EmptyList) {
|
|
Value D = Object{{"list", Array{}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{#list}}Yay lists!{{/list}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, Doubled) {
|
|
Value D = Object{{"bool", true}, {"two", "second"}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{#bool}}\n* first\n{{/bool}}\n* "
|
|
"{{two}}\n{{#bool}}\n* third\n{{/bool}}\n",
|
|
Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("* first\n* second\n* third\n", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, NestedTruthy) {
|
|
Value D = Object{{"bool", true}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("| A B C D E |", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, NestedFalsey) {
|
|
Value D = Object{{"bool", false}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("| A E |", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, ContextMisses) {
|
|
Value D = Object{};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("[{{#missing}}Found key 'missing'!{{/missing}}]", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("[]", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, ImplicitIteratorString) {
|
|
Value D = Object{{"list", Array{"a", "b", "c", "d", "e"}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{#list}}({{.}}){{/list}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("(a)(b)(c)(d)(e)", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, ImplicitIteratorInteger) {
|
|
Value D = Object{{"list", Array{1, 2, 3, 4, 5}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{#list}}({{.}}){{/list}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("(1)(2)(3)(4)(5)", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, ImplicitIteratorArray) {
|
|
Value D = Object{{"list", Array{Array{1, 2, 3}, Array{"a", "b", "c"}}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{#list}}({{#.}}{{.}}{{/.}}){{/list}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("(123)(abc)", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, ImplicitIteratorHTMLEscaping) {
|
|
Value D = Object{{"list", Array{"&", "\"", "<", ">"}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{#list}}({{.}}){{/list}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("(&)(")(<)(>)", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, ImplicitIteratorAmpersand) {
|
|
Value D = Object{{"list", Array{"&", "\"", "<", ">"}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{#list}}({{&.}}){{/list}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("(&)(\")(<)(>)", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, ImplicitIteratorRootLevel) {
|
|
Value D = Array{Object{{"value", "a"}}, Object{{"value", "b"}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{#.}}({{value}}){{/.}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("(a)(b)", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, DottedNamesTruthy) {
|
|
Value D = Object{{"a", Object{{"b", Object{{"c", true}}}}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{#a.b.c}}Here{{/a.b.c}} == Here", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("Here == Here", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, DottedNamesFalsey) {
|
|
Value D = Object{{"a", Object{{"b", Object{{"c", false}}}}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{#a.b.c}}Here{{/a.b.c}} == ", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ(" == ", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, DottedNamesBrokenChains) {
|
|
Value D = Object{{"a", Object{{}}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{#a.b.c}}Here{{/a.b.c}} == ", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ(" == ", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, SurroundingWhitespace) {
|
|
Value D = Object{{"boolean", true}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T(" | {{#boolean}}\t|\t{{/boolean}} | \n", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ(" | \t|\t | \n", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, InternalWhitespace) {
|
|
Value D = Object{{"boolean", true}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T(" | {{#boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n",
|
|
Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ(" | \n | \n", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, IndentedInlineSections) {
|
|
Value D = Object{{"boolean", true}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T(" {{#boolean}}YES{{/boolean}}\n {{#boolean}}GOOD{{/boolean}}\n",
|
|
Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ(" YES\n GOOD\n", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, StandaloneLines) {
|
|
Value D = Object{{"boolean", true}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("| This Is\n{{#boolean}}\n|\n{{/boolean}}\n| A Line\n", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("| This Is\n|\n| A Line\n", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, IndentedStandaloneLines) {
|
|
Value D = Object{{"boolean", true}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("| This Is\n {{#boolean}}\n|\n {{/boolean}}\n| A Line\n", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("| This Is\n|\n| A Line\n", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, StandaloneLineEndings) {
|
|
Value D = Object{{"boolean", true}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("|\r\n{{#boolean}}\r\n{{/boolean}}\r\n|", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("|\r\n|", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, StandaloneWithoutPreviousLine) {
|
|
Value D = Object{{"boolean", true}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T(" {{#boolean}}\n#{{/boolean}}\n/", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("#\n/", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, StandaloneWithoutNewline) {
|
|
Value D = Object{{"boolean", true}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("#{{#boolean}}\n/\n {{/boolean}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("#\n/\n", Out);
|
|
}
|
|
|
|
TEST(MustacheSections, Padding) {
|
|
Value D = Object{{"boolean", true}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("|{{# boolean }}={{/ boolean }}|", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("|=|", Out);
|
|
}
|
|
|
|
TEST(MustacheInvertedSections, Falsey) {
|
|
Value D = Object{{"boolean", false}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{^boolean}}This should be rendered.{{/boolean}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("This should be rendered.", Out);
|
|
}
|
|
|
|
TEST(MustacheInvertedSections, Truthy) {
|
|
Value D = Object{{"boolean", true}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{^boolean}}This should not be rendered.{{/boolean}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("", Out);
|
|
}
|
|
|
|
TEST(MustacheInvertedSections, NullIsFalsey) {
|
|
Value D = Object{{"null", nullptr}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{^null}}This should be rendered.{{/null}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("This should be rendered.", Out);
|
|
}
|
|
|
|
TEST(MustacheInvertedSections, Context) {
|
|
Value D = Object{{"context", Object{{"name", "Joe"}}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{^context}}Hi {{name}}.{{/context}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("", Out);
|
|
}
|
|
|
|
TEST(MustacheInvertedSections, List) {
|
|
Value D = Object{
|
|
{"list", Array{Object{{"n", 1}}, Object{{"n", 2}}, Object{{"n", 3}}}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{^list}}{{n}}{{/list}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("", Out);
|
|
}
|
|
|
|
TEST(MustacheInvertedSections, EmptyList) {
|
|
Value D = Object{{"list", Array{}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{^list}}Yay lists!{{/list}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("Yay lists!", Out);
|
|
}
|
|
|
|
TEST(MustacheInvertedSections, Doubled) {
|
|
Value D = Object{{"bool", false}, {"two", "second"}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{^bool}}\n* first\n{{/bool}}\n* "
|
|
"{{two}}\n{{^bool}}\n* third\n{{/bool}}\n",
|
|
Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("* first\n* second\n* third\n", Out);
|
|
}
|
|
|
|
TEST(MustacheInvertedSections, NestedFalsey) {
|
|
Value D = Object{{"bool", false}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("| A B C D E |", Out);
|
|
}
|
|
|
|
TEST(MustacheInvertedSections, NestedTruthy) {
|
|
Value D = Object{{"bool", true}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("| A E |", Out);
|
|
}
|
|
|
|
TEST(MustacheInvertedSections, ContextMisses) {
|
|
Value D = Object{};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("[{{^missing}}Cannot find key 'missing'!{{/missing}}]", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("[Cannot find key 'missing'!]", Out);
|
|
}
|
|
|
|
TEST(MustacheInvertedSections, DottedNamesTruthy) {
|
|
Value D = Object{{"a", Object{{"b", Object{{"c", true}}}}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{^a.b.c}}Not Here{{/a.b.c}} == ", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ(" == ", Out);
|
|
}
|
|
|
|
TEST(MustacheInvertedSections, DottedNamesFalsey) {
|
|
Value D = Object{{"a", Object{{"b", Object{{"c", false}}}}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{^a.b.c}}Not Here{{/a.b.c}} == Not Here", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("Not Here == Not Here", Out);
|
|
}
|
|
|
|
TEST(MustacheInvertedSections, DottedNamesBrokenChains) {
|
|
Value D = Object{{"a", Object{}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{^a.b.c}}Not Here{{/a.b.c}} == Not Here", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("Not Here == Not Here", Out);
|
|
}
|
|
|
|
TEST(MustacheInvertedSections, SurroundingWhitespace) {
|
|
Value D = Object{{"boolean", false}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T(" | {{^boolean}}\t|\t{{/boolean}} | \n", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ(" | \t|\t | \n", Out);
|
|
}
|
|
|
|
TEST(MustacheInvertedSections, InternalWhitespace) {
|
|
Value D = Object{{"boolean", false}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T(" | {{^boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n",
|
|
Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ(" | \n | \n", Out);
|
|
}
|
|
|
|
TEST(MustacheInvertedSections, IndentedInlineSections) {
|
|
Value D = Object{{"boolean", false}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T(" {{^boolean}}NO{{/boolean}}\n {{^boolean}}WAY{{/boolean}}\n",
|
|
Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ(" NO\n WAY\n", Out);
|
|
}
|
|
|
|
TEST(MustacheInvertedSections, StandaloneLines) {
|
|
Value D = Object{{"boolean", false}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("| This Is\n{{^boolean}}\n|\n{{/boolean}}\n| A Line\n", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("| This Is\n|\n| A Line\n", Out);
|
|
}
|
|
|
|
TEST(MustacheInvertedSections, StandaloneIndentedLines) {
|
|
Value D = Object{{"boolean", false}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("| This Is\n {{^boolean}}\n|\n {{/boolean}}\n| A Line\n", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("| This Is\n|\n| A Line\n", Out);
|
|
}
|
|
|
|
TEST(MustacheInvertedSections, StandaloneLineEndings) {
|
|
Value D = Object{{"boolean", false}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("|\r\n{{^boolean}}\r\n{{/boolean}}\r\n|", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("|\r\n|", Out);
|
|
}
|
|
|
|
TEST(MustacheInvertedSections, StandaloneWithoutPreviousLine) {
|
|
Value D = Object{{"boolean", false}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T(" {{^boolean}}\n^{{/boolean}}\n/", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("^\n/", Out);
|
|
}
|
|
|
|
TEST(MustacheInvertedSections, StandaloneWithoutNewline) {
|
|
Value D = Object{{"boolean", false}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("^{{^boolean}}\n/\n {{/boolean}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("^\n/\n", Out);
|
|
}
|
|
|
|
TEST(MustacheInvertedSections, Padding) {
|
|
Value D = Object{{"boolean", false}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("|{{^ boolean }}={{/ boolean }}|", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("|=|", Out);
|
|
}
|
|
|
|
TEST(MustachePartials, BasicBehavior) {
|
|
Value D = Object{};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{>text}}", Ctx);
|
|
T.registerPartial("text", "from partial");
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("from partial", Out);
|
|
}
|
|
|
|
TEST(MustachePartials, FailedLookup) {
|
|
Value D = Object{};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{>text}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("", Out);
|
|
}
|
|
|
|
TEST(MustachePartials, Context) {
|
|
Value D = Object{{"text", "content"}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{>partial}}", Ctx);
|
|
T.registerPartial("partial", "*{{text}}*");
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("*content*", Out);
|
|
}
|
|
|
|
TEST(MustachePartials, Recursion) {
|
|
Value D =
|
|
Object{{"content", "X"},
|
|
{"nodes", Array{Object{{"content", "Y"}, {"nodes", Array{}}}}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{>node}}", Ctx);
|
|
T.registerPartial("node", "{{content}}({{#nodes}}{{>node}}{{/nodes}})");
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("X(Y())", Out);
|
|
}
|
|
|
|
TEST(MustachePartials, Nested) {
|
|
Value D = Object{{"a", "hello"}, {"b", "world"}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{>outer}}", Ctx);
|
|
T.registerPartial("outer", "*{{a}} {{>inner}}*");
|
|
T.registerPartial("inner", "{{b}}!");
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("*hello world!*", Out);
|
|
}
|
|
|
|
TEST(MustachePartials, SurroundingWhitespace) {
|
|
Value D = Object{};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("| {{>partial}} |", Ctx);
|
|
T.registerPartial("partial", "\t|\t");
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("| \t|\t |", Out);
|
|
}
|
|
|
|
TEST(MustachePartials, InlineIndentation) {
|
|
Value D = Object{{"data", "|"}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T(" {{data}} {{> partial}}\n", Ctx);
|
|
T.registerPartial("partial", "<\n<");
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ(" | <\n<\n", Out);
|
|
}
|
|
|
|
TEST(MustachePartials, PaddingWhitespace) {
|
|
Value D = Object{{"boolean", true}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("|{{> partial }}|", Ctx);
|
|
T.registerPartial("partial", "[]");
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("|[]|", Out);
|
|
}
|
|
|
|
TEST(MustachePartials, StandaloneIndentation) {
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
mustache::Template T("\\\n {{>partial}}\n/\n", Ctx);
|
|
T.registerPartial("partial", "|\n{{{content}}}\n|\n");
|
|
std::string O;
|
|
raw_string_ostream OS(O);
|
|
Value DataContext = Object{{"content", "<\n->"}};
|
|
T.render(DataContext, OS);
|
|
EXPECT_EQ("\\\n |\n <\n->\n |\n/\n", OS.str());
|
|
}
|
|
|
|
TEST(MustacheLambdas, BasicInterpolation) {
|
|
Value D = Object{};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("Hello, {{lambda}}!", Ctx);
|
|
Lambda L = []() -> llvm::json::Value { return "World"; };
|
|
T.registerLambda("lambda", L);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("Hello, World!", Out);
|
|
}
|
|
|
|
TEST(MustacheLambdas, InterpolationExpansion) {
|
|
Value D = Object{{"planet", "World"}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("Hello, {{lambda}}!", Ctx);
|
|
Lambda L = []() -> llvm::json::Value { return "{{planet}}"; };
|
|
T.registerLambda("lambda", L);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("Hello, World!", Out);
|
|
}
|
|
|
|
TEST(MustacheLambdas, BasicMultipleCalls) {
|
|
Value D = Object{};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{lambda}} == {{lambda}} == {{lambda}}", Ctx);
|
|
int I = 0;
|
|
Lambda L = [&I]() -> llvm::json::Value {
|
|
I += 1;
|
|
return I;
|
|
};
|
|
T.registerLambda("lambda", L);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("1 == 2 == 3", Out);
|
|
}
|
|
|
|
TEST(MustacheLambdas, Escaping) {
|
|
Value D = Object{};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("<{{lambda}}{{&lambda}}", Ctx);
|
|
Lambda L = []() -> llvm::json::Value { return ">"; };
|
|
T.registerLambda("lambda", L);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("<>>", Out);
|
|
}
|
|
|
|
TEST(MustacheLambdas, Sections) {
|
|
Value D = Object{};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("<{{#lambda}}{{x}}{{/lambda}}>", Ctx);
|
|
SectionLambda L = [](StringRef Text) -> llvm::json::Value {
|
|
if (Text == "{{x}}") {
|
|
return "yes";
|
|
}
|
|
return "no";
|
|
};
|
|
T.registerLambda("lambda", L);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("<yes>", Out);
|
|
}
|
|
|
|
TEST(MustacheLambdas, SectionExpansion) {
|
|
Value D = Object{
|
|
{"planet", "Earth"},
|
|
};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("<{{#lambda}}-{{/lambda}}>", Ctx);
|
|
SectionLambda L = [](StringRef Text) -> llvm::json::Value {
|
|
SmallString<128> Result;
|
|
Result += Text;
|
|
Result += "{{planet}}";
|
|
Result += Text;
|
|
return Result;
|
|
};
|
|
T.registerLambda("lambda", L);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("<-Earth->", Out);
|
|
}
|
|
|
|
TEST(MustacheLambdas, SectionsMultipleCalls) {
|
|
Value D = Object{};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{#lambda}}FILE{{/lambda}} != {{#lambda}}LINE{{/lambda}}", Ctx);
|
|
SectionLambda L = [](StringRef Text) -> llvm::json::Value {
|
|
SmallString<128> Result;
|
|
Result += "__";
|
|
Result += Text;
|
|
Result += "__";
|
|
return Result;
|
|
};
|
|
T.registerLambda("lambda", L);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("__FILE__ != __LINE__", Out);
|
|
}
|
|
|
|
TEST(MustacheLambdas, InvertedSections) {
|
|
Value D = Object{{"static", "static"}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("<{{^lambda}}{{static}}{{/lambda}}>", Ctx);
|
|
SectionLambda L = [](StringRef Text) -> llvm::json::Value { return false; };
|
|
T.registerLambda("lambda", L);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("<>", Out);
|
|
}
|
|
|
|
TEST(MustacheComments, Inline) {
|
|
// Comment blocks should be removed from the template.
|
|
Value D = {};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("12345{{! Comment Block! }}67890", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("1234567890", Out);
|
|
}
|
|
|
|
TEST(MustacheComments, Multiline) {
|
|
// Multiline comments should be permitted.
|
|
Value D = {};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("12345{{!\n This is a\n multi-line comment...\n}}67890\n", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("1234567890\n", Out);
|
|
}
|
|
|
|
TEST(MustacheComments, Standalone) {
|
|
// All standalone comment lines should be removed.
|
|
Value D = {};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("Begin.\n{{! Comment Block! }}\nEnd.\n", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("Begin.\nEnd.\n", Out);
|
|
}
|
|
|
|
TEST(MustacheComments, IndentedStandalone) {
|
|
// All standalone comment lines should be removed.
|
|
Value D = {};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("Begin.\n {{! Indented Comment Block! }}\nEnd.\n", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("Begin.\nEnd.\n", Out);
|
|
}
|
|
|
|
TEST(MustacheComments, StandaloneLineEndings) {
|
|
// "\r\n" should be considered a newline for standalone tags.
|
|
Value D = {};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("|\r\n{{! Standalone Comment }}\r\n|", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("|\r\n|", Out);
|
|
}
|
|
|
|
TEST(MustacheComments, StandaloneWithoutPreviousLine) {
|
|
// Standalone tags should not require a newline to precede them.
|
|
Value D = {};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T(" {{! I'm Still Standalone }}\n!", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("!", Out);
|
|
}
|
|
|
|
TEST(MustacheComments, StandaloneWithoutNewline) {
|
|
// Standalone tags should not require a newline to follow them.
|
|
Value D = {};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("!\n {{! I'm Still Standalone }}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("!\n", Out);
|
|
}
|
|
|
|
TEST(MustacheComments, MultilineStandalone) {
|
|
// All standalone comment lines should be removed.
|
|
Value D = {};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("Begin.\n{{!\nSomething's going on here...\n}}\nEnd.\n", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("Begin.\nEnd.\n", Out);
|
|
}
|
|
|
|
TEST(MustacheComments, IndentedMultilineStandalone) {
|
|
// All standalone comment lines should be removed.
|
|
Value D = {};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("Begin.\n {{!\n Something's going on here...\n }}\nEnd.\n",
|
|
Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("Begin.\nEnd.\n", Out);
|
|
}
|
|
|
|
TEST(MustacheComments, IndentedInline) {
|
|
// Inline comments should not strip whitespace.
|
|
Value D = {};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T(" 12 {{! 34 }}\n", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ(" 12 \n", Out);
|
|
}
|
|
|
|
TEST(MustacheComments, SurroundingWhitespace) {
|
|
// Comment removal should preserve surrounding whitespace.
|
|
Value D = {};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("12345 {{! Comment Block! }} 67890", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("12345 67890", Out);
|
|
}
|
|
|
|
TEST(MustacheComments, VariableNameCollision) {
|
|
// Comments must never render, even if a variable with the same name exists.
|
|
Value D = Object{
|
|
{"! comment", 1}, {"! comment ", 2}, {"!comment", 3}, {"comment", 4}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("comments never show: >{{! comment }}<", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("comments never show: ><", Out);
|
|
}
|
|
|
|
// XFAIL: The following tests for the Triple Mustache feature are expected to
|
|
// fail. The assertions have been inverted from EXPECT_EQ to EXPECT_NE to allow
|
|
// them to pass against the current implementation. Once Triple Mustache is
|
|
// implemented, these assertions should be changed back to EXPECT_EQ.
|
|
TEST(MustacheTripleMustache, Basic) {
|
|
Value D = Object{{"subject", "<b>World</b>"}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("Hello, {{{subject}}}!", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("Hello, <b>World</b>!", Out);
|
|
}
|
|
|
|
TEST(MustacheTripleMustache, IntegerInterpolation) {
|
|
Value D = Object{{"mph", 85}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{{mph}}} miles an hour!", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("85 miles an hour!", Out);
|
|
}
|
|
|
|
TEST(MustacheTripleMustache, DecimalInterpolation) {
|
|
Value D = Object{{"power", 1.21}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{{power}}} jiggawatts!", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("1.21 jiggawatts!", Out);
|
|
}
|
|
|
|
TEST(MustacheTripleMustache, NullInterpolation) {
|
|
Value D = Object{{"cannot", nullptr}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("I ({{{cannot}}}) be seen!", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("I () be seen!", Out);
|
|
}
|
|
|
|
TEST(MustacheTripleMustache, ContextMissInterpolation) {
|
|
Value D = Object{};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("I ({{{cannot}}}) be seen!", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("I () be seen!", Out);
|
|
}
|
|
|
|
TEST(MustacheTripleMustache, DottedNames) {
|
|
Value D = Object{{"person", Object{{"name", "<b>Joe</b>"}}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{{person.name}}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("<b>Joe</b>", Out);
|
|
}
|
|
|
|
TEST(MustacheTripleMustache, ImplicitIterator) {
|
|
Value D = Object{{"list", Array{"<a>", "<b>"}}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{#list}}({{{.}}}){{/list}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("(<a>)(<b>)", Out);
|
|
}
|
|
|
|
TEST(MustacheTripleMustache, SurroundingWhitespace) {
|
|
Value D = Object{{"string", "---"}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("| {{{string}}} |", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("| --- |", Out);
|
|
}
|
|
|
|
TEST(MustacheTripleMustache, Standalone) {
|
|
Value D = Object{{"string", "---"}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T(" {{{string}}}\n", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ(" ---\n", Out);
|
|
}
|
|
|
|
TEST(MustacheTripleMustache, WithPadding) {
|
|
Value D = Object{{"string", "---"}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("|{{{ string }}}|", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("|---|", Out);
|
|
}
|
|
|
|
TEST(MustacheDelimiters, PairBehavior) {
|
|
Value D = Object{{"text", "Hey!"}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("{{=<% %>=}}(<%text%>)", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("(Hey!)", Out);
|
|
}
|
|
|
|
TEST(MustacheDelimiters, SpecialCharacters) {
|
|
Value D = Object{{"text", "It worked!"}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("({{=[ ]=}}[text])", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("(It worked!)", Out);
|
|
}
|
|
|
|
TEST(MustacheDelimiters, Sections) {
|
|
Value D = Object{{"section", true}, {"data", "I got interpolated."}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("[\n{{#section}}\n {{data}}\n |data|\n{{/section}}\n\n{{= "
|
|
"| | =}}\n|#section|\n {{data}}\n |data|\n|/section|\n]\n",
|
|
Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("[\n I got interpolated.\n |data|\n\n {{data}}\n I got "
|
|
"interpolated.\n]\n",
|
|
Out);
|
|
}
|
|
|
|
TEST(MustacheDelimiters, InvertedSections) {
|
|
Value D = Object{{"section", false}, {"data", "I got interpolated."}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("[\n{{^section}}\n {{data}}\n |data|\n{{/section}}\n\n{{= "
|
|
"| | =}}\n|^section|\n {{data}}\n |data|\n|/section|\n]\n",
|
|
Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("[\n I got interpolated.\n |data|\n\n {{data}}\n I got "
|
|
"interpolated.\n]\n",
|
|
Out);
|
|
}
|
|
|
|
TEST(MustacheDelimiters, PartialInheritence) {
|
|
Value D = Object{{"value", "yes"}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("[ {{>include}} ]\n{{= | | =}}\n[ |>include| ]\n", Ctx);
|
|
T.registerPartial("include", ".{{value}}.");
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("[ .yes. ]\n[ .yes. ]\n", Out);
|
|
}
|
|
|
|
TEST(MustacheDelimiters, PostPartialBehavior) {
|
|
Value D = Object{{"value", "yes"}};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("[ {{>include}} ]\n[ .{{value}}. .|value|. ]\n", Ctx);
|
|
T.registerPartial("include", ".{{value}}. {{= | | =}} .|value|.");
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("[ .yes. .yes. ]\n[ .yes. .|value|. ]\n", Out);
|
|
}
|
|
|
|
TEST(MustacheDelimiters, SurroundingWhitespace) {
|
|
Value D = Object{};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("| {{=@ @=}} |", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("| |", Out);
|
|
}
|
|
|
|
TEST(MustacheDelimiters, OutlyingWhitespaceInline) {
|
|
Value D = Object{};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T(" | {{=@ @=}}\n", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ(" | \n", Out);
|
|
}
|
|
|
|
TEST(MustacheDelimiters, StandaloneTag) {
|
|
Value D = Object{};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("Begin.\n{{=@ @=}}\nEnd.\n", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("Begin.\nEnd.\n", Out);
|
|
}
|
|
|
|
TEST(MustacheDelimiters, IndentedStandaloneTag) {
|
|
Value D = Object{};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("Begin.\n {{=@ @=}}\nEnd.\n", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("Begin.\nEnd.\n", Out);
|
|
}
|
|
|
|
TEST(MustacheDelimiters, StandaloneLineEndings) {
|
|
Value D = Object{};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("|\r\n{{= @ @ =}}\r\n|", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("|\r\n|", Out);
|
|
}
|
|
|
|
TEST(MustacheDelimiters, StandaloneWithoutPreviousLine) {
|
|
Value D = Object{};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T(" {{=@ @=}}\n=", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("=", Out);
|
|
}
|
|
|
|
TEST(MustacheDelimiters, StandaloneWithoutNewline) {
|
|
Value D = Object{};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("=\n {{=@ @=}}", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("=\n", Out);
|
|
}
|
|
|
|
TEST(MustacheDelimiters, PairwithPadding) {
|
|
Value D = Object{};
|
|
BumpPtrAllocator Allocator;
|
|
StringSaver Saver(Allocator);
|
|
MustacheContext Ctx(Allocator, Saver);
|
|
Template T("|{{= @ @ =}}|", Ctx);
|
|
std::string Out;
|
|
raw_string_ostream OS(Out);
|
|
T.render(D, OS);
|
|
EXPECT_EQ("||", Out);
|
|
}
|