// Copyright (c) 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// ---
// Author: Craig Silverstein

#include "config_for_unittests.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <assert.h>
#include <time.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>     // for mkdir
#if defined(HAVE_PTHREAD) && !defined(NO_THREADS)
#include <pthread.h>
#endif
#ifdef HAVE_DIRENT_H
# include <dirent.h>      // for readdir
#else
# define dirent direct
# ifdef HAVE_SYS_NDIR_H
#  include <sys/ndir.h>
# endif
# ifdef HAVE_SYS_DIR_H
#  include <sys/dir.h>
# endif
# ifdef HAVE_NDIR_H
#  include <ndir.h>
# endif
#endif
#include <string>
#include <vector>         // for MissingListType, SyntaxListType
#include "ctemplate/template.h"
#include "ctemplate/template_pathops.h"
#include "ctemplate/template_annotator.h"
#include "ctemplate/template_dictionary.h"
#include "ctemplate/template_emitter.h"
#include "ctemplate/template_modifiers.h"
#include "ctemplate/template_namelist.h"
#include "ctemplate/template_string.h"

using std::vector;
using std::string;
using GOOGLE_NAMESPACE::ExpandEmitter;
using GOOGLE_NAMESPACE::PerExpandData;
using GOOGLE_NAMESPACE::Template;
using GOOGLE_NAMESPACE::TemplateDictionary;
using GOOGLE_NAMESPACE::TemplateString;
using GOOGLE_NAMESPACE::StaticTemplateString;
using GOOGLE_NAMESPACE::TemplateNamelist;
using GOOGLE_NAMESPACE::TemplateContext;
using GOOGLE_NAMESPACE::Strip;
using GOOGLE_NAMESPACE::DO_NOT_STRIP;
using GOOGLE_NAMESPACE::STRIP_BLANK_LINES;
using GOOGLE_NAMESPACE::STRIP_WHITESPACE;
using GOOGLE_NAMESPACE::TC_HTML;
using GOOGLE_NAMESPACE::TC_CSS;
using GOOGLE_NAMESPACE::TC_JS;
using GOOGLE_NAMESPACE::TC_JSON;
using GOOGLE_NAMESPACE::TC_XML;
using GOOGLE_NAMESPACE::TC_MANUAL;
using GOOGLE_NAMESPACE::TC_UNUSED;

static const StaticTemplateString kHello = STS_INIT(kHello, "Hello");
static const StaticTemplateString kWorld = STS_INIT(kWorld, "World");

static const char* kPragmaHtml = "{{%AUTOESCAPE context=\"HTML\"}}\n";
static const char* kPragmaJs   = "{{%AUTOESCAPE context=\"JAVASCRIPT\"}}\n";
static const char* kPragmaCss  = "{{%AUTOESCAPE context=\"CSS\"}}\n";
static const char* kPragmaXml  = "{{%AUTOESCAPE context=\"XML\"}}\n";
static const char* kPragmaJson = "{{%AUTOESCAPE context=\"JSON\"}}\n";

// How many threads to use for our threading test.
// This is a #define instead of a const int so we can use it in array-sizes
// even on c++ compilers that don't support var-length arrays.
#define kNumThreads  10

#define PFATAL(s)  do { perror(s); exit(1); } while (0)

#define ASSERT(cond)  do {                                      \
  if (!(cond)) {                                                \
    printf("ASSERT FAILED, line %d: %s\n", __LINE__, #cond);    \
    assert(cond);                                               \
    exit(1);                                                    \
  }                                                             \
} while (0)

#define ASSERT_STREQ_EXCEPT(a, b, except)  ASSERT(StreqExcept(a, b, except))
#define ASSERT_STREQ(a, b)                 ASSERT(strcmp(a, b) == 0)
#define ASSERT_NOT_STREQ(a, b)             ASSERT(strcmp(a, b) != 0)
#define ASSERT_STREQ_VERBOSE(a, b, c)      ASSERT(StrEqVerbose(a, b, c))
#define ASSERT_INTEQ(a, b)         ASSERT(IntEqVerbose(a, b))

namespace {

// First, (conceptually) remove all chars in "except" from both a and b.
// Then return true iff munged_a == munged_b.
bool StreqExcept(const char* a, const char* b, const char* except) {
  const char* pa = a, *pb = b;
  const size_t exceptlen = strlen(except);
  while (1) {
    // Use memchr isntead of strchr because memchr(foo, '\0') always fails
    while (memchr(except, *pa, exceptlen))  pa++;  // ignore "except" chars in a
    while (memchr(except, *pb, exceptlen))  pb++;  // ignore "except" chars in b
    if ((*pa == '\0') && (*pb == '\0'))
      return true;
    if (*pa++ != *pb++)                  // includes case where one is at \0
      return false;
  }
}

// If a and b do not match, print their values and that of text
// and return false.
bool StrEqVerbose(const string& a, const string& b,
                         const string& text) {
  if (a != b) {
    printf("EXPECTED: %s\n", a.c_str());
    printf("ACTUAL: %s\n", b.c_str());
    printf("TEXT: %s\n", text.c_str());
    return false;
  }
  return true;
}

bool IntEqVerbose(int a, int b) {
  if (a != b) {
    printf("EXPECTED: %d\n", a);
    printf("ACTUAL: %d\n", b);
    return false;
  }
  return true;
}

// This test emitter writes to a string, but writes X's of the right
// length, rather than the actual content passed in.
class SizeofEmitter : public ExpandEmitter {
  string* const outbuf_;
 public:
  SizeofEmitter(string* outbuf) : outbuf_(outbuf) {}
  virtual void Emit(char c) { Emit(&c, 1); }
  virtual void Emit(const string& s) { Emit(s.data(), s.length()); }
  virtual void Emit(const char* s) { Emit(s, strlen(s)); }
  virtual void Emit(const char*, size_t slen) { outbuf_->append(slen, 'X'); }
};

}  // unnamed namespace

RegisterTemplateFilename(VALID1_FN, "template_unittest_test_valid1.in");
RegisterTemplateFilename(INVALID1_FN, "template_unittest_test_invalid1.in");
RegisterTemplateFilename(INVALID2_FN, "template_unittest_test_invalid2.in");
RegisterTemplateFilename(NONEXISTENT_FN, "nonexistent__file.tpl");

// Returns the proper AUTOESCAPE pragma that corresponds to the
// given TemplateContext.
static string GetPragmaForContext(TemplateContext context) {
  switch(context) {
    case TC_HTML:
      return kPragmaHtml;
    case TC_JS:
      return kPragmaJs;
    case TC_CSS:
      return kPragmaCss;
    case TC_JSON:
      return kPragmaJson;
    case TC_XML:
      return kPragmaXml;
    case TC_MANUAL:
      return "";  // No AUTOESCAPE pragma.
    case TC_UNUSED:
      ASSERT(false);  // Developer error, this TC is not to be used.
  }
  ASSERT(false);  // Developer error - invalid TemplateContext.
  return "";
}

// deletes all files named *template* in dir
#ifndef _WIN32   // windows will define its own version, in src/windows/port.cc
static void CleanTestDir(const string& dirname) {
  DIR* dir = opendir(dirname.c_str());
  if (!dir) {  // directory doesn't exist or something like that.
    if (errno == ENOENT)   // if dir doesn't exist, try to make it
      ASSERT(mkdir(dirname.c_str(), 0755) == 0);
    return;
  }
  while (struct dirent* d = readdir(dir)) {
    if (strstr(d->d_name, "template"))
      unlink(GOOGLE_NAMESPACE::PathJoin(dirname, d->d_name).c_str());
  }
  closedir(dir);
}

static string TmpFile(const char* basename) {
  return string("/tmp/") + basename;
}
#endif

static const string FLAGS_test_tmpdir(TmpFile("template_unittest_dir"));


// This writes s to the given file
static void StringToFile(const string& s, const string& filename) {
  FILE* fp = fopen(filename.c_str(), "wb");
  ASSERT(fp);
  size_t r = fwrite(s.data(), 1, s.length(), fp);
  ASSERT(r == s.length());
  fclose(fp);
}

// This writes s to a file and returns the filename.
static string StringToTemplateFile(const string& s) {
  static int filenum = 0;
  char buf[16];
  snprintf(buf, sizeof(buf), "%03d", ++filenum);
  string filename = GOOGLE_NAMESPACE::PathJoin(FLAGS_test_tmpdir,
                                        string("template.") + buf);
  StringToFile(s, filename);
  return filename;
}

// This writes s to a file and then loads it into a template object.
static Template* StringToTemplate(const string& s, Strip strip) {
  return Template::GetTemplate(StringToTemplateFile(s), strip);
}

// This writes s to a file with the AUTOESCAPE pragma corresponding
// to the given TemplateContext and then loads it into a template object.
static Template* StringToTemplateWithAutoEscaping(const string& s,
                                                  Strip strip,
                                                  TemplateContext context) {
  string text = GetPragmaForContext(context) + s;
  return Template::GetTemplate(StringToTemplateFile(text), strip);
}

// This is esp. useful for calling from within gdb.
// The gdb nice-ness is balanced by the need for the caller to delete the buf.

static const char* ExpandIs(Template* tpl, const TemplateDictionary *dict,
                            PerExpandData* per_expand_data,
                            bool expected) {
  string outstring;
  ASSERT(expected == tpl->ExpandWithData(&outstring, dict, per_expand_data));

  char* buf = new char [outstring.size()+1];
  strcpy(buf, outstring.c_str());
  return buf;
}

static void AssertExpandWithDataIs(Template* tpl,
                                   const TemplateDictionary *dict,
                                   PerExpandData* per_expand_data,
                                   const string& is, bool expected) {
  const char* buf = ExpandIs(tpl, dict, per_expand_data, expected);
  if (strcmp(buf, is.c_str())) {
    printf("expected = '%s'\n", is.c_str());
    printf("actual   = '%s'\n", buf);
  }
  ASSERT_STREQ(buf, is.c_str());
  delete [] buf;
}

static void AssertExpandIs(Template* tpl, TemplateDictionary *dict,
                           const string& is, bool expected) {
  PerExpandData per_expand_data;
  AssertExpandWithDataIs(tpl, dict, &per_expand_data, is, expected);
}

// A helper method used by TestCorrectModifiersForAutoEscape.
// Populates out with lines of the form:
// VARNAME:mod1[=val1][:mod2[=val2]]...\n from the dump of the template
// and compares against the expected string.
static void AssertCorrectModifiersInTemplate(Template* tpl,
                                             const string& text,
                                             const string& expected_out) {
  ASSERT(tpl);
  string dump_out, out;
  tpl->DumpToString("bogus_filename", &dump_out);
  string::size_type i, j;
  i = 0;
  while ((i = dump_out.find("Variable Node: ", i)) != string::npos) {
    i += strlen("Variable Node: ");
    j = dump_out.find("\n", i);
    out.append(dump_out.substr(i, j - i));   // should be safe.
    out.append("\n");
  }
  ASSERT_STREQ_VERBOSE(expected_out, out, text);
}

// Wrapper on top of AssertCorrectModifiersInTemplate which first
// obtains a template from the given contents and template context.
static void AssertCorrectModifiers(TemplateContext template_type,
                                   const string& text,
                                   const string& expected_out) {
  Strip strip = STRIP_WHITESPACE;
  Template *tpl = StringToTemplateWithAutoEscaping(text, strip, template_type);
  AssertCorrectModifiersInTemplate(tpl, text, expected_out);
}

// A helper method used by TestCorrectModifiersForAutoEscape.
// Initializes the template in the Auto Escape mode with the
// given TemplateContext, expands it with the given dictionary
// and checks that the output matches the expected value.
static void AssertCorrectEscaping(TemplateContext template_type,
                                  const TemplateDictionary& dict,
                                  const string& text,
                                  const string& expected_out) {
  Strip strip = STRIP_WHITESPACE;
  Template *tpl = StringToTemplateWithAutoEscaping(text, strip, template_type);
  string outstring;
  tpl->Expand(&outstring, &dict);
  ASSERT_STREQ_VERBOSE(expected_out, outstring, text);
}

class DynamicModifier : public GOOGLE_NAMESPACE::TemplateModifier {
 public:
  void Modify(const char* in, size_t inlen,
              const PerExpandData* per_expand_data,
              ExpandEmitter* outbuf, const string& arg) const {
    assert(arg.empty());    // we don't take an argument
    assert(per_expand_data);
    const char* value = per_expand_data->LookupForModifiersAsString("value");
    if (value)
      outbuf->Emit(value);
  }
};

class EmphasizeTemplateModifier : public GOOGLE_NAMESPACE::TemplateModifier {
 public:
  EmphasizeTemplateModifier(const string& match)
      : match_(match) {
  }

  bool MightModify(const PerExpandData* per_expand_data,
                   const string& arg) const {
    return strstr(arg.c_str(), match_.c_str());
  }

  void Modify(const char* in, size_t inlen,
              const PerExpandData* per_expand_data,
              ExpandEmitter* outbuf, const string& arg) const {
    outbuf->Emit(">>");
    outbuf->Emit(in, inlen);
    outbuf->Emit("<<");
  }

 private:
  string match_;
};

class TemplateForTest : public Template {
 public:
  using Template::kSafeWhitelistedVariables;
  using Template::kNumSafeWhitelistedVariables;
 private:
  // This quiets gcc3, which otherwise complains: "base `Template'
  // with only non-default constructor in class without a constructor".
  TemplateForTest();
};

class TemplateUnittest {
 public:

  static void CheckWhitelistedVariablesSorted() {
    // NOTE(williasr): kSafeWhitelistedVariables must be sorted, it's accessed
    // using binary search.
    for (int i = 1; i < TemplateForTest::kNumSafeWhitelistedVariables; i++) {
      assert(strcmp(TemplateForTest::kSafeWhitelistedVariables[i-1],
                    TemplateForTest::kSafeWhitelistedVariables[i]) < 0);
    }
  }


  // The following tests test various aspects of how Expand() should behave.
  static void TestWeirdSyntax() {
    TemplateDictionary dict("dict");

    // When we see {{{, we should match the second {{, not the first.
    Template* tpl1 = StringToTemplate("hi {{{! VAR {{!VAR} }} lo",
                                      STRIP_WHITESPACE);
    AssertExpandIs(tpl1, &dict, "hi { lo", true);

    // Likewise for }}}
    Template* tpl2 = StringToTemplate("fn(){{{BI_NEWLINE}} x=4;{{BI_NEWLINE}}}",
                                      DO_NOT_STRIP);
    AssertExpandIs(tpl2, &dict, "fn(){\n x=4;\n}", true);

    // Try lots of {'s!
    Template* tpl3 = StringToTemplate("{{{{{{VAR}}}}}}}}", DO_NOT_STRIP);
    AssertExpandIs(tpl3, &dict, "{{{{}}}}}}", true);
  }

  static void TestComment() {
    TemplateDictionary dict("dict");
    Template* tpl1 = StringToTemplate("hi {{!VAR}} lo",
                                      STRIP_WHITESPACE);
    AssertExpandIs(tpl1, &dict, "hi  lo", true);

    Template* tpl2 = StringToTemplate("hi {{!VAR {VAR} }} lo",
                                      STRIP_WHITESPACE);
    AssertExpandIs(tpl2, &dict, "hi  lo", true);

    Template* tpl3 = StringToTemplate("hi {{! VAR {{!VAR} }} lo",
                                      STRIP_WHITESPACE);
    AssertExpandIs(tpl3, &dict, "hi  lo", true);
  }

  static void TestSetMarkerDelimiters() {
    TemplateDictionary dict("dict");
    dict.SetValue("VAR", "yo");
    Template* tpl1 = StringToTemplate("{{=| |=}}\nhi |VAR| {{lo}}",
                                      STRIP_WHITESPACE);
    AssertExpandIs(tpl1, &dict, "hi yo {{lo}}", true);

    Template* tpl2 = StringToTemplate("{{=| |=}}hi |VAR| {{lo}}",
                                      STRIP_WHITESPACE);
    AssertExpandIs(tpl2, &dict, "hi yo {{lo}}", true);

    Template* tpl3 = StringToTemplate("{{=| ||=}}hi ||VAR|||VAR|| {{lo}}",
                                      STRIP_WHITESPACE);
    AssertExpandIs(tpl3, &dict, "hi |yoyo {{lo}}", true);

    Template* tpl4 = StringToTemplate("{{=< >=}}hi <<VAR>> {{lo}}",
                                      STRIP_WHITESPACE);
    AssertExpandIs(tpl4, &dict, "hi <yo> {{lo}}", true);

    Template* tpl4b = StringToTemplate("{{=<< >>=}}hi <<VAR>> {{lo}}",
                                      STRIP_WHITESPACE);
    AssertExpandIs(tpl4b, &dict, "hi yo {{lo}}", true);

    Template* tpl4c = StringToTemplate("{{=<< <<=}}hi <<VAR<< {{lo}}",
                                      STRIP_WHITESPACE);
    AssertExpandIs(tpl4c, &dict, "hi yo {{lo}}", true);

    Template* tpl5 = StringToTemplate("hi {{VAR}} lo\n{{=< >=}}\n"
                                      "hi {{VAR}} lo\n"
                                      "hi <VAR> lo\n<={ }=>\n"
                                      "hi {{VAR}} lo\n{={{ }}=}\n"
                                      "hi {{VAR}} lo\n",
                                      STRIP_WHITESPACE);
    AssertExpandIs(tpl5, &dict,
                   "hi yo lohi {{VAR}} lohi yo lohi {yo} lohi yo lo",
                   true);

    Template* tpl6 = StringToTemplate("hi {{VAR}} lo\n{{=< >}}\n",
                                      STRIP_WHITESPACE);
    ASSERT(tpl6 == NULL);

    Template* tpl7 = StringToTemplate("hi {{VAR}} lo\n{{=<>}}\n",
                                      STRIP_WHITESPACE);
    ASSERT(tpl7 == NULL);

    Template* tpl8 = StringToTemplate("hi {{VAR}} lo\n{{=<  >=}}\n",
                                      STRIP_WHITESPACE);
    ASSERT(tpl8 == NULL);

    Template* tpl9 = StringToTemplate("hi {{VAR}} lo\n{{==}}\n",
                                      STRIP_WHITESPACE);
    ASSERT(tpl9 == NULL);

    Template* tpl10 = StringToTemplate("hi {{VAR}} lo\n{{=}}\n",
                                       STRIP_WHITESPACE);
    ASSERT(tpl10 == NULL);

    // Test that {{= =}} is a "removable" marker.
    Template* tpl11 = StringToTemplate("line\n  {{=| |=}} \nhi |VAR| {{lo}}\n",
                                       STRIP_BLANK_LINES);
    AssertExpandIs(tpl11, &dict, "line\nhi yo {{lo}}\n", true);

    // Test that "removable" markers survive marker-modification.
    Template* tpl12 = StringToTemplate("  {{#SEC1}}  \n"
                                       "{{=| |=}}    |VAR|\n"
                                       "  |/SEC1|\ntada! |VAR|\n"
                                       "hello|=<< >>=|\n"
                                       "   <<! a blank line>>  \n"
                                       "done",
                                       STRIP_BLANK_LINES);
    AssertExpandIs(tpl12, &dict, "tada! yo\nhello\ndone", true);
  }

  static void TestVariable() {
    Template* tpl = StringToTemplate("hi {{VAR}} lo", STRIP_WHITESPACE);
    TemplateDictionary dict("dict");
    AssertExpandIs(tpl, &dict, "hi  lo", true);
    dict.SetValue("VAR", "yo");
    AssertExpandIs(tpl, &dict, "hi yo lo", true);
    dict.SetValue("VAR", "yoyo");
    AssertExpandIs(tpl, &dict, "hi yoyo lo", true);
    dict.SetValue("VA", "noyo");
    dict.SetValue("VAR ", "noyo2");
    dict.SetValue("var", "noyo3");
    AssertExpandIs(tpl, &dict, "hi yoyo lo", true);

    // Sanity check string template behaviour while we're at it.
    Template* tpl2 = Template::StringToTemplate("hi {{VAR}} lo",
                                                STRIP_WHITESPACE);
    TemplateDictionary dict2("dict");
    AssertExpandIs(tpl2, &dict2, "hi  lo", true);
    dict2.SetValue("VAR", "yo");
    AssertExpandIs(tpl2, &dict2, "hi yo lo", true);
    dict2.SetValue("VAR", "yoyo");
    AssertExpandIs(tpl2, &dict2, "hi yoyo lo", true);
    dict2.SetValue("VA", "noyo");
    dict2.SetValue("VAR ", "noyo2");
    dict2.SetValue("var", "noyo3");
    AssertExpandIs(tpl2, &dict2, "hi yoyo lo", true);
    delete tpl2;   // You have to delete StringToTemplate strings
  }

  static void TestVariableWithModifiers() {
    Template* tpl = StringToTemplate("hi {{VAR:html_escape}} lo",
                                     STRIP_WHITESPACE);
    TemplateDictionary dict("dict");

    // Test with no modifiers.
    dict.SetValue("VAR", "yo");
    AssertExpandIs(tpl, &dict, "hi yo lo", true);
    dict.SetValue("VAR", "yo&yo");
    AssertExpandIs(tpl, &dict, "hi yo&amp;yo lo", true);

    // Test with URL escaping.
    tpl = StringToTemplate("<a href=\"/servlet?param={{VAR:u}}\">",
                           STRIP_WHITESPACE);
    AssertExpandIs(tpl, &dict, "<a href=\"/servlet?param=yo%26yo\">", true);
    tpl = StringToTemplate("<a href='/servlet?param={{VAR:url_query_escape}}'>",
                           STRIP_WHITESPACE);
    AssertExpandIs(tpl, &dict, "<a href='/servlet?param=yo%26yo'>", true);

    // Test with multiple URL escaping.
    tpl = StringToTemplate("<a href=\"/servlet?param={{VAR:u:u}}\">",
                           STRIP_WHITESPACE);
    AssertExpandIs(tpl, &dict, "<a href=\"/servlet?param=yo%2526yo\">", true);

    // Test HTML escaping.
    tpl = StringToTemplate("hi {{VAR:h}} lo", STRIP_WHITESPACE);
    AssertExpandIs(tpl, &dict, "hi yo&amp;yo lo", true);

    tpl = StringToTemplate("hi {{VAR:h:h}} lo", STRIP_WHITESPACE);
    AssertExpandIs(tpl, &dict, "hi yo&amp;amp;yo lo", true);

    // Test special HTML escaping
    dict.SetValue("URL_VAR", "javascript:void");
    dict.SetValue("SNIPPET_VAR", "<b>foo & bar</b>");
    tpl = StringToTemplate("hi {{VAR:H=attribute}} {{URL_VAR:H=url}} "
                           "{{SNIPPET_VAR:H=snippet}} lo", STRIP_WHITESPACE);
    AssertExpandIs(tpl, &dict, "hi yo_yo # <b>foo & bar</b> lo", true);

    // Test with custom modifiers [regular or XssSafe should not matter].
    ASSERT(GOOGLE_NAMESPACE::AddModifier("x-test",
                                  &GOOGLE_NAMESPACE::html_escape));
    ASSERT(GOOGLE_NAMESPACE::AddModifier("x-test-arg=",
                                  &GOOGLE_NAMESPACE::html_escape));
    ASSERT(GOOGLE_NAMESPACE::AddXssSafeModifier("x-test-arg=snippet",
                                         &GOOGLE_NAMESPACE::snippet_escape));

    tpl = StringToTemplate("hi {{VAR:x-test}} lo", STRIP_WHITESPACE);
    AssertExpandIs(tpl, &dict, "hi yo&amp;yo lo", true);
    tpl = StringToTemplate("hi {{SNIPPET_VAR:x-test-arg=snippet}} lo",
                           STRIP_WHITESPACE);
    AssertExpandIs(tpl, &dict, "hi <b>foo & bar</b> lo", true);
    tpl = StringToTemplate("hi {{VAR:x-unknown}} lo", STRIP_WHITESPACE);
    AssertExpandIs(tpl, &dict, "hi yo&yo lo", true);

    // Test with a modifier taking per-expand data
    DynamicModifier dynamic_modifier;
    ASSERT(GOOGLE_NAMESPACE::AddModifier("x-dynamic", &dynamic_modifier));
    PerExpandData per_expand_data;
    tpl = StringToTemplate("hi {{VAR:x-dynamic}} lo", STRIP_WHITESPACE);
    AssertExpandWithDataIs(tpl, &dict, &per_expand_data, "hi  lo", true);
    per_expand_data.InsertForModifiers("value", "foo");
    AssertExpandWithDataIs(tpl, &dict, &per_expand_data, "hi foo lo", true);
    per_expand_data.InsertForModifiers("value", "bar");
    AssertExpandWithDataIs(tpl, &dict, &per_expand_data, "hi bar lo", true);
    per_expand_data.InsertForModifiers("value", NULL);
    AssertExpandWithDataIs(tpl, &dict, &per_expand_data, "hi  lo", true);

    // Test with no modifiers.
    tpl = StringToTemplate("hi {{VAR}} lo", STRIP_WHITESPACE);
    AssertExpandIs(tpl, &dict, "hi yo&yo lo", true);

    // Check that ordering is right
    dict.SetValue("VAR", "yo\nyo");
    tpl = StringToTemplate("hi {{VAR:h}} lo", STRIP_WHITESPACE);
    AssertExpandIs(tpl, &dict, "hi yo yo lo", true);
    tpl = StringToTemplate("hi {{VAR:p}} lo", STRIP_WHITESPACE);
    AssertExpandIs(tpl, &dict, "hi yo\nyo lo", true);
    tpl = StringToTemplate("hi {{VAR:j}} lo", STRIP_WHITESPACE);
    AssertExpandIs(tpl, &dict, "hi yo\\nyo lo", true);
    tpl = StringToTemplate("hi {{VAR:h:j}} lo", STRIP_WHITESPACE);
    AssertExpandIs(tpl, &dict, "hi yo yo lo", true);
    tpl = StringToTemplate("hi {{VAR:j:h}} lo", STRIP_WHITESPACE);
    AssertExpandIs(tpl, &dict, "hi yo\\nyo lo", true);

    // Check more complicated modifiers using fullname
    tpl = StringToTemplate("hi {{VAR:javascript_escape:h}} lo",
                           STRIP_WHITESPACE);
    AssertExpandIs(tpl, &dict, "hi yo\\nyo lo", true);
    tpl = StringToTemplate("hi {{VAR:j:html_escape}} lo",
                           STRIP_WHITESPACE);
    AssertExpandIs(tpl, &dict, "hi yo\\nyo lo", true);
    tpl = StringToTemplate("hi {{VAR:pre_escape:j}} lo",
                           STRIP_WHITESPACE);
    AssertExpandIs(tpl, &dict, "hi yo\\nyo lo", true);

    // Check that illegal modifiers are rejected
    tpl = StringToTemplate("hi {{VAR:j:h2}} lo", STRIP_WHITESPACE);
    ASSERT(tpl == NULL);
    tpl = StringToTemplate("hi {{VAR:html_ecap}} lo", STRIP_WHITESPACE);
    ASSERT(tpl == NULL);
    tpl = StringToTemplate("hi {{VAR:javascript_escaper}} lo",
                           STRIP_WHITESPACE);
    ASSERT(tpl == NULL);
    tpl = StringToTemplate("hi {{VAR:js:j}} lo", STRIP_WHITESPACE);
    ASSERT(tpl == NULL);
    tpl = StringToTemplate("hi {{VAR:}} lo", STRIP_WHITESPACE);
    ASSERT(tpl == NULL);

    // Check we reject modifier-values when we ought to
    tpl = StringToTemplate("hi {{VAR:j=4}} lo", STRIP_WHITESPACE);
    ASSERT(tpl == NULL);
    tpl = StringToTemplate("hi {{VAR:html_escape=yes}} lo", STRIP_WHITESPACE);
    ASSERT(tpl == NULL);
    tpl = StringToTemplate("hi {{VAR:url_query_escape=wombats}} lo",
                           STRIP_WHITESPACE);
    ASSERT(tpl == NULL);

    // Check we don't allow modifiers on sections
    tpl = StringToTemplate("hi {{#VAR:h}} lo {{/VAR}}", STRIP_WHITESPACE);
    ASSERT(tpl == NULL);

    // Test when expanded grows by more than 12% per modifier.
    dict.SetValue("VAR", "http://a.com?b=c&d=e&f=g&q=a>b");
    tpl = StringToTemplate("{{VAR:u:j:h}}",
                           STRIP_WHITESPACE);
    AssertExpandIs(tpl, &dict,
                   "http%3A//a.com%3Fb%3Dc%26d%3De%26f%3Dg%26q%3Da%3Eb",
                   true);

    // As above with 4 modifiers.
    dict.SetValue("VAR", "http://a.com?b=c&d=e&f=g&q=a>b");
    tpl = StringToTemplate("{{VAR:u:j:h:h}}",
                           STRIP_WHITESPACE);
    AssertExpandIs(tpl, &dict,
                   "http%3A//a.com%3Fb%3Dc%26d%3De%26f%3Dg%26q%3Da%3Eb",
                   true);
  }

  static void TestSection() {
    Template* tpl = StringToTemplate(
        "boo!\nhi {{#SEC}}lo{{#SUBSEC}}jo{{/SUBSEC}}{{/SEC}} bar",
        STRIP_WHITESPACE);
    TemplateDictionary dict("dict");
    AssertExpandIs(tpl, &dict, "boo!hi  bar", true);
    dict.ShowSection("SEC");
    AssertExpandIs(tpl, &dict, "boo!hi lo bar", true);
    dict.ShowSection("SEC");
    AssertExpandIs(tpl, &dict, "boo!hi lo bar", true);
    // This should work even though subsec isn't a child of the main dict
    dict.ShowSection("SUBSEC");
    AssertExpandIs(tpl, &dict, "boo!hi lojo bar", true);

    TemplateDictionary dict2("dict2");
    dict2.AddSectionDictionary("SEC");
    AssertExpandIs(tpl, &dict2, "boo!hi lo bar", true);
    dict2.AddSectionDictionary("SEC");
    AssertExpandIs(tpl, &dict2, "boo!hi lolo bar", true);
    dict2.AddSectionDictionary("sec");
    AssertExpandIs(tpl, &dict2, "boo!hi lolo bar", true);
    dict2.ShowSection("SUBSEC");
    AssertExpandIs(tpl, &dict2, "boo!hi lojolojo bar", true);
  }

  static void TestSectionSeparator() {
    Template* tpl = StringToTemplate(
        "hi {{#SEC}}lo{{#SEC_separator}}jo{{JO}}{{/SEC_separator}}{{/SEC}} bar",
        STRIP_WHITESPACE);
    TemplateDictionary dict("dict");
    AssertExpandIs(tpl, &dict, "hi  bar", true);
    // Since SEC is only expanded once, the separator section shouldn't show.
    dict.ShowSection("SEC");
    AssertExpandIs(tpl, &dict, "hi lo bar", true);
    dict.ShowSection("SEC");
    AssertExpandIs(tpl, &dict, "hi lo bar", true);
    // This should work even though SEC_separator isn't a child of the
    // main dict.  It verifies SEC_separator is just a normal section, too.
    dict.ShowSection("SEC_separator");
    AssertExpandIs(tpl, &dict, "hi lojo bar", true);

    TemplateDictionary dict2("dict2");
    dict2.AddSectionDictionary("SEC");
    AssertExpandIs(tpl, &dict2, "hi lo bar", true);
    dict2.AddSectionDictionary("SEC");
    AssertExpandIs(tpl, &dict2, "hi lojolo bar", true);
    // This is a weird case: using separator and specifying manually.
    dict2.ShowSection("SEC_separator");
    AssertExpandIs(tpl, &dict2, "hi lojojolojo bar", true);

    TemplateDictionary dict3("dict3");
    TemplateDictionary* sec1 = dict3.AddSectionDictionary("SEC");
    TemplateDictionary* sec2 = dict3.AddSectionDictionary("SEC");
    TemplateDictionary* sec3 = dict3.AddSectionDictionary("SEC");
    dict3.SetValue("JO", "J");
    AssertExpandIs(tpl, &dict3, "hi lojoJlojoJlo bar", true);
    sec1->SetValue("JO", "JO");
    AssertExpandIs(tpl, &dict3, "hi lojoJOlojoJlo bar", true);
    sec2->SetValue("JO", "JOO");
    AssertExpandIs(tpl, &dict3, "hi lojoJOlojoJOOlo bar", true);
    dict3.AddSectionDictionary("SEC");
    AssertExpandIs(tpl, &dict3, "hi lojoJOlojoJOOlojoJlo bar", true);
    sec3->AddSectionDictionary("SEC_separator");
    AssertExpandIs(tpl, &dict3, "hi lojoJOlojoJOOlojoJjoJlo bar", true);

    // Make sure we don't do anything special with var or include names
    Template* tpl2 = StringToTemplate(
        "hi {{#SEC}}lo{{>SEC_separator}}{{/SEC}} bar",
        STRIP_WHITESPACE);
    AssertExpandIs(tpl2, &dict2, "hi lolo bar", true);

    Template* tpl3 = StringToTemplate(
        "hi {{#SEC}}lo{{SEC_separator}}{{/SEC}} bar",
        STRIP_WHITESPACE);
    dict2.SetValue("SEC_separator", "-");
    AssertExpandIs(tpl3, &dict2, "hi lo-lo- bar", true);
  }

  static void TestInclude() {
    string incname = StringToTemplateFile("include file\n");
    string incname2 = StringToTemplateFile("inc2a\ninc2b\n");
    string incname_bad = StringToTemplateFile("{{syntax_error");
    Template* tpl = StringToTemplate("hi {{>INC}} bar\n", STRIP_WHITESPACE);
    TemplateDictionary dict("dict");
    AssertExpandIs(tpl, &dict, "hi  bar", true);
    dict.AddIncludeDictionary("INC");
    AssertExpandIs(tpl, &dict, "hi  bar", true);   // noop: no filename was set
    dict.AddIncludeDictionary("INC")->SetFilename("/notarealfile ");
    AssertExpandIs(tpl, &dict, "hi  bar", false);   // noop: illegal filename
    dict.AddIncludeDictionary("INC")->SetFilename(incname);
    AssertExpandIs(tpl, &dict, "hi include file bar", false);
    dict.AddIncludeDictionary("INC")->SetFilename(incname_bad);
    AssertExpandIs(tpl, &dict, "hi include file bar",
                   false);  // noop: syntax error
    dict.AddIncludeDictionary("INC")->SetFilename(incname);
    AssertExpandIs(tpl, &dict, "hi include fileinclude file bar", false);
    dict.AddIncludeDictionary("inc")->SetFilename(incname);
    AssertExpandIs(tpl, &dict, "hi include fileinclude file bar", false);
    dict.AddIncludeDictionary("INC")->SetFilename(incname2);
    AssertExpandIs(tpl, &dict,
                   "hi include fileinclude fileinc2ainc2b bar", false);

    // Now test that includes preserve Strip
    Template* tpl2 = StringToTemplate("hi {{>INC}} bar", DO_NOT_STRIP);
    AssertExpandIs(tpl2, &dict,
                   "hi include file\ninclude file\ninc2a\ninc2b\n bar", false);

    // Test that if we indent the include, every line on the include
    // is indented.
    Template* tpl3 = StringToTemplate("hi\n  {{>INC}} bar", DO_NOT_STRIP);
    AssertExpandIs(tpl3, &dict,
                   "hi\n  include file\n  include file\n"
                   "  inc2a\n  inc2b\n   bar",
                   false);
    // But obviously, if we strip leading whitespace, no indentation.
    Template* tpl4 = StringToTemplate("hi\n  {{>INC}} bar", STRIP_WHITESPACE);
    AssertExpandIs(tpl4, &dict,
                   "hiinclude fileinclude fileinc2ainc2b bar", false);
    // And if it's not a whitespace indent, we don't indent either.
    Template* tpl5 = StringToTemplate("hi\n - {{>INC}} bar", DO_NOT_STRIP);
    AssertExpandIs(tpl5, &dict,
                   "hi\n - include file\ninclude file\n"
                   "inc2a\ninc2b\n bar",
                   false);
    // Make sure we indent properly at the beginning.
    Template* tpl6 = StringToTemplate("  {{>INC}}\nbar", DO_NOT_STRIP);
    AssertExpandIs(tpl6, &dict,
                   "  include file\n  include file\n"
                   "  inc2a\n  inc2b\n  \nbar",
                   false);
    // And deal correctly when we include twice in a row.
    Template* tpl7 = StringToTemplate("  {{>INC}}-{{>INC}}", DO_NOT_STRIP);
    AssertExpandIs(tpl7, &dict,
                   "  include file\n  include file\n  inc2a\n  inc2b\n  "
                   "-include file\ninclude file\ninc2a\ninc2b\n",
                   false);
  }

  static void TestIncludeWithModifiers() {
    string incname = StringToTemplateFile("include & print file\n");
    string incname2 = StringToTemplateFile("inc2\n");
    string incname3 = StringToTemplateFile("yo&yo");
    // Note this also tests that html-escape, but not javascript-escape or
    // pre-escape, escapes \n to <space>
    Template* tpl1 = StringToTemplate("hi {{>INC:h}} bar\n", DO_NOT_STRIP);
    Template* tpl2 = StringToTemplate("hi {{>INC:javascript_escape}} bar\n",
                                      DO_NOT_STRIP);
    Template* tpl3 = StringToTemplate("hi {{>INC:pre_escape}} bar\n",
                                      DO_NOT_STRIP);
    Template* tpl4 = StringToTemplate("hi {{>INC:u}} bar\n", DO_NOT_STRIP);
    // Test that if we include the same template twice, once with a modifer
    // and once without, they each get applied properly.
    Template* tpl5 = StringToTemplate("hi {{>INC:h}} bar {{>INC}} baz\n",
                                      DO_NOT_STRIP);

    TemplateDictionary dict("dict");
    AssertExpandIs(tpl1, &dict, "hi  bar\n", true);
    dict.AddIncludeDictionary("INC")->SetFilename(incname);
    AssertExpandIs(tpl1, &dict, "hi include &amp; print file  bar\n", true);
    dict.AddIncludeDictionary("INC")->SetFilename(incname2);
    AssertExpandIs(tpl1, &dict, "hi include &amp; print file inc2  bar\n",
                   true);
    AssertExpandIs(tpl2, &dict, "hi include \\x26 print file\\ninc2\\n bar\n",
                   true);
    AssertExpandIs(tpl3, &dict, "hi include &amp; print file\ninc2\n bar\n",
                   true);
    dict.AddIncludeDictionary("INC")->SetFilename(incname3);
    AssertExpandIs(tpl4, &dict,
                   "hi include+%26+print+file%0Ainc2%0Ayo%26yo bar\n",
                   true);
    AssertExpandIs(tpl5, &dict,
                   "hi include &amp; print file inc2 yo&amp;yo bar "
                   "include & print file\ninc2\nyo&yo baz\n",
                   true);

    // Don't test modifier syntax here; that's in TestVariableWithModifiers()
  }

  // Make sure we don't deadlock when a template includes itself.
  // This also tests we handle recursive indentation properly.
  static void TestRecursiveInclude() {
    string incname = StringToTemplateFile("hi {{>INC}} bar\n  {{>INC}}!");
    Template* tpl = Template::GetTemplate(incname, DO_NOT_STRIP);
    TemplateDictionary dict("dict");
    dict.AddIncludeDictionary("INC")->SetFilename(incname);
    // Note the last line is indented 4 spaces instead of 2.  This is
    // because the last sub-include is indented.
    AssertExpandIs(tpl, &dict, "hi hi  bar\n  ! bar\n  hi  bar\n    !!", true);
  }

  // Tests that vars inherit/override their parents properly
  static void TestInheritence() {
    Template* tpl = StringToTemplate("{{FOO}}{{#SEC}}{{FOO}}{{#SEC}}{{FOO}}{{/SEC}}{{/SEC}}",
                                     STRIP_WHITESPACE);
    TemplateDictionary dict("dict");
    dict.SetValue("FOO", "foo");
    dict.ShowSection("SEC");
    AssertExpandIs(tpl, &dict, "foofoofoo", true);

    TemplateDictionary dict2("dict2");
    dict2.SetValue("FOO", "foo");
    TemplateDictionary* sec = dict2.AddSectionDictionary("SEC");
    AssertExpandIs(tpl, &dict2, "foofoofoo", true);
    sec->SetValue("FOO", "bar");
    AssertExpandIs(tpl, &dict2, "foobarbar", true);
    TemplateDictionary* sec2 = sec->AddSectionDictionary("SEC");
    AssertExpandIs(tpl, &dict2, "foobarbar", true);
    sec2->SetValue("FOO", "baz");
    AssertExpandIs(tpl, &dict2, "foobarbaz", true);

    // Now test an include template, which shouldn't inherit from its parents
    tpl = StringToTemplate("{{FOO}}{{#SEC}}hi{{/SEC}}\n{{>INC}}",
                           STRIP_WHITESPACE);
    string incname = StringToTemplateFile(
        "include {{FOO}}{{#SEC}}invisible{{/SEC}}file\n");
    TemplateDictionary incdict("dict");
    incdict.ShowSection("SEC");
    incdict.SetValue("FOO", "foo");
    incdict.AddIncludeDictionary("INC")->SetFilename(incname);
    AssertExpandIs(tpl, &incdict, "foohiinclude file", true);
  }

  static void TestTemplateString() {
    // Make sure using TemplateString and StaticTemplateString for the
    // dictionary expands the same as using char*'s.
    Template* tpl = StringToTemplate("hi {{VAR}} lo", STRIP_WHITESPACE);
    TemplateDictionary dict("dict");
    dict.SetValue("VAR", TemplateString("short-lived", strlen("short")));
    AssertExpandIs(tpl, &dict, "hi short lo", true);
    dict.SetValue("VAR", kHello);
    AssertExpandIs(tpl, &dict, "hi Hello lo", true);
  }

  // Tests that we append to the output string, rather than overwrite
  static void TestExpand() {
    Template* tpl = StringToTemplate("hi", STRIP_WHITESPACE);
    TemplateDictionary dict("test_expand");
    string output("premade");
    ASSERT(tpl->Expand(&output, &dict));
    ASSERT_STREQ(output.c_str(), "premadehi");

    tpl = StringToTemplate("   lo   ", STRIP_WHITESPACE);
    ASSERT(tpl->Expand(&output, &dict));
    ASSERT_STREQ(output.c_str(), "premadehilo");
  }

  static void TestExpandWithCustomEmitter() {
    Template* tpl = StringToTemplate("{{VAR}} {{VAR}}", STRIP_WHITESPACE);
    TemplateDictionary dict("test_expand");
    dict.SetValue("VAR", "this song is just six words long");
    string output;
    SizeofEmitter e(&output);
    ASSERT(tpl->Expand(&e, &dict));
    ASSERT_STREQ("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
                 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
                 output.c_str());
  }

  // Tests annotation, in particular inheriting annotation among children
  // This should be called first, so the filenames don't change as we add
  // more tests.
  static void TestAnnotation() {
    string incname = StringToTemplateFile("include {{#ISEC}}file{{/ISEC}}\n");
    string incname2 = StringToTemplateFile("include #2\n");
    Template* tpl = StringToTemplate(
        "boo!\n{{>INC}}\nhi {{#SEC}}lo{{#SUBSEC}}jo{{/SUBSEC}}{{/SEC}} bar "
        "{{VAR:x-foo}}",
        DO_NOT_STRIP);
    TemplateDictionary dict("dict");
    PerExpandData per_expand_data;

    dict.ShowSection("SEC");
    TemplateDictionary* incdict = dict.AddIncludeDictionary("INC");
    incdict->SetFilename(incname);
    incdict->ShowSection("ISEC");
    dict.AddIncludeDictionary("INC")->SetFilename(incname2);
    dict.SetValue("VAR", "var");

    // This string is equivalent to "/template." (at least on unix)
    string slash_tpl(GOOGLE_NAMESPACE::PathJoin(GOOGLE_NAMESPACE::kRootdir, "template."));
    per_expand_data.SetAnnotateOutput("");
    char expected[10240];           // 10k should be big enough!
    snprintf(expected, sizeof(expected),
             "{{#FILE=%s003}}{{#SEC=__{{MAIN}}__}}boo!\n"
             "{{#INC=INC}}{{#FILE=%s001}}"
             "{{#SEC=__{{MAIN}}__}}include {{#SEC=ISEC}}file{{/SEC}}\n"
             "{{/SEC}}{{/FILE}}{{/INC}}"
             "{{#INC=INC}}{{#FILE=%s002}}"
             "{{#SEC=__{{MAIN}}__}}include #2\n{{/SEC}}{{/FILE}}{{/INC}}"
             "\nhi {{#SEC=SEC}}lo{{/SEC}} bar "
             "{{#VAR=VAR:x-foo<not registered>}}var{{/VAR}}{{/SEC}}{{/FILE}}",
             (FLAGS_test_tmpdir + slash_tpl).c_str(),
             (FLAGS_test_tmpdir + slash_tpl).c_str(),
             (FLAGS_test_tmpdir + slash_tpl).c_str());
    AssertExpandWithDataIs(tpl, &dict, &per_expand_data, expected, true);

    // Test ability to set custom annotator.
    CustomTestAnnotator custom_annotator;
    per_expand_data.SetAnnotator(&custom_annotator);
    snprintf(expected, sizeof(expected),
             "{{EVENT=1}}{{#FILE=%s003}}"
             "{{EVENT=2}}{{#SEC=__{{MAIN}}__}}boo!\n"
             "{{EVENT=3}}{{#INC=INC}}"
             "{{EVENT=4}}{{#FILE=%s001}}"
             "{{EVENT=5}}{{#SEC=__{{MAIN}}__}}include "
             "{{EVENT=6}}{{#SEC=ISEC}}file"
             "{{EVENT=7}}{{/SEC}}\n"
             "{{EVENT=8}}{{/SEC}}"
             "{{EVENT=9}}{{/FILE}}"
             "{{EVENT=10}}{{/INC}}"
             "{{EVENT=11}}{{#INC=INC}}"
             "{{EVENT=12}}{{#FILE=%s002}}"
             "{{EVENT=13}}{{#SEC=__{{MAIN}}__}}include #2\n"
             "{{EVENT=14}}{{/SEC}}"
             "{{EVENT=15}}{{/FILE}}"
             "{{EVENT=16}}{{/INC}}\nhi "
             "{{EVENT=17}}{{#SEC=SEC}}lo"
             "{{EVENT=18}}{{/SEC}} bar "
             "{{EVENT=19}}{{#VAR=VAR:x-foo<not registered>}}var"
             "{{EVENT=20}}{{/VAR}}"
             "{{EVENT=21}}{{/SEC}}"
             "{{EVENT=22}}{{/FILE}}",
             (FLAGS_test_tmpdir + slash_tpl).c_str(),
             (FLAGS_test_tmpdir + slash_tpl).c_str(),
             (FLAGS_test_tmpdir + slash_tpl).c_str());
    // We can't use AssertExpandWithDataIs() on our deliberately stateful
    // test annotator because it internally does a second expansion
    // assuming no state change between calls.
    string custom_outstring;
    ASSERT(tpl->ExpandWithData(&custom_outstring, &dict, &per_expand_data));
    ASSERT_STREQ(custom_outstring.c_str(), expected);

    // Unset annotator and continue with next test as test of ability
    // to revert to built-in annotator.
    per_expand_data.SetAnnotator(NULL);

    per_expand_data.SetAnnotateOutput(slash_tpl.c_str());
    snprintf(expected, sizeof(expected),
             "{{#FILE=%s003}}{{#SEC=__{{MAIN}}__}}boo!\n"
             "{{#INC=INC}}{{#FILE=%s001}}"
             "{{#SEC=__{{MAIN}}__}}include {{#SEC=ISEC}}file{{/SEC}}\n"
             "{{/SEC}}{{/FILE}}{{/INC}}"
             "{{#INC=INC}}{{#FILE=%s002}}"
             "{{#SEC=__{{MAIN}}__}}include #2\n{{/SEC}}{{/FILE}}{{/INC}}"
             "\nhi {{#SEC=SEC}}lo{{/SEC}} bar "
             "{{#VAR=VAR:x-foo<not registered>}}var{{/VAR}}{{/SEC}}{{/FILE}}",
             (slash_tpl).c_str(),
             (slash_tpl).c_str(),
             (slash_tpl).c_str());
    AssertExpandWithDataIs(tpl, &dict, &per_expand_data, expected, true);

    per_expand_data.SetAnnotateOutput(NULL);   // should turn off annotations
    AssertExpandWithDataIs(tpl, &dict, &per_expand_data,
                           "boo!\ninclude file\ninclude #2\n\nhi lo bar var",
                           true);

    // Test that even if we set an annotator we shouldn't get annotation
    // if it is not turned on with SetAnnotateOutput().
    per_expand_data.SetAnnotator(&custom_annotator);
    AssertExpandWithDataIs(tpl, &dict, &per_expand_data,
                           "boo!\ninclude file\ninclude #2\n\nhi lo bar var",
                           true);

    // Test annotation of "missing include" condition.
    Template* one_inc_tpl =
        StringToTemplate("File contents: {{>INC}}\n", DO_NOT_STRIP);
    TemplateDictionary dict_missing_file("dict_with_missing_file");
    dict_missing_file.AddIncludeDictionary("INC")->SetFilename("missing.tpl");

    per_expand_data.SetAnnotateOutput("");
    per_expand_data.SetAnnotator(NULL);
    snprintf(expected, sizeof(expected),
             "{{#FILE=%s004}}{{#SEC=__{{MAIN}}__}}File contents: "
             "{{#MISSING_INC=INC}}missing.tpl{{/MISSING_INC}}\n"
             "{{/SEC}}{{/FILE}}",
             (FLAGS_test_tmpdir + slash_tpl).c_str());
    // We expect a false return value because of the missing file.
    AssertExpandWithDataIs(one_inc_tpl, &dict_missing_file, &per_expand_data,
                           expected, false);

    // Same missing include test with custom annotator
    custom_annotator.Reset();
    per_expand_data.SetAnnotator(&custom_annotator);
    snprintf(expected, sizeof(expected),
             "{{EVENT=1}}{{#FILE=%s004}}"
             "{{EVENT=2}}{{#SEC=__{{MAIN}}__}}File contents: "
             "{{EVENT=3}}{{#MISSING_INC=INC}}missing.tpl"
             "{{EVENT=4}}{{/MISSING_INC}}\n"
             "{{EVENT=5}}{{/SEC}}"
             "{{EVENT=6}}{{/FILE}}",
             (FLAGS_test_tmpdir + slash_tpl).c_str());
    // See comment above on why we can't use AssertExpandWithDataIs() for
    // our stateful test annotator.
    custom_outstring.clear();
    ASSERT(!one_inc_tpl->ExpandWithData(&custom_outstring,
                                        &dict_missing_file,
                                        &per_expand_data));
    ASSERT_STREQ(custom_outstring.c_str(), expected);
  }

  static void TestTemplateExpansionModifier() {
    string parent_tpl_name = StringToTemplateFile("before {{>INC}} after");
    string child_tpl_name1 = StringToTemplateFile("child1");
    string child_tpl_name2 = StringToTemplateFile("child2");
    Template* tpl = Template::GetTemplate(parent_tpl_name, DO_NOT_STRIP);

    TemplateDictionary dict("parent dict");
    dict.AddIncludeDictionary("INC")->SetFilename(child_tpl_name1);
    dict.AddIncludeDictionary("INC")->SetFilename(child_tpl_name2);

    PerExpandData per_expand_data;

    EmphasizeTemplateModifier modifier1(child_tpl_name1);
    per_expand_data.SetTemplateExpansionModifier(&modifier1);
    AssertExpandWithDataIs(tpl, &dict, &per_expand_data,
                           "before >>child1<<child2 after", true);

    EmphasizeTemplateModifier modifier2(child_tpl_name2);
    per_expand_data.SetTemplateExpansionModifier(&modifier2);
    AssertExpandWithDataIs(tpl, &dict, &per_expand_data,
                           "before child1>>child2<< after", true);

    EmphasizeTemplateModifier modifier3(parent_tpl_name);
    per_expand_data.SetTemplateExpansionModifier(&modifier3);
    AssertExpandWithDataIs(tpl, &dict, &per_expand_data,
                           ">>before child1child2 after<<", true);

    per_expand_data.SetTemplateExpansionModifier(NULL);
    AssertExpandWithDataIs(tpl, &dict, &per_expand_data,
                           "before child1child2 after", true);
  }

  static void TestGetTemplate() {
    // Tests the cache
    string filename = StringToTemplateFile("{This is perfectly valid} yay!");
    Template* tpl1 = Template::GetTemplate(filename, DO_NOT_STRIP);
    Template* tpl2 = Template::GetTemplate(filename.c_str(), DO_NOT_STRIP);
    Template* tpl3 = Template::GetTemplate(filename, STRIP_WHITESPACE);
    ASSERT(tpl1 && tpl2 && tpl3);
    ASSERT(tpl1 == tpl2);
    ASSERT(tpl1 != tpl3);

    // Tests that a nonexistent template returns NULL
    Template* tpl4 = Template::GetTemplate("/yakakak", STRIP_WHITESPACE);
    ASSERT(!tpl4);

    // Tests that syntax errors cause us to return NULL
    Template* tpl5 = StringToTemplate("{{This has spaces in it}}", DO_NOT_STRIP);
    ASSERT(!tpl5);
    Template* tpl6 = StringToTemplate("{{#SEC}}foo", DO_NOT_STRIP);
    ASSERT(!tpl6);
    Template* tpl7 = StringToTemplate("{{#S1}}foo{{/S2}}", DO_NOT_STRIP);
    ASSERT(!tpl7);
    Template* tpl8 = StringToTemplate("{{#S1}}foo{{#S2}}bar{{/S1}{{/S2}",
                                      DO_NOT_STRIP);
    ASSERT(!tpl8);
    Template* tpl9 = StringToTemplate("{{noend", DO_NOT_STRIP);
    ASSERT(!tpl9);
  }

  static void TestStringCacheKey() {
    // If you use these same cache keys somewhere else,
    // call Template::ClearCache first.
    const string cache_key_a = "cache key a";
    const string text = "Test template 1";
    TemplateDictionary empty_dict("dict");

    // When a string template is registered via StringToTemplateCache,
    // we can use GetTemplate for that same cache-key under any other
    // Strip because we cache the contents.
    Template *tpl1, *tpl2;
    ASSERT(Template::StringToTemplateCache(cache_key_a, text));
    tpl1 = Template::GetTemplate(cache_key_a, DO_NOT_STRIP);
    AssertExpandIs(tpl1, &empty_dict, text, true);

    // Different strip.
    ASSERT(tpl2 = Template::GetTemplate(cache_key_a, STRIP_BLANK_LINES));
    ASSERT(tpl2 != tpl1);
    AssertExpandIs(tpl2, &empty_dict, text, true);

    Template::ClearCache();
  }

  static void TestStringGetTemplate() {
    TemplateDictionary dict("dict");

    // Test cache lookups
    const char* const tpltext = "{This is perfectly valid} yay!";
    ASSERT(Template::StringToTemplateCache("tgt", tpltext));

    Template* tpl1 = Template::GetTemplate("tgt", DO_NOT_STRIP);
    Template* tpl2 = Template::GetTemplate("tgt", STRIP_WHITESPACE);
    ASSERT(tpl1 && tpl2);
    ASSERT(tpl1 != tpl2);
    AssertExpandIs(tpl1, &dict, tpltext, true);
    AssertExpandIs(tpl2, &dict, tpltext, true);

    // If we register a new string under the same text, it should be
    // ignored.
    ASSERT(!Template::StringToTemplateCache("tgt", tpltext));
    ASSERT(!Template::StringToTemplateCache("tgt", "new text"));
    Template* tpl3 = Template::GetTemplate("tgt", DO_NOT_STRIP);
    ASSERT(tpl3 == tpl1);
    AssertExpandIs(tpl3, &dict, tpltext, true);

    // Tests that syntax errors cause us to return NULL
    ASSERT(!Template::StringToTemplateCache("tgt2", "{{This has spaces}}"));
    ASSERT(!Template::StringToTemplateCache("tgt3", "{{#SEC}}foo"));
    ASSERT(!Template::StringToTemplateCache("tgt4", "{{#S1}}foo{{/S2}}"));
    ASSERT(!Template::StringToTemplateCache("tgt5",
                                            "{{#S1}}foo{{#S2}}bar{{/S1}{{/S2}"));
    ASSERT(!Template::StringToTemplateCache("tgt6", "{{noend"));
    // And that we didn't cache them by mistake
    ASSERT(!Template::GetTemplate("tgt2", STRIP_WHITESPACE));

    Template::ClearCache();
  }

  static void TestStringTemplateInclude() {
    Template::ClearCache();   // just for exercise.
    const string cache_key = "TestStringTemplateInclude";
    const string cache_key_inc = "TestStringTemplateInclude-inc";
    const string cache_key_indent = "TestStringTemplateInclude-indent";
    const string text = "<html>{{>INC}}</html>";
    const string text_inc = "<div>\n<p>\nUser {{USER}}\n</div>";
    const string text_indent = "<html>\n  {{>INC}}</html>";

    ASSERT(Template::StringToTemplateCache(cache_key, text));
    ASSERT(Template::StringToTemplateCache(cache_key_inc, text_inc));
    ASSERT(Template::StringToTemplateCache(cache_key_indent, text_indent));

    Template *tpl = Template::GetTemplate(cache_key, DO_NOT_STRIP);
    ASSERT(tpl);

    TemplateDictionary dict("dict");
    TemplateDictionary* sub_dict = dict.AddIncludeDictionary("INC");
    sub_dict->SetFilename(cache_key_inc);

    sub_dict->SetValue("USER", "John<>Doe");
    string expected = "<html><div>\n<p>\nUser John<>Doe\n</div></html>";
    AssertExpandIs(tpl, &dict, expected, true);

    // Repeat the same except that now the parent has a template-level
    // directive (by way of the automatic-line-indenter).
    tpl = Template::GetTemplate(cache_key_indent, DO_NOT_STRIP);
    ASSERT(tpl);
    expected =
        "<html>\n"
        "  <div>\n"
        "  <p>\n"
        "  User John<>Doe\n"
        "  </div>"
        "</html>";
    AssertExpandIs(tpl, &dict, expected, true);

    Template::ClearCache();
  }

  static void TestTemplateSearchPath() {
    const string pathA = ctemplate::PathJoin(FLAGS_test_tmpdir, "a/");
    const string pathB = ctemplate::PathJoin(FLAGS_test_tmpdir, "b/");

    TemplateDictionary dict("");
    Template::SetTemplateRootDirectory(pathA);
    Template::AddAlternateTemplateRootDirectory(pathB);

    // 1. Show that a template in the secondary path can be found.
    const string path_b_bar = ctemplate::PathJoin(pathB, "bar");
    StringToFile("b/bar", path_b_bar);
    ASSERT_STREQ(path_b_bar.c_str(),
                 Template::FindTemplateFilename("bar").c_str());
    Template* b_bar = Template::GetTemplate("bar", DO_NOT_STRIP);
    ASSERT(b_bar);
    AssertExpandIs(b_bar, &dict, "b/bar", true);

    // 2. Show that the search stops once the first match is found.
    //    Create two templates in separate directories with the same name.
    const string path_a_foo = ctemplate::PathJoin(pathA, "foo");
    StringToFile("a/foo", path_a_foo);
    StringToFile("b/foo", ctemplate::PathJoin(pathB, "foo"));
    ASSERT_STREQ(path_a_foo.c_str(),
                 Template::FindTemplateFilename("foo").c_str());
    Template* a_foo = Template::GetTemplate("foo", DO_NOT_STRIP);
    ASSERT(a_foo);
    AssertExpandIs(a_foo, &dict, "a/foo", true);

    // 3. Show that attempting to find a non-existent template gives an
    //    empty path.
    ASSERT(Template::FindTemplateFilename("baz").empty());
  }

  static void TestRemoveStringFromTemplateCache() {
    Template::ClearCache();   // just for exercise.
    const string cache_key = "TestRemoveStringFromTemplateCache";
    const string text = "<html>here today...</html>";

    TemplateDictionary dict("test");
    ASSERT(Template::StringToTemplateCache(cache_key, text));
    Template* tpl = Template::GetTemplate(cache_key, DO_NOT_STRIP);
    ASSERT(tpl);
    AssertExpandIs(tpl, &dict, text, true);
    tpl = Template::GetTemplate(cache_key, STRIP_WHITESPACE);
    ASSERT(tpl);
    AssertExpandIs(tpl, &dict, text, true);

    Template::RemoveStringFromTemplateCache(cache_key);
    tpl = Template::GetTemplate(cache_key, DO_NOT_STRIP);
    ASSERT(!tpl);
    tpl = Template::GetTemplate(cache_key, STRIP_WHITESPACE);
    ASSERT(!tpl);
    tpl = Template::GetTemplate(cache_key, STRIP_BLANK_LINES);
    ASSERT(!tpl);
  }

  static void TestTemplateCache() {
    const string filename_a = StringToTemplateFile("Test template 1");
    const string filename_b = StringToTemplateFile("Test template 2.");

    Template *tpl, *tpl2;
    ASSERT(tpl = Template::GetTemplate(filename_a, DO_NOT_STRIP));

    ASSERT(tpl2 = Template::GetTemplate(filename_b, DO_NOT_STRIP));
    ASSERT(tpl2 != tpl);  // different filenames.
    ASSERT(tpl2 = Template::GetTemplate(filename_a, STRIP_BLANK_LINES));
    ASSERT(tpl2 != tpl);  // different strip.
    ASSERT(tpl2 = Template::GetTemplate(filename_b, STRIP_BLANK_LINES));
    ASSERT(tpl2 != tpl);  // different filenames and strip.
    ASSERT(tpl2 = Template::GetTemplate(filename_a, DO_NOT_STRIP));
    ASSERT(tpl2 == tpl);  // same filename and strip.
  }

  // Tests that the various strip values all do the expected thing.
  static void TestStrip() {
    TemplateDictionary dict("dict");
    dict.SetValue("FOO", "foo");

    const char* tests[][4] = {  // 0: in, 1: do-not-strip, 2: blanklines, 3: ws
      {"hi!\n", "hi!\n", "hi!\n", "hi!"},
      {"hi!", "hi!", "hi!", "hi!"},
      // These test strip-blank-lines, primarily
      {"{{FOO}}\n\n{{FOO}}", "foo\n\nfoo", "foo\nfoo", "foofoo"},
      {"{{FOO}}\r\n\r\n{{FOO}}", "foo\r\n\r\nfoo", "foo\r\nfoo", "foofoo"},
      {"{{FOO}}\n   \n{{FOO}}\n", "foo\n   \nfoo\n", "foo\nfoo\n", "foofoo"},
      {"{{FOO}}\n{{BI_NEWLINE}}\nb", "foo\n\n\nb", "foo\n\n\nb", "foo\nb"},
      {"{{FOO}}\n{{!comment}}\nb", "foo\n\nb", "foo\nb", "foob"},
      {"{{FOO}}\n{{!comment}}{{!comment2}}\nb", "foo\n\nb", "foo\n\nb", "foob"},
      {"{{FOO}}\n{{>ONE_INC}}\nb", "foo\n\nb", "foo\nb", "foob"},
      {"{{FOO}}\n\t{{>ONE_INC}}  \nb", "foo\n\t  \nb", "foo\nb", "foob"},
      {"{{FOO}}\n{{>ONE_INC}}{{>TWO_INC}}\nb", "foo\n\nb", "foo\n\nb", "foob"},
      {"{{FOO}}\n  {{#SEC}}\ntext \n  {{/SEC}}\n", "foo\n  \n", "foo\n", "foo"},
      {"{{%AUTOESCAPE context=\"HTML\"}}\nBLA", "\nBLA", "BLA", "BLA"},
      // These test strip-whitespace
      {"foo\nbar\n", "foo\nbar\n", "foo\nbar\n", "foobar"},
      {"{{FOO}}\nbar\n", "foo\nbar\n", "foo\nbar\n", "foobar"},
      {"  {{FOO}}  {{!comment}}\nb", "  foo  \nb", "  foo  \nb", "foo  b"},
      {"  {{FOO}}  {{BI_SPACE}}\n", "  foo   \n", "  foo   \n", "foo   "},
      {"  \t \f\v  \n\r\n  ", "  \t \f\v  \n\r\n  ", "", ""},
    };

    for (int i = 0; i < sizeof(tests)/sizeof(*tests); ++i) {
      Template* tpl1 = StringToTemplate(tests[i][0], DO_NOT_STRIP);
      Template* tpl2 = StringToTemplate(tests[i][0], STRIP_BLANK_LINES);
      Template* tpl3 = StringToTemplate(tests[i][0], STRIP_WHITESPACE);
      AssertExpandIs(tpl1, &dict, tests[i][1], true);
      AssertExpandIs(tpl2, &dict, tests[i][2], true);
      AssertExpandIs(tpl3, &dict, tests[i][3], true);
    }
  }

  // Tests both static and non-static versions
  static void TestReloadIfChanged() {
    TemplateDictionary dict("empty");

    string filename = StringToTemplateFile("{valid template}");
    string nonexistent = StringToTemplateFile("dummy");
    unlink(nonexistent.c_str());

    Template* tpl = Template::GetTemplate(filename, STRIP_WHITESPACE);
    assert(tpl);
    Template* tpl2 = Template::GetTemplate(nonexistent, STRIP_WHITESPACE);
    assert(!tpl2);

    // TODO(csilvers): figure out how to mock time, rather than sleeping
    sleep(1);    // since mtime goes by 1-second increments
    ASSERT(!tpl->ReloadIfChanged());  // false: no reloading needed
    StringToFile("{valid template}", filename);   // no contentful change
    sleep(1);
    ASSERT(tpl->ReloadIfChanged());   // true: change, even if not contentful
    tpl = Template::GetTemplate(filename, STRIP_WHITESPACE);  // needed
    AssertExpandIs(tpl, &dict, "{valid template}", true);

    StringToFile("exists now!", nonexistent);
    tpl2 = Template::GetTemplate(nonexistent, STRIP_WHITESPACE);
    ASSERT(!tpl2);                    // because it's cached as not existing
    Template::ReloadAllIfChanged();
    tpl = Template::GetTemplate(filename, STRIP_WHITESPACE);  // force the reload
    tpl2 = Template::GetTemplate(nonexistent, STRIP_WHITESPACE);
    ASSERT(tpl2);                     // file exists now
    ASSERT(!tpl2->ReloadIfChanged()); // false: file hasn't changed
    sleep(1);
    ASSERT(!tpl2->ReloadIfChanged()); // false: file *still* hasn't changed

    unlink(nonexistent.c_str());      // here today...
    sleep(1);
    ASSERT(!tpl2->ReloadIfChanged()); // false: file has disappeared
    // The old template content should be forgotten
    ASSERT(NULL == Template::GetTemplate(nonexistent, STRIP_WHITESPACE));

    StringToFile("lazarus", nonexistent);
    sleep(1);
    ASSERT(tpl2->ReloadIfChanged());  // true: file exists again

    tpl2 = Template::GetTemplate(nonexistent, STRIP_WHITESPACE);
    AssertExpandIs(tpl2, &dict, "lazarus", true);
    StringToFile("{new template}", filename);
    tpl = Template::GetTemplate(filename, STRIP_WHITESPACE);  // needed
    AssertExpandIs(tpl, &dict, "{valid template}", true);   // haven't reloaded
    sleep(1);
    ASSERT(tpl->ReloadIfChanged());   // true: change, even if not contentful
    tpl = Template::GetTemplate(filename, STRIP_WHITESPACE);  // needed
    AssertExpandIs(tpl, &dict, "{new template}", true);

    // Now change both tpl and tpl2
    StringToFile("{all-changed}", filename);
    StringToFile("lazarus2", nonexistent);
    Template::ReloadAllIfChanged();
    tpl = Template::GetTemplate(filename, STRIP_WHITESPACE);  // needed
    tpl2 = Template::GetTemplate(nonexistent, STRIP_WHITESPACE);
    AssertExpandIs(tpl, &dict, "{all-changed}", true);
    AssertExpandIs(tpl2, &dict, "lazarus2", true);

    //
    // Tests to ensure selective Auto-Escape works correctly across reloads.
    //
    TemplateDictionary dict2("user");
    dict2.SetValue("USER", "<bla>");  // Escaped differently by JS and HTML.

    const string kHtmlPragma("{{%AUTOESCAPE context=\"HTML\"}}\n");
    const string kJsPragma("{{%AUTOESCAPE context=\"JAVASCRIPT\"}}\n");
    const string kHtmltext("<p>{{USER}}");          // some HTML
    const string kHtmltextOther("<span>{{USER}}");  // different HTML
    const string kJstext("a = '{{USER}}'");         // some Javascript

    // Case 1: Change template from non auto-escaped to HTML auto-escaped.
    filename = StringToTemplateFile(kHtmltext);
    tpl = Template::GetTemplate(filename, STRIP_WHITESPACE);
    sleep(1);
    StringToFile(kHtmlPragma + kHtmltext, filename);
    AssertExpandIs(tpl, &dict2, "<p><bla>", true);   // haven't reloaded
    ASSERT(tpl->ReloadIfChanged());   // true: content changed
    AssertExpandIs(tpl, &dict2, "<p>&lt;bla&gt;", true);   // reloaded & escaped
    tpl2 = Template::GetTemplate(filename, STRIP_WHITESPACE);
    AssertExpandIs(tpl2, &dict2, "<p>&lt;bla&gt;", true);   // same as tpl

    // Case 2: Change template content, same auto-escape context.
    sleep(1);
    StringToFile(kHtmlPragma + kHtmltextOther, filename);
    ASSERT(tpl->ReloadIfChanged());   // true: content changed
    AssertExpandIs(tpl, &dict2, "<span>&lt;bla&gt;", true);   // reloaded
    tpl2 = Template::GetTemplate(filename, STRIP_WHITESPACE);
    AssertExpandIs(tpl2, &dict2, "<span>&lt;bla&gt;", true);   // same as tpl

    // Case 3: Change auto-escape context to Javascript.
    sleep(1);
    StringToFile(kJsPragma + kJstext, filename);
    AssertExpandIs(tpl, &dict2, "<span>&lt;bla&gt;", true);  // haven't reloaded
    ASSERT(tpl->ReloadIfChanged());   // true: content changed
    AssertExpandIs(tpl, &dict2, "a = '\\x3cbla\\x3e'", true);   // reloaded
    tpl2 = Template::GetTemplate(filename, STRIP_WHITESPACE);
    AssertExpandIs(tpl2, &dict2, "a = '\\x3cbla\\x3e'", true);  // same as tpl

    // Case 4: Change template from auto-escaped to non auto-escaped.
    sleep(1);
    StringToFile(kHtmltext, filename);
    AssertExpandIs(tpl, &dict2, "a = '\\x3cbla\\x3e'", true);
    ASSERT(tpl->ReloadIfChanged());   // true: content changed
    AssertExpandIs(tpl, &dict2, "<p><bla>", true);  // reloaded => not escaped
    tpl2 = Template::GetTemplate(filename, STRIP_WHITESPACE);
    AssertExpandIs(tpl2, &dict2, "<p><bla>", true);   // same as tpl
  }

  static void TestTemplateRootDirectory() {
    string filename = StringToTemplateFile("Test template");
    ASSERT(GOOGLE_NAMESPACE::IsAbspath(filename));
    Template* tpl1 = Template::GetTemplate(filename, DO_NOT_STRIP);
    Template::SetTemplateRootDirectory(GOOGLE_NAMESPACE::kRootdir);  // "/"
    // template-root shouldn't matter for absolute directories
    Template* tpl2 = Template::GetTemplate(filename, DO_NOT_STRIP);
    Template::SetTemplateRootDirectory("/sadfadsf/waerfsa/safdg");
    Template* tpl3 = Template::GetTemplate(filename, DO_NOT_STRIP);
    ASSERT(tpl1 != NULL);
    ASSERT(tpl1 == tpl2);
    ASSERT(tpl1 == tpl3);

    // Now test it actually works by breaking the abspath in various places.
    // We do it twice, since we don't know if the path-sep is "/" or "\".
    // NOTE: this depends on filename not using "/" or "\" except as a
    //       directory separator (so nothing like "/var/tmp/foo\a/weirdfile").
    const char* const kPathSeps = "/\\";
    for (const char* path_sep = kPathSeps; *path_sep; path_sep++) {
      for (string::size_type sep_pos = filename.find(*path_sep, 0);
           sep_pos != string::npos;
           sep_pos = filename.find(*path_sep, sep_pos + 1)) {
        Template::SetTemplateRootDirectory(filename.substr(0, sep_pos + 1));
        Template* tpl = Template::GetTemplate(filename.substr(sep_pos + 1),
                                              DO_NOT_STRIP);
        ASSERT(string(tpl->template_file()) == tpl1->template_file());
      }
    }
  }

  struct ThreadReturn {
    Template* file_template;
    bool string_to_template_cache_return;
    Template* string_template;
  };

  // RunThread returns a ThreadReturn* that should be deleted.
  static void* RunThread(void* vfilename) {
    const char* filename = reinterpret_cast<const char*>(vfilename);
    ThreadReturn* ret = new ThreadReturn;
    ret->file_template = Template::GetTemplate(filename, DO_NOT_STRIP);
    ASSERT(ret->file_template != NULL);
    const char* const key = "RunThread key";
    ret->string_to_template_cache_return =
        Template::StringToTemplateCache(key, " RunThread text ");
    ret->string_template = Template::GetTemplate(key, STRIP_WHITESPACE);
    ASSERT(ret->string_template != NULL);
    return ret;
  }

  static void TestThreadSafety() {
#if defined(HAVE_PTHREAD) && !defined(NO_THREADS)
    string filename = StringToTemplateFile("(testing thread-safety)");

    // GetTemplate() is the most thread-contended routine.  We get a
    // template in many threads, and assert we get the same template
    // from each.
    pthread_t thread_ids[kNumThreads];
    for (int i = 0; i < kNumThreads; ++i) {
      ASSERT(pthread_create(thread_ids+i, NULL, TemplateUnittest::RunThread,
                            (void*)filename.c_str())
             == 0);
    }

    // Wait for all the threads to terminate (should be very quick!)
    ThreadReturn* first_thread_return = NULL;
    int num_times_string_to_template_cache_returned_true = 0;
    for (int i = 0; i < kNumThreads; ++i) {
      void* vthread_return;
      ASSERT(pthread_join(thread_ids[i], &vthread_return) == 0);
      ThreadReturn* thread_return =
          reinterpret_cast<ThreadReturn*>(vthread_return);
      if (thread_return->string_to_template_cache_return) {
        ++num_times_string_to_template_cache_returned_true;
      }
      if (first_thread_return == NULL) {   // we're the first thread
        first_thread_return = thread_return;
      } else {
        ASSERT(thread_return->file_template ==
               first_thread_return->file_template);
        ASSERT(thread_return->string_template ==
               first_thread_return->string_template);
        delete thread_return;
      }
    }
    delete first_thread_return;
    ASSERT_INTEQ(1, num_times_string_to_template_cache_returned_true);
    Template::ClearCache();
#endif
  }

  // Tests all the static methods in TemplateNamelist
  static void TestTemplateNamelist() {
    time_t before_time = time(NULL);
    string f1 = StringToTemplateFile("{{This has spaces in it}}");
    string f2 = StringToTemplateFile("{{#SEC}}foo");
    string f3 = StringToTemplateFile("{This is ok");
    // Where we'll copy f1 - f3 to: these are names known at compile-time
    string f1_copy = GOOGLE_NAMESPACE::PathJoin(FLAGS_test_tmpdir, INVALID1_FN);
    string f2_copy = GOOGLE_NAMESPACE::PathJoin(FLAGS_test_tmpdir, INVALID2_FN);
    string f3_copy = GOOGLE_NAMESPACE::PathJoin(FLAGS_test_tmpdir, VALID1_FN);
    Template::SetTemplateRootDirectory(FLAGS_test_tmpdir);
    time_t after_time = time(NULL);   // f1, f2, f3 all written by now

    TemplateNamelist::NameListType names = TemplateNamelist::GetList();
    ASSERT(names.size() == 4);
    ASSERT(names.find(NONEXISTENT_FN) != names.end());
    ASSERT(names.find(INVALID1_FN) != names.end());
    ASSERT(names.find(INVALID2_FN) != names.end());
    ASSERT(names.find(VALID1_FN) != names.end());

    // Before creating the files INVALID1_FN, etc., all should be missing.
    for (int i = 0; i < 3; ++i) {   // should be consistent all 3 times
      const TemplateNamelist::MissingListType& missing =
        TemplateNamelist::GetMissingList(false);
      ASSERT(missing.size() == 4);
    }
    // Everyone is missing, but nobody should have bad syntax
    ASSERT(!TemplateNamelist::AllDoExist());
    ASSERT(TemplateNamelist::IsAllSyntaxOkay(DO_NOT_STRIP));

    // Now create those files
    link(f1.c_str(), f1_copy.c_str());
    link(f2.c_str(), f2_copy.c_str());
    link(f3.c_str(), f3_copy.c_str());
    // We also have to clear the template cache, since we created a new file.
    // ReloadAllIfChanged() would probably work, too.
    Template::ClearCache();

    // When GetMissingList is false, we don't reload, so you still get all-gone
    TemplateNamelist::MissingListType missing =
      TemplateNamelist::GetMissingList(false);
    ASSERT(missing.size() == 4);
    // But with true, we should have a different story
    missing = TemplateNamelist::GetMissingList(true);
    ASSERT(missing.size() == 1);
    missing = TemplateNamelist::GetMissingList(false);
    ASSERT(missing.size() == 1);
    ASSERT(missing[0] == NONEXISTENT_FN);
    ASSERT(!TemplateNamelist::AllDoExist());

    // IsAllSyntaxOK did a badsyntax check, before the files were created.
    // So with a false arg, should still say everything is ok
    TemplateNamelist::SyntaxListType badsyntax =
      TemplateNamelist::GetBadSyntaxList(false, DO_NOT_STRIP);
    ASSERT(badsyntax.size() == 0);
    // But IsAllSyntaxOK forces a refresh
    ASSERT(!TemplateNamelist::IsAllSyntaxOkay(DO_NOT_STRIP));
    badsyntax = TemplateNamelist::GetBadSyntaxList(false, DO_NOT_STRIP);
    ASSERT(badsyntax.size() == 2);
    ASSERT(badsyntax[0] == INVALID1_FN || badsyntax[1] == INVALID1_FN);
    ASSERT(badsyntax[0] == INVALID2_FN || badsyntax[1] == INVALID2_FN);
    ASSERT(!TemplateNamelist::IsAllSyntaxOkay(DO_NOT_STRIP));
    badsyntax = TemplateNamelist::GetBadSyntaxList(true, DO_NOT_STRIP);
    ASSERT(badsyntax.size() == 2);

    time_t modtime = TemplateNamelist::GetLastmodTime();
    ASSERT(modtime >= before_time && modtime <= after_time);
    // Now update a file and make sure lastmod time is updated
    sleep(1);
    FILE* fp = fopen(f1_copy.c_str(), "ab");
    ASSERT(fp);
    fwrite("\n", 1, 1, fp);
    fclose(fp);
    modtime = TemplateNamelist::GetLastmodTime();
    ASSERT(modtime > after_time);

    // Checking if we can register templates at run time.
    string f4 = StringToTemplateFile("{{ONE_GOOD_TEMPLATE}}");
    TemplateNamelist::RegisterTemplate(f4.c_str());
    names = TemplateNamelist::GetList();
    ASSERT(names.size() == 5);

    string f5 = StringToTemplateFile("{{ONE BAD TEMPLATE}}");
    TemplateNamelist::RegisterTemplate(f5.c_str());
    names = TemplateNamelist::GetList();
    ASSERT(names.size() == 6);
    badsyntax = TemplateNamelist::GetBadSyntaxList(false, DO_NOT_STRIP);
    ASSERT(badsyntax.size() == 2);  // we did not refresh the bad syntax list
    badsyntax = TemplateNamelist::GetBadSyntaxList(true, DO_NOT_STRIP);
    // After refresh, the file we just registerd also added in bad syntax list
    ASSERT(badsyntax.size() == 3);

    TemplateNamelist::RegisterTemplate("A_non_existant_file.tpl");
    names = TemplateNamelist::GetList();
    ASSERT(names.size() == 7);
    missing = TemplateNamelist::GetMissingList(false);
    ASSERT(missing.size() == 1);  // we did not refresh the missing list
    missing = TemplateNamelist::GetMissingList(true);
    // After refresh, the file we just registerd also added in missing list
    ASSERT(missing.size() == 2);
  }

  // This test is not "end-to-end", it doesn't use a dictionary
  // and only outputs what the template system thinks is the
  // correct modifier for variables.
  static void TestCorrectModifiersForAutoEscape() {
    string text, expected_out;

    // template with no variable, nothing to emit.
    text = "Static template.";
    AssertCorrectModifiers(TC_HTML, text, "");

    // Simple templates with one variable substitution.

    // 1. No in-template modifiers. Auto Escaper sets correct ones.
    text = "Hello {{USER}}";
    AssertCorrectModifiers(TC_HTML, text, "USER:h\n");

    // Complete URLs in different attributes that take URLs.
    text = "<a href=\"{{URL}}\">bla</a>";
    AssertCorrectModifiers(TC_HTML, text, "URL:U=html\n");
    text = "<script src=\"{{URL}}\"></script>";
    AssertCorrectModifiers(TC_HTML, text, "URL:U=html\n");
    text = "<img src=\"{{URL}}\">";
    AssertCorrectModifiers(TC_HTML, text, "URL:U=html\n");
    // URL fragment only so just html_escape.
    text = "<img src=\"/bla?q={{QUERY}}\">";
    AssertCorrectModifiers(TC_HTML, text, "QUERY:h\n");
    // URL fragment not quoted, so url_escape.
    text = "<img src=/bla?q={{QUERY}}>";
    AssertCorrectModifiers(TC_HTML, text, "QUERY:u\n");

    text = "<br class=\"{{CLASS}}\">";
    AssertCorrectModifiers(TC_HTML, text, "CLASS:h\n");
    text = "<br class={{CLASS}}>";
    AssertCorrectModifiers(TC_HTML, text, "CLASS:H=attribute\n");
    text = "<br {{CLASS}}>";   // CLASS here is name/value pair.
    AssertCorrectModifiers(TC_HTML, text, "CLASS:H=attribute\n");
    text = "<br style=\"display:{{DISPLAY}}\">";   // Style attribute.
    AssertCorrectModifiers(TC_HTML, text, "DISPLAY:c\n");

    // Content inside a style tag should have :c regardless of quoting.
    text = "<style>color:{{COLOR}}; font:\"{{FONT}}\"</style>";
    AssertCorrectModifiers(TC_HTML, text, "COLOR:c\nFONT:c\n");

    // onMouseEvent and onKeyUp accept javascript.
    text = "<a href=\"url\" onkeyup=\"doX('{{ID}}');\">";  // ID quoted
    AssertCorrectModifiers(TC_HTML, text, "ID:j\n");
    text = "<a href=\"url\" onclick=\"doX({{ID}});\">";    // ID not quoted
    AssertCorrectModifiers(TC_HTML, text, "ID:J=number\n");
    text = "<a href=\"url\" onclick=\"'{{ID}}'\">";        // not common
    AssertCorrectModifiers(TC_HTML, text, "ID:j\n");
    // If ID is javascript code, J=number  will break it, for good and bad.
    text = "<a href=\"url\" onclick=\"{{ID}}\">";
    AssertCorrectModifiers(TC_HTML, text, "ID:J=number\n");

    // Target just needs html escaping.
    text = "<a href=\"url\" target=\"{{TARGET}}\">";
    AssertCorrectModifiers(TC_HTML, text, "TARGET:h\n");

    // Test a parsing corner case which uses TemplateDirective
    // call in the parser to change state properly. To reproduce
    // both variables should be unquoted and the first should
    // have no value except the variable substitution.
    text = "<img class={{CLASS}} src=/bla?q={{QUERY}}>";
    AssertCorrectModifiers(TC_HTML, text, "CLASS:H=attribute\nQUERY:u\n");

    // TODO(jad): Once we have a fix for it in code, fix me.
    // Javascript URL is not properly supported, we currently
    // apply :h which is not sufficient.
    text = "<a href=\"javascript:foo('{{VAR}}')>bla</a>";
    AssertCorrectModifiers(TC_HTML, text, "VAR:h\n");

    // Special handling for BI_SPACE and BI_NEWLINE.
    text = "{{BI_SPACE}}";
    AssertCorrectModifiers(TC_HTML, text, "BI_SPACE\n");      // Untouched.
    text = "{{BI_NEWLINE}}";
    AssertCorrectModifiers(TC_HTML, text, "BI_NEWLINE\n");    // Untouched.
    // Check that the parser is parsing BI_SPACE, if not, it would have failed.
    text = "<a href=/bla{{BI_SPACE}}style=\"{{VAR}}\">text</a>";
    AssertCorrectModifiers(TC_HTML, text, "BI_SPACE\nVAR:c\n");

    // XML and JSON modes.
    text = "<PARAM name=\"{{VAL}}\">{{DATA}}";
    AssertCorrectModifiers(TC_XML, text, "VAL:xml_escape\nDATA:xml_escape\n");
    text = "{ x = \"{{VAL}}\"}";
    AssertCorrectModifiers(TC_JSON, text, "VAL:j\n");

    // 2. Escaping modifiers were set, handle them.

    // 2a: Modifier :none is honored whether the escaping is correct or not.
    text = "Hello {{USER:none}}";                   // :none on its own.
    AssertCorrectModifiers(TC_HTML, text, "USER:none\n");
    text = "Hello {{USER:h:none}}";                 // correct escaping.
    AssertCorrectModifiers(TC_HTML, text, "USER:h:none\n");
    text = "Hello {{USER:j:none}}";                 // incorrect escaping.
    AssertCorrectModifiers(TC_HTML, text, "USER:j:none\n");
    text = "<a href=\"url\" onkeyup=\"doX('{{ID:none}}');\">";
    AssertCorrectModifiers(TC_HTML, text, "ID:none\n");

    // 2b: Correct modifiers, nothing to change.
    text = "Hello {{USER:h}}";
    AssertCorrectModifiers(TC_HTML, text, "USER:h\n");
    text = "Hello {{USER:U=html}}";  // :U=html is a valid replacement for .h
    AssertCorrectModifiers(TC_HTML, text, "USER:U=html\n");
    text = "Hello {{USER:H=url}}";   // :H=url (a.k.a. U=html) is valid too
    AssertCorrectModifiers(TC_HTML, text, "USER:H=url\n");
    text = "Hello {{USER:h:j}}";   // Extra :j, honor it.
    AssertCorrectModifiers(TC_HTML, text, "USER:h:j\n");
    text = "<a href=\"{{URL:U=html}}\">bla</a>";
    AssertCorrectModifiers(TC_HTML, text, "URL:U=html\n");
    text = "<a href=\"/bla?q={{QUERY:h}}\">bla</a>";  // :h is valid.
    AssertCorrectModifiers(TC_HTML, text, "QUERY:h\n");
    text = "<a href=\"/bla?q={{QUERY:u}}\">bla</a>";  // so is :u.
    AssertCorrectModifiers(TC_HTML, text, "QUERY:u\n");
    text = "<a href=\"url\" onclick=\"doX('{{ID:j}}');\">";
    AssertCorrectModifiers(TC_HTML, text, "ID:j\n");
    text = "<a href=\"url\" onclick=\"doX({{ID:J=number}});\">";
    AssertCorrectModifiers(TC_HTML, text, "ID:J=number\n");
    text = "<style>@import url(\"{{URL:U=css}}\")</style>";  // correct :U=css
    AssertCorrectModifiers(TC_HTML, text, "URL:U=css\n");

    // 2c: Incorrect modifiers, add our own.
    text = "Hello {{USER:j}}";                          // Missing :h
    AssertCorrectModifiers(TC_HTML, text, "USER:j:h\n");
    text = "Hello {{USER:c:c:c:c:c:j}}";                // Still missing :h
    AssertCorrectModifiers(TC_HTML, text, "USER:c:c:c:c:c:j:h\n");
    text = "<script>var a = \"{{VAR:h}}\";</script>";   // Missing :j
    AssertCorrectModifiers(TC_HTML, text, "VAR:h:j\n");
    text = "<script>var a = \"{{VAR:j:h:j}}\";</script>";   // Extra :h:j
    AssertCorrectModifiers(TC_HTML, text, "VAR:j:h:j\n");
    text = "<a href=\"url\" onclick=\"doX({{ID:j}});\">";   // Unquoted
    AssertCorrectModifiers(TC_HTML, text, "ID:j:J=number\n");

    // 2d: Custom modifiers are maintained.
    text = "Hello {{USER:x-bla}}";                  // Missing :h
    AssertCorrectModifiers(TC_HTML, text, "USER:x-bla:h\n");
    text = "Hello {{USER:x-bla:h}}";                // Correct, accept it.
    AssertCorrectModifiers(TC_HTML, text, "USER:x-bla:h\n");
    text = "Hello {{USER:x-bla:x-foo}}";            // Missing :h
    AssertCorrectModifiers(TC_HTML, text, "USER:x-bla:x-foo:h\n");
    text = "Hello {{USER:x-bla:none}}";             // Complete due to :none
    AssertCorrectModifiers(TC_HTML, text, "USER:x-bla:none\n");
    text = "Hello {{USER:h:x-bla}}";                // Still missing :h.
    AssertCorrectModifiers(TC_HTML, text, "USER:h:x-bla:h\n");
    text = "Hello {{USER:x-bla:h:x-foo}}";          // Still missing :h
    AssertCorrectModifiers(TC_HTML, text, "USER:x-bla:h:x-foo:h\n");
    text = "Hello {{USER:x-bla:h:x-foo:h}}";        // Valid, accept it.
    AssertCorrectModifiers(TC_HTML, text, "USER:x-bla:h:x-foo:h\n");

    // 2e: Equivalent modifiers are honored. All HTML Escapes.
    text = "Hello {{USER:p}}";
    AssertCorrectModifiers(TC_HTML, text, "USER:p\n");
    text = "Hello {{USER:H=attribute}}";
    AssertCorrectModifiers(TC_HTML, text, "USER:H=attribute\n");
    text = "Hello {{USER:H=snippet}}";
    AssertCorrectModifiers(TC_HTML, text, "USER:H=snippet\n");
    text = "Hello {{USER:H=pre}}";
    AssertCorrectModifiers(TC_HTML, text, "USER:H=pre\n");
    // All URL + HTML Escapes.
    text = "<a href=\"{{URL:H=url}}\">bla</a>";
    AssertCorrectModifiers(TC_HTML, text, "URL:H=url\n");
    text = "<a href=\"{{URL:U=html}}\">bla</a>";
    AssertCorrectModifiers(TC_HTML, text, "URL:U=html\n");

    // 2f: Initialize template in Javascript Context.
    text = "var a = '{{VAR}}'";                     // Escaping not given.
    AssertCorrectModifiers(TC_JS, text, "VAR:j\n");
    text = "var a = '{{VAR:none}}'";                // Variable safe.
    AssertCorrectModifiers(TC_JS, text, "VAR:none\n");
    text = "var a = '{{VAR:j}}'";                   // Escaping correct.
    AssertCorrectModifiers(TC_JS, text, "VAR:j\n");
    text = "var a = '{{VAR:h}}'";                   // Escaping incorrect.
    AssertCorrectModifiers(TC_JS, text, "VAR:h:j\n");
    text = "var a = '{{VAR:J=number}}'";            // Not considered equiv.
    AssertCorrectModifiers(TC_JS, text, "VAR:J=number:j\n");

    // 2g: Honor any modifiers for BI_SPACE and BI_NEWLINE.
    text = "{{BI_NEWLINE:j}}";     // An invalid modifier for the context.
    AssertCorrectModifiers(TC_HTML, text, "BI_NEWLINE:j\n");
    text = "{{BI_SPACE:h}}";       // An otherwise valid modifier.
    AssertCorrectModifiers(TC_HTML, text, "BI_SPACE:h\n");
    text = "{{BI_SPACE:x-bla}}";   // Also support custom modifiers.
    AssertCorrectModifiers(TC_HTML, text, "BI_SPACE:x-bla\n");

    // 2h: TC_CSS, TC_XML and TC_JSON
    text = "H1{margin-{{START_EDGE}}:0;\n text-align:{{END_EDGE}}\n}";
    AssertCorrectModifiers(TC_CSS, text, "START_EDGE:c\nEND_EDGE:c\n");
    text = "body{background:url('{{URL:U=css}}')}";  // :U=css valid substitute
    AssertCorrectModifiers(TC_CSS, text, "URL:U=css\n");
    text = "body{background:url('{{URL:U=html}}')}";  // Not valid, will add :c.
    AssertCorrectModifiers(TC_CSS, text, "URL:U=html:c\n");
    text = "<PARAM name=\"{{VAL:xml_escape}}\">";   // Correct escaping
    AssertCorrectModifiers(TC_XML, text, "VAL:xml_escape\n");
    text = "<PARAM name=\"{{VAL:H=attribute}}\">";   // XSS equivalent
    AssertCorrectModifiers(TC_XML, text, "VAL:H=attribute\n");
    text = "<PARAM name=\"{{VAL:h}}\">";   // XSS equivalent
    AssertCorrectModifiers(TC_XML, text, "VAL:h\n");
    text = "<PARAM name=\"{{VAL:H=pre}}\">";   // Not XSS equivalent
    AssertCorrectModifiers(TC_XML, text, "VAL:H=pre:xml_escape\n");
    text = "<PARAM name=\"{{VAL:c}}\">";   // Not XSS equivalent
    AssertCorrectModifiers(TC_XML, text, "VAL:c:xml_escape\n");
    text = "{user={{USER:j}}";   // Correct escaping
    AssertCorrectModifiers(TC_JSON, text, "USER:j\n");
    text = "{user={{USER:o}}";   // json_escape is XSS equivalent
    AssertCorrectModifiers(TC_JSON, text, "USER:o\n");
    text = "{user={{USER:h}}";   // but html_escape is not
    AssertCorrectModifiers(TC_JSON, text, "USER:h:j\n");

    // 2i: Variables with XssSafe Custom modifiers are untouched.
    ASSERT(GOOGLE_NAMESPACE::AddXssSafeModifier("x-test-cm",
                                         &GOOGLE_NAMESPACE::html_escape));
    text = "Hello {{USER:x-test-cm}}";              // Missing :h
    AssertCorrectModifiers(TC_HTML, text, "USER:x-test-cm\n");
    text = "Hello {{USER:x-test-cm:j}}";            // Extra :j
    AssertCorrectModifiers(TC_HTML, text, "USER:x-test-cm:j\n");
    text = "Hello {{USER:x-test-cm:x-foo}}";        // Non-safe modifier
    AssertCorrectModifiers(TC_HTML, text, "USER:x-test-cm:x-foo\n");
    text = "Hello {{USER:x-foo:x-test-cm}}";        // Non-safe modifier
    AssertCorrectModifiers(TC_HTML, text, "USER:x-foo:x-test-cm\n");
    text = "Hello {{USER:x-test-cm:none}}";         // Complete due to :none
    AssertCorrectModifiers(TC_HTML, text, "USER:x-test-cm:none\n");
    text = "Hello {{USER:h:x-test-cm}}";            // Prior escaping
    AssertCorrectModifiers(TC_HTML, text, "USER:h:x-test-cm\n");

    // 3. Larger test with close to every escaping case.

    text = "<html><head>\n"
        "<style>\n"
        "@import url(\"{{CSS_URL:U=css}}\");\n"
        "color:{{COLOR}}</style></head><body>\n"
        "<h1>{{TITLE}}</h1>\n"
        "<img src=\"{{IMG_URL}}\">\n"
        "<form action=\"/search\">\n"
        "  <input name=\"hl\" value={{HL}}>\n"
        "  <input name=\"m\" value=\"{{FORM_MSG}}\">\n"
        "</form>\n"
        "<div style=\"background:{{BG_COLOR}}\">\n"
        "</div>\n"
        "<script>\n"
        "  var msg_text = '{{MSG_TEXT}}';\n"
        "</script>\n"
        "<a href=\"url\" onmouseover=\"'{{MOUSE}}'\">bla</a>\n"
        "Goodbye friend {{USER}}!\n</body></html>\n";
    expected_out = "CSS_URL:U=css\n"
        "COLOR:c\n"
        "TITLE:h\n"
        "IMG_URL:U=html\n"
        "HL:H=attribute\n"
        "FORM_MSG:h\n"
        "BG_COLOR:c\n"
        "MSG_TEXT:j\n"
        "MOUSE:j\n"   // :j also escapes html entities
        "USER:h\n";
    AssertCorrectModifiers(TC_HTML, text, expected_out);
  }

  // More "end-to-end" test to ensure that variables are
  // escaped as expected with auto-escape mode enabled.
  // Obviously there is a lot more we can test.
  static void TestVariableWithAutoEscape() {
    string text, expected_out;
    TemplateDictionary dict("dict");
    string good_url("http://www.google.com/");
    string bad_url("javascript:alert();");

    text = "hi {{VAR}} lo";
    dict.SetValue("VAR", "<bad>yo");
    AssertCorrectEscaping(TC_HTML, dict, text, "hi &lt;bad&gt;yo lo");

    text = "<a href=\"{{URL}}\">bla</a>";
    dict.SetValue("URL", good_url);
    expected_out = "<a href=\"" + good_url + "\">bla</a>";
    AssertCorrectEscaping(TC_HTML, dict, text, expected_out);
    dict.SetValue("URL", bad_url);
    expected_out = "<a href=\"#\">bla</a>";
    AssertCorrectEscaping(TC_HTML, dict, text, expected_out);

    text = "<br style=\"display:{{DISPLAY}}\">";
    dict.SetValue("DISPLAY", "none");
    expected_out = "<br style=\"display:none\">";
    AssertCorrectEscaping(TC_HTML, dict, text, expected_out);
    // Bad characters are simply removed in CleanseCss.
    dict.SetValue("URL", "!#none_ ");
    expected_out = "<br style=\"display:none\">";
    AssertCorrectEscaping(TC_HTML, dict, text, expected_out);

    text = "<a href=\"url\" onkeyup=\"'{{EVENT}}'\">";
    dict.SetValue("EVENT", "safe");
    expected_out = "<a href=\"url\" onkeyup=\"'safe'\">";
    AssertCorrectEscaping(TC_HTML, dict, text, expected_out);
    dict.SetValue("EVENT", "f = 'y';");
    expected_out = "<a href=\"url\" onkeyup=\"'f \\x3d \\x27y\\x27;'\">";

    // Check special handling of BI_SPACE and BI_NEWLINE.
    text = "Hello\n{{BI_SPACE}}bla{{BI_NEWLINE}}foo.";
    expected_out = "Hello bla\nfoo.";
    AssertCorrectEscaping(TC_HTML, dict, text, expected_out);

    // TC_CSS
    text = "H1{margin-{{EDGE}}:0; text-align:{{BAD_EDGE}}}";
    dict.SetValue("EDGE", "left");
    dict.SetValue("BAD_EDGE", "$$center()!!");  // Bad chars are removed.
    AssertCorrectEscaping(TC_CSS, dict, text,
                          "H1{margin-left:0; text-align:center!!}");

    // TC_XML and TC_JSON
    text = "<Q>{{DATA}}</Q>";
    dict.SetValue("DATA", "good-data");
    AssertCorrectEscaping(TC_XML, dict, text, "<Q>good-data</Q>");
    dict.SetValue("DATA", "<BAD>FOO</BAD>");
    AssertCorrectEscaping(TC_XML, dict, text,
                          "<Q>&lt;BAD&gt;FOO&lt;/BAD&gt;</Q>");
    text = "{user = \"{{USER}}\"}";
    dict.SetValue("USER", "good-user");
    AssertCorrectEscaping(TC_JSON, dict, text, "{user = \"good-user\"}");
    dict.SetValue("USER", "evil'<>\"");
    AssertCorrectEscaping(TC_JSON, dict, text,
                          "{user = \"evil\\x27\\x3c\\x3e\\x22\"}");
  }

  // Test that the template initialization fails in auto-escape
  // mode if the parser failed to parse.
  static void TestFailedInitWithAutoEscape() {
    Strip strip = STRIP_WHITESPACE;
    // Taken from HTML Parser test suite.
    string bad_html = "<a href='http://www.google.com' ''>\n";
    ASSERT(NULL == StringToTemplateWithAutoEscaping(bad_html, strip, TC_HTML));

    // Missing quotes around URL, not accepted in URL-taking attributes.
    bad_html = "<a href={{URL}}>bla</a>";
    ASSERT(NULL == StringToTemplateWithAutoEscaping(bad_html, strip, TC_HTML));

    // Missing quotes around STYLE, not accepted in style-taking attributes.
    bad_html = "<div style={{STYLE}}>";
    ASSERT(NULL == StringToTemplateWithAutoEscaping(bad_html, strip, TC_HTML));
  }

  static void TestAutoEscaping() {
    Strip strip = STRIP_WHITESPACE;
    Template *tpl;
    string filename;
    string text;
    string user = "John<>Doe";
    string user_esc = "John&lt;&gt;Doe";

    // Positive test cases -- template initialization succeeds.
    // We also check that modifiers that were missing or given incorrect
    // have been updated as expected.
    // TODO(jad): Cut-down redundancy by merging with
    //            TestCorrectModifiersForAutoEscape.
    text = "{{%AUTOESCAPE context=\"HTML\"}}"         // HTML
        "{{USER:o}}<a href=\"{{URL}}\" class={{CLASS:h}}</a>";
    ASSERT(tpl = StringToTemplate(text, strip));
    string expected_mods = "USER:o:h\nURL:U=html\nCLASS:h:H=attribute\n";
    AssertCorrectModifiersInTemplate(tpl, text, expected_mods);

    text = "{{%AUTOESCAPE context=\"HTML\" state=\"IN_TAG\"}}"  // HTML in tag
        "href=\"{{URL}}\" class={{CLASS:h}} style=\"font:{{COLOR}}\"";
    ASSERT(tpl = StringToTemplate(text, strip));
    expected_mods = "URL:U=html\nCLASS:h:H=attribute\nCOLOR:c\n";
    AssertCorrectModifiersInTemplate(tpl, text, expected_mods);

    // Repeat the test with trailing HTML that closes the tag. This is
    // undefined behavior. We test it to ensure the parser does not choke.
    text += ">Hello</a><span>Some text</span></body></html>";
    ASSERT(tpl = StringToTemplate(text, strip));
    expected_mods = "URL:U=html\nCLASS:h:H=attribute\nCOLOR:c\n";
    AssertCorrectModifiersInTemplate(tpl, text, expected_mods);

    text = "{{%AUTOESCAPE context=\"JAVASCRIPT\"}}"   // JAVASCRIPT
        "var a = {{A}}; var b = '{{B:h}}';";
    ASSERT(tpl = StringToTemplate(text, strip));
    expected_mods = "A:J=number\nB:h:j\n";
    AssertCorrectModifiersInTemplate(tpl, text, expected_mods);

    text = "{{%AUTOESCAPE context=\"CSS\"}}"          // CSS
        "body {color:\"{{COLOR}}\"; font-size:{{SIZE:j}}";
    ASSERT(tpl = StringToTemplate(text, strip));
    expected_mods = "COLOR:c\nSIZE:j:c\n";
    AssertCorrectModifiersInTemplate(tpl, text, expected_mods);

    text = "{{%AUTOESCAPE context=\"JSON\"}}"         // JSON
        "{ 'id': {{ID:j}}, 'value': {{VALUE:h}} }";
    ASSERT(tpl = StringToTemplate(text, strip));
    expected_mods = "ID:j\nVALUE:h:j\n";
    AssertCorrectModifiersInTemplate(tpl, text, expected_mods);

    text = "{{%AUTOESCAPE context=\"XML\"}}"          // XML
        "<PARAM name=\"{{VAL}}\">{{DATA:h}}";
    ASSERT(tpl = StringToTemplate(text, strip));
    expected_mods = "VAL:xml_escape\nDATA:h\n";
    AssertCorrectModifiersInTemplate(tpl, text, expected_mods);

    text = "{{!bla}}{{%AUTOESCAPE context=\"HTML\"}}";  // after comment
    ASSERT(tpl = StringToTemplate(text, strip));
    text = "{{%AUTOESCAPE context=\"HTML\" state=\"default\"}}";
    ASSERT(tpl = StringToTemplate(text, strip));        // adding state

    // Negative test cases - template initialization fails due to errors
    // in the marker. Also checks that our parsing is defensive.
    text = "{{%AUTOESCAPE}}";                                 // missing context
    ASSERT((tpl = StringToTemplate(text, strip)) == NULL);
    text = "{{%AUTOESCAPER context=\"HTML\"}}";               // invalid id
    ASSERT((tpl = StringToTemplate(text, strip)) == NULL);
    text = "{{%}}";                                           // missing id
    ASSERT((tpl = StringToTemplate(text, strip)) == NULL);
    text = "{{% }}";                                          // missing id
    ASSERT((tpl = StringToTemplate(text, strip)) == NULL);
    text = "{{% =}}";                                         // missing id
    ASSERT((tpl = StringToTemplate(text, strip)) == NULL);
    text = "{{%AUTOESCAPE =\"HTML\"}}";                       // missing name
    ASSERT((tpl = StringToTemplate(text, strip)) == NULL);
    text = "{{%AUTOESCAPE foo=\"HTML\"}}";                    // bogus name
    ASSERT((tpl = StringToTemplate(text, strip)) == NULL);
    text = "{{%AUTOESCAPE =}}";                               // lone '='
    ASSERT((tpl = StringToTemplate(text, strip)) == NULL);
    text = "{{%AUTOESCAPE context=HTML}}";                    // val not quoted
    ASSERT((tpl = StringToTemplate(text, strip)) == NULL);
    text = "{{%AUTOESCAPE context=\"HTML}}";                  // no end quotes
    ASSERT((tpl = StringToTemplate(text, strip)) == NULL);
    text = "{{%AUTOESCAPE context=\"\\\"HTML\"}}";            // Unescape val
    ASSERT((tpl = StringToTemplate(text, strip)) == NULL);
    text = "{{%AUTOESCAPE context=\"\\\"HT\\\"\\\"ML\\\"\"}}";   // more complex
    ASSERT((tpl = StringToTemplate(text, strip)) == NULL);
    text = "{{%AUTOESCAPE context=\"\"HTML\"}}";              // Unescape val
    ASSERT((tpl = StringToTemplate(text, strip)) == NULL);
    text = "{{%AUTOESCAPE context=\"JAVASCRIPT\" bla}}";      // extra attr
    ASSERT((tpl = StringToTemplate(text, strip)) == NULL);
    text = "{{%AUTOESCAPE context=\"JAVASCRIPT\"bla}}";       // invalid value
    ASSERT((tpl = StringToTemplate(text, strip)) == NULL);
    text = "{{%AUTOESCAPE context=\"JAVASCRIPT\" foo=bla}}";  // extra attr/val
    ASSERT((tpl = StringToTemplate(text, strip)) == NULL);
    text = "{{%AUTOESCAPE  context=\"HTML\"}}";               // extra whitesp
    ASSERT((tpl = StringToTemplate(text, strip)) == NULL);
    text = "{{%AUTOESCAPE context =\"HTML\"}}";               // extra whitesp
    ASSERT((tpl = StringToTemplate(text, strip)) == NULL);
    text = "{{%AUTOESCAPE context= \"HTML\"}}";               // extra whitesp
    ASSERT((tpl = StringToTemplate(text, strip)) == NULL);
    text = "{{%AUTOESCAPE context=\"HTML\" }}";               // extra whitesp
    ASSERT((tpl = StringToTemplate(text, strip)) == NULL);
    text = "{{%AUTOESCAPE context=\"xml\"}}";                 // lower-case xml
    ASSERT((tpl = StringToTemplate(text, strip)) == NULL);
    text = "{{%AUTOESCAPE context=\"HTML\" state=\"tag\"}}";  // bad state
    ASSERT((tpl = StringToTemplate(text, strip)) == NULL);
    text = "{{%AUTOESCAPE context=\"CSS\" state=\"IN_TAG\"}}";  // invalid state
    ASSERT((tpl = StringToTemplate(text, strip)) == NULL);
    text = "Hello{{%AUTOESCAPE context=\"HTML\"}}";           // after text
    ASSERT((tpl = StringToTemplate(text, strip)) == NULL);
    text = "{{USER}}{{%AUTOESCAPE context=\"HTML\"}}";        // after variable
    ASSERT((tpl = StringToTemplate(text, strip)) == NULL);
    text = "{{#SEC}}{{%AUTOESCAPE context=\"HTML\"}}{{/SEC}}";  // not in MAIN
    ASSERT((tpl = StringToTemplate(text, strip)) == NULL);

    string kAutoescapeHtmlPragma = "{{%AUTOESCAPE context=\"HTML\"}}";

    // Check that Selective Auto-Escape does not auto-escape included templates
    // unless these are also marked for auto-escape. To attest that,
    // we check that when no escaping was given in the included template, none
    // will be applied to it. USER will not get html-escaped.
    text = kAutoescapeHtmlPragma + "{{>INC}}";
    tpl = StringToTemplate(text, strip);
    ASSERT(tpl);
    string inc_text = "{{USER}}";    // missing :h escaping.
    TemplateDictionary dict("dict");
    TemplateDictionary *inc_dict = dict.AddIncludeDictionary("INC");
    inc_dict->SetFilename(StringToTemplateFile(inc_text));
    inc_dict->SetValue("USER", user);
    AssertExpandIs(tpl, &dict, user, true);

    // Add AUTOESCAPE pragma to included template and check that it works.
    inc_text = kAutoescapeHtmlPragma + inc_text;
    filename = StringToTemplateFile(inc_text);
    inc_dict->SetFilename(filename);
    AssertExpandIs(tpl, &dict, user_esc, true);

    // Check that Selective Auto-Escape works with Template::StringToTemplate.
    tpl = Template::StringToTemplate(inc_text, strip);
    ASSERT(tpl);
    TemplateDictionary dict2("dict2");
    dict2.SetValue("USER", user);
    AssertExpandIs(tpl, &dict2, user_esc, true);
    delete tpl;

    // Test that Selective AutoEscape follows included templates: Included
    // templates 2 and 4 are registered for auto-escaping but not included
    // templates 1 and 3. Check that only templates 2 and 4 get escaped.
    text = "Parent: {{USER}}; {{>INCONE}}";
    string text_inc1 = "INC1: {{USER1}}; {{>INCTWO}}";
    string text_inc2 = kAutoescapeHtmlPragma + "INC2: {{USER2}}; {{>INCTHREE}}";
    string text_inc3 = "INC3: {{USER3}}; {{>INCFOUR}}";
    string text_inc4 = kAutoescapeHtmlPragma + "INC4: {{USER4}}";
    dict.SetValue("USER", user);

    TemplateDictionary *dict_inc1 = dict.AddIncludeDictionary("INCONE");
    dict_inc1->SetFilename(StringToTemplateFile(text_inc1));
    dict_inc1->SetValue("USER1", user);

    TemplateDictionary *dict_inc2 = dict_inc1->AddIncludeDictionary("INCTWO");
    filename = StringToTemplateFile(text_inc2);
    dict_inc2->SetFilename(filename);
    dict_inc2->SetValue("USER2", user);

    TemplateDictionary *dict_inc3 = dict_inc2->AddIncludeDictionary("INCTHREE");
    dict_inc3->SetFilename(StringToTemplateFile(text_inc3));
    dict_inc3->SetValue("USER3", user);

    TemplateDictionary *dict_inc4 = dict_inc3->AddIncludeDictionary("INCFOUR");
    filename = StringToTemplateFile(text_inc4);
    dict_inc4->SetFilename(filename);
    dict_inc4->SetValue("USER4", user);

    tpl = StringToTemplate(text, strip);
    string expected_out = "Parent: " + user + "; INC1: " + user +
        "; INC2: " + user_esc + "; INC3: " + user + "; INC4: " + user_esc;
    AssertExpandIs(tpl, &dict, expected_out, true);

    // Check that we do not modify template-includes.
    // Here, xml_escape would have been changed to :h:xml_escape
    // causing a double-escaping of the USER.
    text = kAutoescapeHtmlPragma + "{{>INC:xml_escape}}";
    inc_text = "{{USER}}";
    tpl = StringToTemplate(text, strip);
    ASSERT(tpl);
    TemplateDictionary dict3("dict");
    inc_dict = dict3.AddIncludeDictionary("INC");
    inc_dict->SetFilename(StringToTemplateFile(inc_text));
    inc_dict->SetValue("USER", user);
    AssertExpandIs(tpl, &dict3, user_esc, true);

    // Test that {{%...}} is a "removable" marker. A related test is
    // also added to TestStrip().
    tpl = StringToTemplate("{{%AUTOESCAPE context=\"HTML\"}}\nText\n Text",
                           STRIP_BLANK_LINES);
    AssertExpandIs(tpl, &dict, "Text\n Text", true);
  }

  static void TestRegisterString() {
    ASSERT(Template::StringToTemplateCache("file1", "Some text"));
    Template* tpl = Template::GetTemplate("file1", STRIP_WHITESPACE);
    ASSERT(tpl);
    ASSERT(Template::GetTemplate("file1", STRIP_WHITESPACE) == tpl);

    ASSERT(Template::StringToTemplateCache("file2", "Top {{>INC}}"));

    TemplateDictionary dict("dict");
    string expected = "Some text";
    AssertExpandIs(tpl, &dict, expected, true);

    TemplateDictionary* sub_dict = dict.AddIncludeDictionary("INC");
    sub_dict->SetFilename("file1");
    tpl = Template::GetTemplate("file2", STRIP_WHITESPACE);
    expected = "Top Some text";
    AssertExpandIs(tpl, &dict, expected, true);
  }

  static void TestRegisterStringVsFileCollision() {
    TemplateDictionary dict("dict");

    string filename = StringToTemplateFile("file contents");

    Template *tpl = Template::GetTemplate(filename, STRIP_WHITESPACE);
    string expected = "file contents";
    AssertExpandIs(tpl, &dict, expected, true);

    // Try to register new template contents under the same template key
    Template::StringToTemplateCache(filename, "string contents");

    tpl = Template::GetTemplate(filename, STRIP_WHITESPACE);
    expected = "file contents";   // Contents as of first GetTemplate win
    AssertExpandIs(tpl, &dict, expected, true);

    sleep(1);    // since mtime goes by 1-second increments
    StringToFile("new file contents", filename);
    ASSERT(tpl->ReloadIfChanged());   // should reload - file wins (came first)
    ASSERT(Template::GetTemplate(filename, STRIP_WHITESPACE) == tpl);
    expected = "new file contents";   // First registration should win
    AssertExpandIs(tpl, &dict, expected, true);

    Template::ReloadAllIfChanged();
    ASSERT(Template::GetTemplate(filename, STRIP_WHITESPACE) == tpl);
    AssertExpandIs(tpl, &dict, expected, true);
  }

 private:
  // This is used by TestAnnotation().  It behaves like
  // TextTemplateAnnotator but just to test our ability to customize
  // annotation, and with stateful one, it prefixes each text annotation
  // with an event (call) count.
  class CustomTestAnnotator : public GOOGLE_NAMESPACE::TextTemplateAnnotator {
   public:
    CustomTestAnnotator() : event_count_(0) { }
    void Reset() { event_count_ = 0; }

    virtual void EmitOpenInclude(ExpandEmitter* emitter, const string& value) {
      EmitTestPrefix(emitter);
      GOOGLE_NAMESPACE::TextTemplateAnnotator::EmitOpenInclude(emitter, value);
    }
    virtual void EmitCloseInclude(ExpandEmitter* emitter) {
      EmitTestPrefix(emitter);
      GOOGLE_NAMESPACE::TextTemplateAnnotator::EmitCloseInclude(emitter);
    }
    virtual void EmitOpenFile(ExpandEmitter* emitter, const string& value) {
      EmitTestPrefix(emitter);
      GOOGLE_NAMESPACE::TextTemplateAnnotator::EmitOpenFile(emitter, value);
    }
    virtual void EmitCloseFile(ExpandEmitter* emitter) {
      EmitTestPrefix(emitter);
      GOOGLE_NAMESPACE::TextTemplateAnnotator::EmitCloseFile(emitter);
    }
    virtual void EmitOpenSection(ExpandEmitter* emitter, const string& value) {
      EmitTestPrefix(emitter);
      GOOGLE_NAMESPACE::TextTemplateAnnotator::EmitOpenSection(emitter, value);
    }
    virtual void EmitCloseSection(ExpandEmitter* emitter) {
      EmitTestPrefix(emitter);
      GOOGLE_NAMESPACE::TextTemplateAnnotator::EmitCloseSection(emitter);
    }
    virtual void EmitOpenVariable(ExpandEmitter* emitter, const string& value) {
      EmitTestPrefix(emitter);
      GOOGLE_NAMESPACE::TextTemplateAnnotator::EmitOpenVariable(emitter, value);
    }
    virtual void EmitCloseVariable(ExpandEmitter* emitter) {
      EmitTestPrefix(emitter);
      GOOGLE_NAMESPACE::TextTemplateAnnotator::EmitCloseVariable(emitter);
    }
    virtual void EmitOpenMissingInclude(ExpandEmitter* emitter,
                                        const string& value) {
      EmitTestPrefix(emitter);
      GOOGLE_NAMESPACE::TextTemplateAnnotator::EmitOpenMissingInclude(emitter, value);
    }
    virtual void EmitCloseMissingInclude(ExpandEmitter* emitter) {
      EmitTestPrefix(emitter);
      GOOGLE_NAMESPACE::TextTemplateAnnotator::EmitCloseMissingInclude(emitter);
    }

   private:
    void EmitTestPrefix(ExpandEmitter* emitter) {
      char buf[128];
      snprintf(buf, sizeof(buf), "{{EVENT=%d}}", ++event_count_);
      emitter->Emit(buf);
    }
    int event_count_;
  };
};

// This tests that StaticTemplateString is sufficiently initialized at
// static-initialization time (as opposed to dynamic-initialization
// time, which comes later), that we can safely expand templates
// during dynamic initialization.  This is worth testing, because some
// parts of a StaticTemplateString -- especially the hash value, *do*
// get computed later at dynamic-initialization time, and we want to
// make sure that things still work properly even if we access the
// StaticTemplateString before that happens.
extern const StaticTemplateString kLateDefine;
class DynamicInitializationTemplateExpander {
 public:
  DynamicInitializationTemplateExpander() {
    Template* tpl = Template::StringToTemplate("hi {{VAR}} lo",
                                               STRIP_WHITESPACE);
    TemplateDictionary dict("dict");
    dict.SetValue("VAR", TemplateString("short-lived", strlen("short")));
    AssertExpandIs(tpl, &dict, "hi short lo", true);
    dict.SetValue("VAR", kHello);
    AssertExpandIs(tpl, &dict, "hi Hello lo", true);
    dict.SetValue("VAR", kLateDefine);
    AssertExpandIs(tpl, &dict, "hi laterz lo", true);
    delete tpl;
  }
};
DynamicInitializationTemplateExpander sts_tester;  // this runs before main()
const StaticTemplateString kLateDefine = STS_INIT(kLateDefine, "laterz");

int main(int argc, char** argv) {
  CleanTestDir(FLAGS_test_tmpdir);
  // Subdirs used by TestTemplateSearchPath
  CleanTestDir(ctemplate::PathJoin(FLAGS_test_tmpdir, "a"));
  CleanTestDir(ctemplate::PathJoin(FLAGS_test_tmpdir, "b"));

  // This goes first so that future tests don't mess up the filenames
  TemplateUnittest::TestAnnotation();
  TemplateUnittest::CheckWhitelistedVariablesSorted();
  TemplateUnittest::TestTemplateExpansionModifier();

  TemplateUnittest::TestWeirdSyntax();

  TemplateUnittest::TestComment();
  TemplateUnittest::TestSetMarkerDelimiters();
  TemplateUnittest::TestVariable();
  TemplateUnittest::TestVariableWithModifiers();
  TemplateUnittest::TestSection();
  TemplateUnittest::TestSectionSeparator();
  TemplateUnittest::TestInclude();
  TemplateUnittest::TestIncludeWithModifiers();
  TemplateUnittest::TestRecursiveInclude();
  TemplateUnittest::TestInheritence();
  TemplateUnittest::TestTemplateString();
  TemplateUnittest::TestExpand();
  TemplateUnittest::TestExpandWithCustomEmitter();

  TemplateUnittest::TestTemplateSearchPath();
  TemplateUnittest::TestGetTemplate();
  TemplateUnittest::TestRegisterString();
  TemplateUnittest::TestRegisterStringVsFileCollision();
  TemplateUnittest::TestStringCacheKey();
  TemplateUnittest::TestStringGetTemplate();
  TemplateUnittest::TestStringTemplateInclude();
  TemplateUnittest::TestRemoveStringFromTemplateCache();
  TemplateUnittest::TestTemplateCache();
  TemplateUnittest::TestStrip();
  TemplateUnittest::TestReloadIfChanged();

  TemplateUnittest::TestTemplateRootDirectory();
  TemplateUnittest::TestTemplateNamelist();

  TemplateUnittest::TestThreadSafety();

  TemplateUnittest::TestCorrectModifiersForAutoEscape();
  TemplateUnittest::TestVariableWithAutoEscape();
  TemplateUnittest::TestFailedInitWithAutoEscape();
  TemplateUnittest::TestAutoEscaping();

  printf("DONE\n");
  return 0;
}
