1
0
mirror of https://github.com/OlafvdSpek/ctemplate.git synced 2025-09-28 19:05:49 +08:00
ctemplate/src/tests/template_cache_test.cc
csilvers+ctemplate@google.com 760d83b13a Change the behavior of template cache so that it reloads the template if
another template has been added with the same name earlier in the search path,
even if the original file is NOT updated(touched, updated, deleted etc).

Tested:
blaze test template:all

R=panicker,csilvers
DELTA=45  (6 added, 24 deleted, 15 changed)


Revision created by MOE tool push_codebase.
MOE_MIGRATION=3885
2011-12-01 17:54:59 +00:00

1065 lines
44 KiB
C++

// Copyright (c) 2009, 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: csilvers@google.com (Craig Silverstein)
//
#include "config_for_unittests.h"
#include <ctemplate/template_cache.h>
#include <assert.h> // for assert()
#include <stdio.h> // for printf()
#include <stdlib.h> // for exit()
#include <string.h> // for strcmp()
#include <sys/types.h> // for mode_t
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif // for unlink()
#include <ctemplate/template.h> // for Template
#include <ctemplate/template_dictionary.h> // for TemplateDictionary
#include <ctemplate/template_enums.h> // for DO_NOT_STRIP, etc
#include <ctemplate/template_pathops.h> // for PathJoin(), kCWD
#include <ctemplate/template_string.h> // for TemplateString
#include "tests/template_test_util.h" // for AssertExpandIs(), etc
using std::string;
using GOOGLE_NAMESPACE::FLAGS_test_tmpdir;
using GOOGLE_NAMESPACE::AssertExpandIs;
using GOOGLE_NAMESPACE::CreateOrCleanTestDir;
using GOOGLE_NAMESPACE::CreateOrCleanTestDirAndSetAsTmpdir;
using GOOGLE_NAMESPACE::DO_NOT_STRIP;
using GOOGLE_NAMESPACE::PathJoin;
using GOOGLE_NAMESPACE::STRIP_BLANK_LINES;
using GOOGLE_NAMESPACE::STRIP_WHITESPACE;
using GOOGLE_NAMESPACE::StaticTemplateString;
using GOOGLE_NAMESPACE::StringToFile;
using GOOGLE_NAMESPACE::StringToTemplateCache;
using GOOGLE_NAMESPACE::StringToTemplateFile;
using GOOGLE_NAMESPACE::Template;
using GOOGLE_NAMESPACE::TemplateCache;
using GOOGLE_NAMESPACE::TemplateCachePeer;
using GOOGLE_NAMESPACE::TemplateDictionary;
using GOOGLE_NAMESPACE::kCWD;
#define ASSERT(cond) do { \
if (!(cond)) { \
printf("ASSERT FAILED, line %d: %s\n", __LINE__, #cond); \
assert(cond); \
exit(1); \
} \
} while (0)
#define ASSERT_STREQ(a, b) ASSERT(strcmp(a, b) == 0)
static const StaticTemplateString kKey = STS_INIT(kKey, "MY_KEY");
static const StaticTemplateString kContent = STS_INIT(kContent, "content");
// It would be nice to use the TEST framework, but it makes friendship
// more difficult. (TemplateCache befriends TemplateCacheUnittest.)
class TemplateCacheUnittest {
public:
static void TestGetTemplate() {
// Tests the cache
TemplateCache cache1;
const char* text = "{This is perfectly valid} yay!";
TemplateDictionary empty_dict("dict");
string filename = StringToTemplateFile(text);
const Template* tpl1 = cache1.GetTemplate(filename, DO_NOT_STRIP);
const Template* tpl2 = cache1.GetTemplate(filename.c_str(), DO_NOT_STRIP);
const Template* tpl3 = cache1.GetTemplate(filename, STRIP_WHITESPACE);
ASSERT(tpl1 && tpl2 && tpl3);
ASSERT(tpl1 == tpl2);
ASSERT(tpl1 != tpl3);
AssertExpandIs(tpl1, &empty_dict, text, true);
AssertExpandIs(tpl2, &empty_dict, text, true);
AssertExpandIs(tpl3, &empty_dict, text, true);
// Tests that a nonexistent template returns NULL
const Template* tpl4 = cache1.GetTemplate("/yakakak", STRIP_WHITESPACE);
ASSERT(!tpl4);
// Make sure we get different results if we use a different cache.
TemplateCache cache2;
const Template* tpl5 = cache2.GetTemplate(filename, DO_NOT_STRIP);
ASSERT(tpl5);
ASSERT(tpl5 != tpl1);
AssertExpandIs(tpl5, &empty_dict, text, true);
// And different results yet if we use the default cache.
const Template* tpl6 = Template::GetTemplate(filename, DO_NOT_STRIP);
ASSERT(tpl6);
ASSERT(tpl6 != tpl1);
AssertExpandIs(tpl6, &empty_dict, text, true);
}
static void TestLoadTemplate() {
// Tests the cache
TemplateCache cache1;
const char* text = "{This is perfectly valid} yay!";
TemplateDictionary empty_dict("dict");
string filename = StringToTemplateFile(text);
ASSERT(cache1.LoadTemplate(filename, DO_NOT_STRIP));
// Tests that a nonexistent template returns false
ASSERT(!cache1.LoadTemplate("/yakakak", STRIP_WHITESPACE));
}
static void TestStringGetTemplate() {
// 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");
TemplateCache cache1;
const Template *tpl1;
ASSERT(cache1.StringToTemplateCache(cache_key_a, text, DO_NOT_STRIP));
tpl1 = cache1.GetTemplate(cache_key_a, DO_NOT_STRIP);
AssertExpandIs(tpl1, &empty_dict, text, true);
// A different cache should give different templates.
TemplateCache cache2;
const Template *tpl3;
ASSERT(cache2.StringToTemplateCache(cache_key_a, text, DO_NOT_STRIP));
tpl3 = cache2.GetTemplate(cache_key_a, DO_NOT_STRIP);
ASSERT(tpl3 != tpl1);
AssertExpandIs(tpl3, &empty_dict, text, true);
// And the main cache different still
const Template *tpl4;
ASSERT(StringToTemplateCache(cache_key_a, text, DO_NOT_STRIP));
tpl4 = Template::GetTemplate(cache_key_a, DO_NOT_STRIP);
ASSERT(tpl4 != tpl1);
AssertExpandIs(tpl4, &empty_dict, text, true);
// If we register a new string with the same text, it should be ignored.
ASSERT(!cache1.StringToTemplateCache(cache_key_a, "new text",
DO_NOT_STRIP));
Template::ClearCache();
}
static void TestStringToTemplateCacheWithStrip() {
const string cache_key_a = "cache key a";
const string text = "Test template 1";
TemplateDictionary empty_dict("dict");
TemplateCache cache;
ASSERT(cache.StringToTemplateCache(cache_key_a, text, DO_NOT_STRIP));
TemplateCachePeer cache_peer(&cache);
TemplateCachePeer::TemplateCacheKey cache_key1(cache_key_a, DO_NOT_STRIP);
ASSERT(cache_peer.TemplateIsCached(cache_key1));
const Template* tpl1 = cache_peer.GetTemplate(cache_key_a, DO_NOT_STRIP);
ASSERT(tpl1);
AssertExpandIs(tpl1, &empty_dict, text, true);
// Different strip: when a string template is registered via
// StringToTemplateCache with a strip, we cannot use a different
// strip later to fetch the template.
TemplateCachePeer::TemplateCacheKey cache_key2(cache_key_a,
STRIP_WHITESPACE);
ASSERT(!cache_peer.TemplateIsCached(cache_key2));
}
static void TestExpandNoLoad() {
TemplateCache cache;
string filename = StringToTemplateFile("alone");
string top_filename = StringToTemplateFile("Hello, {{>WORLD}}");
string inc_filename = StringToTemplateFile("world");
TemplateDictionary dict("ExpandNoLoad");
dict.AddIncludeDictionary("WORLD")->SetFilename(inc_filename);
string out;
// This should fail because the cache is empty.
cache.Freeze();
ASSERT(!cache.ExpandNoLoad(filename, DO_NOT_STRIP, &dict, NULL, &out));
cache.ClearCache(); // also clears the "frozen" state
// This should succeed -- it loads inc_filename from disk.
ASSERT(cache.ExpandWithData(filename, DO_NOT_STRIP, &dict, NULL, &out));
ASSERT(out == "alone");
out.clear();
// Now this should succeed -- it's in the cache.
cache.Freeze();
ASSERT(cache.ExpandNoLoad(filename, DO_NOT_STRIP, &dict, NULL, &out));
ASSERT(out == "alone");
out.clear();
// This should fail because neither top nor inc are in the cache.
cache.ClearCache();
cache.Freeze();
ASSERT(!cache.ExpandNoLoad(top_filename, DO_NOT_STRIP, &dict, NULL, &out));
cache.ClearCache();
ASSERT(cache.LoadTemplate(top_filename, DO_NOT_STRIP));
// This *should* fail, but because inc_filename isn't in the cache.
cache.Freeze();
ASSERT(!cache.ExpandNoLoad(top_filename, DO_NOT_STRIP, &dict, NULL, &out));
// TODO(csilvers): this should not be necessary. But expand writes
// to its output even before it fails.
out.clear();
cache.ClearCache();
ASSERT(cache.LoadTemplate(top_filename, DO_NOT_STRIP));
ASSERT(cache.LoadTemplate(inc_filename, DO_NOT_STRIP));
cache.Freeze();
// *Now* it should succeed, with everything it needs loaded.
ASSERT(cache.ExpandNoLoad(top_filename, DO_NOT_STRIP, &dict, NULL, &out));
ASSERT(out == "Hello, world");
out.clear();
// This should succeed too, of course.
ASSERT(cache.ExpandWithData(top_filename, DO_NOT_STRIP, &dict, NULL, &out));
ASSERT(out == "Hello, world");
out.clear();
cache.ClearCache();
ASSERT(cache.ExpandWithData(top_filename, DO_NOT_STRIP, &dict, NULL, &out));
ASSERT(out == "Hello, world");
out.clear();
// Now everything NoLoad needs should be in the cache again.
cache.Freeze();
ASSERT(cache.ExpandNoLoad(top_filename, DO_NOT_STRIP, &dict, NULL, &out));
ASSERT(out == "Hello, world");
out.clear();
cache.ClearCache();
ASSERT(cache.LoadTemplate(top_filename, DO_NOT_STRIP));
cache.Freeze();
// This fails, of course, because we're frozen.
ASSERT(!cache.LoadTemplate(inc_filename, DO_NOT_STRIP));
// And thus, this fails too.
ASSERT(!cache.ExpandNoLoad(top_filename, DO_NOT_STRIP, &dict, NULL, &out));
}
static void TestTemplateSearchPath() {
TemplateCache cache1;
const string pathA = PathJoin(FLAGS_test_tmpdir, "a/");
const string pathB = PathJoin(FLAGS_test_tmpdir, "b/");
CreateOrCleanTestDir(pathA);
CreateOrCleanTestDir(pathB);
TemplateDictionary dict("");
cache1.SetTemplateRootDirectory(pathA);
cache1.AddAlternateTemplateRootDirectory(pathB);
ASSERT(cache1.template_root_directory() == pathA);
// 1. Show that a template in the secondary path can be found.
const string path_b_bar = PathJoin(pathB, "template_bar");
StringToFile("b/template_bar", path_b_bar);
ASSERT_STREQ(path_b_bar.c_str(),
cache1.FindTemplateFilename("template_bar").c_str());
const Template* b_bar = cache1.GetTemplate("template_bar", DO_NOT_STRIP);
ASSERT(b_bar);
AssertExpandIs(b_bar, &dict, "b/template_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 = PathJoin(pathA, "template_foo");
const string path_b_foo = PathJoin(pathB, "template_foo");
StringToFile("a/template_foo", path_a_foo);
StringToFile("b/template_foo", path_b_foo);
ASSERT_STREQ(path_a_foo.c_str(),
cache1.FindTemplateFilename("template_foo").c_str());
const Template* a_foo = cache1.GetTemplate("template_foo", DO_NOT_STRIP);
ASSERT(a_foo);
AssertExpandIs(a_foo, &dict, "a/template_foo", true);
// 3. Show that attempting to find a non-existent template gives an
// empty path.
ASSERT(cache1.FindTemplateFilename("baz").empty());
// 4. If we make a new cache, its path will be followed.
TemplateCache cache2;
cache2.SetTemplateRootDirectory(pathB);
ASSERT_STREQ(path_b_foo.c_str(),
cache2.FindTemplateFilename("template_foo").c_str());
const Template* b_foo = cache2.GetTemplate("template_foo", DO_NOT_STRIP);
ASSERT(b_foo);
AssertExpandIs(b_foo, &dict, "b/template_foo", true);
// 5. Neither path will work for the default cache, which has no path.
ASSERT(Template::template_root_directory() == kCWD);
ASSERT(Template::FindTemplateFilename("template_foo").empty());
ASSERT(!Template::GetTemplate("template_foo", DO_NOT_STRIP));
CreateOrCleanTestDir(pathA);
CreateOrCleanTestDir(pathB);
}
static void TestDelete() {
Template::ClearCache(); // just for exercise.
const string cache_key = "TestRemoveStringFromTemplateCache";
const string text = "<html>here today...</html>";
const string text2 = "<html>on disk tomorrow</html>";
TemplateDictionary dict("test");
TemplateCache cache1;
ASSERT(cache1.StringToTemplateCache(cache_key, text, DO_NOT_STRIP));
const Template* tpl = cache1.GetTemplate(cache_key, DO_NOT_STRIP);
ASSERT(tpl);
AssertExpandIs(tpl, &dict, text, true);
cache1.Delete(cache_key);
tpl = cache1.GetTemplate(cache_key, DO_NOT_STRIP);
ASSERT(!tpl);
tpl = cache1.GetTemplate(cache_key, STRIP_WHITESPACE);
ASSERT(!tpl);
tpl = cache1.GetTemplate(cache_key, STRIP_BLANK_LINES);
ASSERT(!tpl);
// Try delete on a file-based template as well.
string filename = StringToTemplateFile(text2);
tpl = cache1.GetTemplate(filename, DO_NOT_STRIP);
ASSERT(tpl);
AssertExpandIs(tpl, &dict, text2, true);
cache1.Delete(filename);
tpl = cache1.GetTemplate(filename, DO_NOT_STRIP);
ASSERT(tpl);
AssertExpandIs(tpl, &dict, text2, true);
// Try re-adding a cache key after deleting it.
ASSERT(cache1.StringToTemplateCache(cache_key, text, DO_NOT_STRIP));
tpl = cache1.GetTemplate(cache_key, DO_NOT_STRIP);
ASSERT(tpl);
AssertExpandIs(tpl, &dict, text, true);
// Try ClearCache while we're at it.
cache1.ClearCache();
tpl = cache1.GetTemplate(cache_key, STRIP_BLANK_LINES);
ASSERT(!tpl);
// Test on the Template class, which has a different function name.
ASSERT(StringToTemplateCache(cache_key, text, DO_NOT_STRIP));
tpl = Template::GetTemplate(cache_key, DO_NOT_STRIP);
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.");
TemplateCache cache1;
const Template *tpl, *tpl2;
ASSERT(tpl = cache1.GetTemplate(filename_a, DO_NOT_STRIP));
ASSERT(tpl2 = cache1.GetTemplate(filename_b, DO_NOT_STRIP));
ASSERT(tpl2 != tpl); // different filenames.
ASSERT(tpl2 = cache1.GetTemplate(filename_a, STRIP_BLANK_LINES));
ASSERT(tpl2 != tpl); // different strip.
ASSERT(tpl2 = cache1.GetTemplate(filename_b, STRIP_BLANK_LINES));
ASSERT(tpl2 != tpl); // different filenames and strip.
ASSERT(tpl2 = cache1.GetTemplate(filename_a, DO_NOT_STRIP));
ASSERT(tpl2 == tpl); // same filename and strip.
}
static void TestReloadAllIfChangedLazyLoad() {
TemplateDictionary dict("empty");
TemplateCache cache1;
string filename = StringToTemplateFile("{valid template}");
string nonexistent = StringToTemplateFile("dummy");
unlink(nonexistent.c_str());
const Template* tpl = cache1.GetTemplate(filename, STRIP_WHITESPACE);
assert(tpl);
const Template* tpl2 = cache1.GetTemplate(nonexistent, STRIP_WHITESPACE);
assert(!tpl2);
StringToFile("exists now!", nonexistent);
tpl2 = cache1.GetTemplate(nonexistent, STRIP_WHITESPACE);
ASSERT(!tpl2);
cache1.ReloadAllIfChanged(TemplateCache::LAZY_RELOAD);
tpl = cache1.GetTemplate(filename, STRIP_WHITESPACE); // force the reload
tpl2 = cache1.GetTemplate(nonexistent, STRIP_WHITESPACE);
ASSERT(tpl2); // file exists now
unlink(nonexistent.c_str()); // here today...
cache1.ReloadAllIfChanged(TemplateCache::LAZY_RELOAD);
ASSERT(cache1.GetTemplate(filename, STRIP_WHITESPACE));
ASSERT(!cache1.GetTemplate(nonexistent, STRIP_WHITESPACE));
StringToFile("lazarus", nonexistent);
StringToFile("{new template}", filename);
tpl = cache1.GetTemplate(filename, STRIP_WHITESPACE);
AssertExpandIs(tpl, &dict, "{valid template}", true); // haven't reloaded
// But a different cache (say, the default) should load the new content.
const Template* tpl3 = Template::GetTemplate(filename, STRIP_WHITESPACE);
AssertExpandIs(tpl3, &dict, "{new template}", true);
cache1.ReloadAllIfChanged(TemplateCache::LAZY_RELOAD);
tpl = cache1.GetTemplate(filename, STRIP_WHITESPACE); // needed
AssertExpandIs(tpl, &dict, "{new template}", true);
tpl2 = cache1.GetTemplate(nonexistent, STRIP_WHITESPACE);
ASSERT(tpl2);
AssertExpandIs(tpl2, &dict, "lazarus", true);
// Ensure that string templates don't reload
const string cache_key_a = "cache key a";
const string text = "Test template 1";
const Template *str_tpl;
ASSERT(cache1.StringToTemplateCache(cache_key_a, text, DO_NOT_STRIP));
str_tpl = cache1.GetTemplate(cache_key_a, DO_NOT_STRIP);
AssertExpandIs(str_tpl, &dict, text, true);
cache1.ReloadAllIfChanged(TemplateCache::LAZY_RELOAD);
ASSERT(cache1.GetTemplate(cache_key_a, DO_NOT_STRIP) == str_tpl);
cache1.ClearCache();
}
static void TestReloadAllIfChangedImmediateLoad() {
TemplateDictionary dict("empty");
TemplateCache cache1;
TemplateCachePeer cache_peer(&cache1);
// Add templates
string filename1 = StringToTemplateFile("{valid template}");
string filename2 = StringToTemplateFile("{another valid template}");
const Template* tpl1 = cache1.GetTemplate(filename1,
STRIP_WHITESPACE);
assert(tpl1);
const Template* tpl2 = cache1.GetTemplate(filename2,
STRIP_WHITESPACE);
assert(tpl2);
StringToFile("{file1 contents changed}", filename1);
cache1.ReloadAllIfChanged(TemplateCache::IMMEDIATE_RELOAD);
TemplateCachePeer::TemplateCacheKey cache_key1(filename1, STRIP_WHITESPACE);
ASSERT(cache_peer.TemplateIsCached(cache_key1));
const Template* tpl1_post_reload = cache_peer.GetTemplate(filename1,
STRIP_WHITESPACE);
ASSERT(tpl1_post_reload != tpl1);
// Check that cache1's tpl1 has the new contents
AssertExpandIs(tpl1_post_reload, &dict, "{file1 contents changed}",
true);
// Ensure tpl2 is unchanged
TemplateCachePeer::TemplateCacheKey cache_key2(filename2, STRIP_WHITESPACE);
ASSERT(cache_peer.TemplateIsCached(cache_key2));
const Template* tpl2_post_reload = cache_peer.GetTemplate(filename2,
STRIP_WHITESPACE);
ASSERT(tpl2_post_reload == tpl2);
// Test delete & re-add: delete tpl2, and reload.
unlink(filename2.c_str());
cache1.ReloadAllIfChanged(TemplateCache::IMMEDIATE_RELOAD);
ASSERT(!cache_peer.GetTemplate(filename2, STRIP_WHITESPACE));
// Re-add tpl2 and ensure it reloads.
StringToFile("{re-add valid template contents}", filename2);
cache1.ReloadAllIfChanged(TemplateCache::IMMEDIATE_RELOAD);
ASSERT(cache_peer.GetTemplate(filename2, STRIP_WHITESPACE));
// Ensure that string templates don't reload
const string cache_key_a = "cache key a";
const string text = "Test template 1";
const Template *str_tpl;
ASSERT(cache1.StringToTemplateCache(cache_key_a, text, DO_NOT_STRIP));
str_tpl = cache1.GetTemplate(cache_key_a, DO_NOT_STRIP);
AssertExpandIs(str_tpl, &dict, text, true);
cache1.ReloadAllIfChanged(TemplateCache::IMMEDIATE_RELOAD);
ASSERT(cache1.GetTemplate(cache_key_a, DO_NOT_STRIP) == str_tpl);
cache1.ClearCache();
}
static void TestReloadImmediateWithDifferentSearchPaths() {
TemplateDictionary dict("empty");
TemplateCache cache1;
TemplateCachePeer cache_peer(&cache1);
const string pathA = PathJoin(FLAGS_test_tmpdir, "a/");
const string pathB = PathJoin(FLAGS_test_tmpdir, "b/");
CreateOrCleanTestDir(pathA);
CreateOrCleanTestDir(pathB);
cache1.SetTemplateRootDirectory(pathA);
cache1.AddAlternateTemplateRootDirectory(pathB);
ASSERT(cache1.template_root_directory() == pathA);
// Add b/foo
const string path_b_foo = PathJoin(pathB, "template_foo");
StringToFile("b/template_foo", path_b_foo);
ASSERT_STREQ(path_b_foo.c_str(),
cache1.FindTemplateFilename("template_foo").c_str());
// Add b/foo to the template cache.
cache1.GetTemplate("template_foo", DO_NOT_STRIP);
// Add a/foo
const string path_a_foo = PathJoin(pathA, "template_foo");
StringToFile("a/template_foo", path_a_foo);
ASSERT_STREQ(path_a_foo.c_str(),
cache1.FindTemplateFilename("template_foo").c_str());
// Now, on reload we pick up foo from the earlier search path: a/foo
cache1.ReloadAllIfChanged(TemplateCache::IMMEDIATE_RELOAD);
const Template* foo_post_reload = cache_peer.GetTemplate("template_foo",
STRIP_WHITESPACE);
AssertExpandIs(foo_post_reload, &dict, "a/template_foo",
true);
// Delete a/foo and reload. Now we pick up the next available foo: b/foo
unlink(path_a_foo.c_str());
cache1.ReloadAllIfChanged(TemplateCache::IMMEDIATE_RELOAD);
foo_post_reload = cache_peer.GetTemplate("template_foo",
STRIP_WHITESPACE);
AssertExpandIs(foo_post_reload, &dict, "b/template_foo",
true);
}
static void TestReloadLazyWithDifferentSearchPaths() {
// Identical test as above with but with LAZY_RELOAD
TemplateDictionary dict("empty");
TemplateCache cache1;
TemplateCachePeer cache_peer(&cache1);
const string pathA = PathJoin(FLAGS_test_tmpdir, "a/");
const string pathB = PathJoin(FLAGS_test_tmpdir, "b/");
CreateOrCleanTestDir(pathA);
CreateOrCleanTestDir(pathB);
cache1.SetTemplateRootDirectory(pathA);
cache1.AddAlternateTemplateRootDirectory(pathB);
ASSERT(cache1.template_root_directory() == pathA);
// Add b/foo
const string path_b_foo = PathJoin(pathB, "template_foo");
StringToFile("b/template_foo", path_b_foo);
ASSERT_STREQ(path_b_foo.c_str(),
cache1.FindTemplateFilename("template_foo").c_str());
// Add b/foo to the template cache.
cache1.GetTemplate("template_foo", DO_NOT_STRIP);
// Add a/foo
const string path_a_foo = PathJoin(pathA, "template_foo");
StringToFile("a/template_foo", path_a_foo);
ASSERT_STREQ(path_a_foo.c_str(),
cache1.FindTemplateFilename("template_foo").c_str());
// Now, on reload we pick up foo from the earlier search path: a/foo
cache1.ReloadAllIfChanged(TemplateCache::LAZY_RELOAD);
const Template* foo_post_reload = cache_peer.GetTemplate("template_foo",
STRIP_WHITESPACE);
AssertExpandIs(foo_post_reload, &dict, "a/template_foo",
true);
// Delete a/foo and reload. Now we pick up the next available foo: b/foo
unlink(path_a_foo.c_str());
cache1.ReloadAllIfChanged(TemplateCache::LAZY_RELOAD);
foo_post_reload = cache_peer.GetTemplate("template_foo",
STRIP_WHITESPACE);
AssertExpandIs(foo_post_reload, &dict, "b/template_foo",
true);
}
static void TestRefcounting() {
TemplateCache cache1;
TemplateCachePeer cache_peer(&cache1);
TemplateDictionary dict("dict");
// Add templates
string filename1 = StringToTemplateFile("{valid template}");
string filename2 = StringToTemplateFile("{another valid template}");
const Template* cache1_tpl1 = cache1.GetTemplate(filename1,
STRIP_WHITESPACE);
assert(cache1_tpl1);
const Template* cache1_tpl2 = cache1.GetTemplate(filename2,
STRIP_WHITESPACE);
assert(cache1_tpl2);
// Check refcount. It should be 2 -- one for the originalvalue
// when it's constructed, and one for the call to GetTemplate.
TemplateCachePeer::TemplateCacheKey cache_key1(filename1, STRIP_WHITESPACE);
ASSERT(cache_peer.Refcount(cache_key1) == 2);
TemplateCachePeer::TemplateCacheKey cache_key2(filename2, STRIP_WHITESPACE);
ASSERT(cache_peer.Refcount(cache_key2) == 2);
// Clone cache2 from cache1
TemplateCache* cache2 = cache1.Clone();
TemplateCachePeer cache_peer2(cache2);
// Check refcount was incremented. It should be the same for both caches.
ASSERT(cache_peer.Refcount(cache_key1) == 3);
ASSERT(cache_peer2.Refcount(cache_key1) == 3);
ASSERT(cache_peer.Refcount(cache_key2) == 3);
ASSERT(cache_peer2.Refcount(cache_key2) == 3);
// Check that the template ptrs in both caches are the same.
const Template* cache2_tpl1 = cache2->GetTemplate(filename1,
STRIP_WHITESPACE);
const Template* cache2_tpl2 = cache2->GetTemplate(filename2,
STRIP_WHITESPACE);
ASSERT(cache2_tpl1 == cache1_tpl1);
ASSERT(cache2_tpl2 == cache1_tpl2);
// GetTemplate should have augmented the refcount.
ASSERT(cache_peer.Refcount(cache_key1) == 4);
ASSERT(cache_peer2.Refcount(cache_key1) == 4);
ASSERT(cache_peer.Refcount(cache_key2) == 4);
ASSERT(cache_peer2.Refcount(cache_key2) == 4);
// Change tpl1 file contents and reload.
StringToFile("{file1 contents changed}", filename1);
cache2->ReloadAllIfChanged(TemplateCache::LAZY_RELOAD);
// Since the template will be reloaded into a new instance,
// GetTemplate will return new pointers. The older template
// pointer was moved to the freelist.
const Template* cache2_tpl1_post_reload = cache2->GetTemplate(
filename1, STRIP_WHITESPACE);
ASSERT(cache2_tpl1_post_reload != cache2_tpl1);
// Check that cache1's tpl1 has the new contents
AssertExpandIs(cache2_tpl1_post_reload, &dict, "{file1 contents changed}",
true);
// Ensure tpl2 is unchanged
const Template* cache2_tpl2_post_reload = cache2->GetTemplate(
filename2, STRIP_WHITESPACE);
ASSERT(cache2_tpl2_post_reload == cache2_tpl2);
// Now key1 points to different templates in cache1 and cache2.
// cache1's version should have a refcount of 3 (was 4, went down
// by 1 when cache2 dropped its reference to it). cache2's
// version should be 2 (one for the new file, 1 for the call to
// GetTemplate() that followed it), while key2 should have a
// refcount of 5 in both caches (due to the new call, above, to
// GetTemplate()).
ASSERT(cache_peer.Refcount(cache_key1) == 3);
ASSERT(cache_peer2.Refcount(cache_key1) == 2);
ASSERT(cache_peer.Refcount(cache_key2) == 5);
ASSERT(cache_peer2.Refcount(cache_key2) == 5);
const int old_delete_count = cache_peer.NumTotalTemplateDeletes();
// Clear up the cache2's freelist, this should drop all refcounts,
// due to the calls cache_peer2 made to
// GetTemplate(the-old-filename1), GetTemplate(the-new-filename1),
// and GetTemplate(filename2) (twice!)
cache_peer2.DoneWithGetTemplatePtrs();
ASSERT(cache_peer.Refcount(cache_key1) == 2);
ASSERT(cache_peer2.Refcount(cache_key1) == 1);
ASSERT(cache_peer.Refcount(cache_key2) == 3);
ASSERT(cache_peer2.Refcount(cache_key2) == 3);
// Make sure that deleting from the cache causes deletion.
// ClearCache() on peer1 should finally get rid of the old filename1.
cache_peer.ClearCache();
ASSERT(cache_peer.NumTotalTemplateDeletes() == old_delete_count + 1);
cache_peer2.ClearCache();
// Delete-count should go up by 2 as both the new tpl1, and tpl2, go away.
ASSERT(cache_peer.NumTotalTemplateDeletes() == old_delete_count + 3);
delete cache2;
}
static void TestDoneWithGetTemplatePtrs() {
TemplateCache cache1;
TemplateCachePeer cache_peer1(&cache1);
TemplateDictionary dict("dict");
// Add templates
string fname = StringToTemplateFile("{valid template}");
TemplateCachePeer::TemplateCacheKey cache_key(fname, STRIP_WHITESPACE);
string out;
int old_delete_count = cache_peer1.NumTotalTemplateDeletes();
// OK, let's get the templates in the cache.
ASSERT(cache1.ExpandWithData(fname, STRIP_WHITESPACE, &dict, NULL, &out));
// This should not have changed the delete-count.
ASSERT(cache_peer1.NumTotalTemplateDeletes() == old_delete_count);
// And the refcount should be 1.
ASSERT(cache_peer1.Refcount(cache_key) == 1);
// Same holds if we expand again.
ASSERT(cache1.ExpandWithData(fname, STRIP_WHITESPACE, &dict, NULL, &out));
ASSERT(cache_peer1.NumTotalTemplateDeletes() == old_delete_count);
ASSERT(cache_peer1.Refcount(cache_key) == 1);
// Now we delete from the cache. Should up the delete_count.
ASSERT(cache1.Delete(fname));
ASSERT(cache_peer1.NumTotalTemplateDeletes() == ++old_delete_count);
// Calling DoneWithGetTemplatePtrs() should be a noop -- we
// haven't called GetTemplate() yet.
cache1.DoneWithGetTemplatePtrs();
ASSERT(cache_peer1.NumTotalTemplateDeletes() == old_delete_count);
// Now do the same thing, but throw in a GetTemplate(). Now
// DoneWithGetTemplatePtrs() should still cause a delete, but only
// after a call to Delete() deletes the cache's refcount too.
ASSERT(cache1.ExpandWithData(fname, STRIP_WHITESPACE, &dict, NULL, &out));
cache1.GetTemplate(fname, STRIP_WHITESPACE);
cache1.DoneWithGetTemplatePtrs();
ASSERT(cache_peer1.NumTotalTemplateDeletes() == old_delete_count);
ASSERT(cache1.Delete(fname));
ASSERT(cache_peer1.NumTotalTemplateDeletes() == ++old_delete_count);
cache1.ClearCache();
ASSERT(cache_peer1.NumTotalTemplateDeletes() == old_delete_count);
// Now load in a replacement. The loading itself should cause a
// delete (no GetTemplate calls, so no need to involve the freelist).
ASSERT(cache1.ExpandWithData(fname, STRIP_WHITESPACE, &dict, NULL, &out));
StringToFile("{file1 contents changed}", fname);
cache1.ReloadAllIfChanged(TemplateCache::IMMEDIATE_RELOAD);
ASSERT(cache_peer1.NumTotalTemplateDeletes() == ++old_delete_count);
// DoneWithGetTemplatePtrs() should just be a noop.
cache1.DoneWithGetTemplatePtrs();
ASSERT(cache_peer1.NumTotalTemplateDeletes() == old_delete_count);
// Delete the new version of fname too!
cache1.Delete(fname);
ASSERT(cache_peer1.NumTotalTemplateDeletes() == ++old_delete_count);
// Now load in a replacement, but having done a GetTemplate() first.
// We need DoneWithGetTemplatePtrs() to delete, in this case.
ASSERT(cache1.ExpandWithData(fname, STRIP_WHITESPACE, &dict, NULL, &out));
cache1.GetTemplate(fname, STRIP_WHITESPACE);
ASSERT(cache_peer1.Refcount(cache_key) == 2);
StringToFile("{file1 contents changed}", fname);
cache1.ReloadAllIfChanged(TemplateCache::IMMEDIATE_RELOAD);
ASSERT(cache_peer1.NumTotalTemplateDeletes() == old_delete_count);
cache1.DoneWithGetTemplatePtrs();
ASSERT(cache_peer1.NumTotalTemplateDeletes() == ++old_delete_count);
// Delete the new version of fname too!
cache1.Delete(fname);
ASSERT(cache_peer1.NumTotalTemplateDeletes() == ++old_delete_count);
// Add a Clone() into the mix. Now Delete() calls, even from both
// caches, won't up the delete-count until we DoneWithGetTemplatePtrs()
// -- but only from the cache that called GetTemplate().
ASSERT(cache1.ExpandWithData(fname, STRIP_WHITESPACE, &dict, NULL, &out));
cache1.GetTemplate(fname, STRIP_WHITESPACE);
ASSERT(cache_peer1.Refcount(cache_key) == 2);
{
TemplateCache* cache2 = cache1.Clone();
TemplateCachePeer cache_peer2(cache2);
ASSERT(cache_peer1.Refcount(cache_key) == 3);
ASSERT(cache_peer2.Refcount(cache_key) == 3);
// Do all sorts of Delete()s.
StringToFile("{file1 contents changed}", fname);
cache1.ReloadAllIfChanged(TemplateCache::IMMEDIATE_RELOAD);
ASSERT(cache_peer1.Refcount(cache_key) == 1); // the new file
ASSERT(cache_peer2.Refcount(cache_key) == 2); // the old file
cache2->ReloadAllIfChanged(TemplateCache::IMMEDIATE_RELOAD);
// Each cache has a different copy of the new file.
ASSERT(cache_peer1.Refcount(cache_key) == 1); // the new file
ASSERT(cache_peer2.Refcount(cache_key) == 1); // the new file
ASSERT(cache1.Delete(fname)); // should delete the new file
ASSERT(cache_peer1.NumTotalTemplateDeletes() == ++old_delete_count);
ASSERT(cache2->Delete(fname));
ASSERT(cache_peer1.NumTotalTemplateDeletes() == ++old_delete_count);
cache2->DoneWithGetTemplatePtrs();
ASSERT(cache_peer1.NumTotalTemplateDeletes() == old_delete_count);
cache1.DoneWithGetTemplatePtrs();
ASSERT(cache_peer1.NumTotalTemplateDeletes() == ++old_delete_count);
cache1.ClearCache();
ASSERT(cache_peer1.NumTotalTemplateDeletes() == old_delete_count);
delete cache2;
}
// If we call DoneWithGetTemplatePtrs() while a clone points to the
// template, it won't delete the template yet.
ASSERT(cache1.ExpandWithData(fname, STRIP_WHITESPACE, &dict, NULL, &out));
{
TemplateCache* cache2 = cache1.Clone();
TemplateCachePeer cache_peer2(cache2);
StringToFile("{file1 contents changed}", fname);
cache1.ReloadAllIfChanged(TemplateCache::IMMEDIATE_RELOAD);
ASSERT(cache_peer1.NumTotalTemplateDeletes() == old_delete_count);
delete cache2;
ASSERT(cache_peer1.NumTotalTemplateDeletes() == ++old_delete_count);
}
cache1.ClearCache();
ASSERT(cache_peer1.NumTotalTemplateDeletes() == ++old_delete_count);
// If we throw an explicit GetTemplate() in, we still need
// DoneWithGetTemplatePtrs().
ASSERT(cache1.ExpandWithData(fname, STRIP_WHITESPACE, &dict, NULL, &out));
cache1.GetTemplate(fname, STRIP_WHITESPACE);
{
TemplateCache* cache2 = cache1.Clone();
TemplateCachePeer cache_peer2(cache2);
StringToFile("{file1 contents changed}", fname);
cache1.ReloadAllIfChanged(TemplateCache::IMMEDIATE_RELOAD);
cache1.DoneWithGetTemplatePtrs();
ASSERT(cache_peer1.NumTotalTemplateDeletes() == old_delete_count);
delete cache2;
ASSERT(cache_peer1.NumTotalTemplateDeletes() == ++old_delete_count);
}
cache1.ClearCache();
ASSERT(cache_peer1.NumTotalTemplateDeletes() == ++old_delete_count);
// Multiple GetTemplate()s should still all be cleared by
// DoneWithGetTemplatePtrs().
ASSERT(cache1.ExpandWithData(fname, STRIP_WHITESPACE, &dict, NULL, &out));
ASSERT(cache1.ExpandWithData(fname, STRIP_WHITESPACE, &dict, NULL, &out));
cache1.GetTemplate(fname, STRIP_WHITESPACE);
cache1.GetTemplate(fname, STRIP_WHITESPACE);
ASSERT(cache_peer1.Refcount(cache_key) == 3);
StringToFile("{file1 contents changed}", fname);
cache1.ReloadAllIfChanged(TemplateCache::IMMEDIATE_RELOAD);
cache1.DoneWithGetTemplatePtrs();
ASSERT(cache_peer1.NumTotalTemplateDeletes() == ++old_delete_count);
cache1.ClearCache();
ASSERT(cache_peer1.NumTotalTemplateDeletes() == ++old_delete_count);
// Calling ClearCache() deletes old templates too -- we don't even
// need to change the content.
ASSERT(cache1.ExpandWithData(fname, STRIP_WHITESPACE, &dict, NULL, &out));
cache1.GetTemplate(fname, STRIP_WHITESPACE);
cache1.GetTemplate(fname, STRIP_WHITESPACE);
cache1.ClearCache();
ASSERT(cache_peer1.NumTotalTemplateDeletes() == ++old_delete_count);
// So does deleting the cache object.
ASSERT(cache1.ExpandWithData(fname, STRIP_WHITESPACE, &dict, NULL, &out));
{
TemplateCache* cache2 = cache1.Clone();
TemplateCachePeer cache_peer2(cache2);
ASSERT(cache_peer1.Refcount(cache_key) == 2);
cache2->GetTemplate(fname, STRIP_WHITESPACE);
ASSERT(cache_peer1.Refcount(cache_key) == 3);
ASSERT(cache_peer2.Refcount(cache_key) == 3);
ASSERT(cache1.Delete(fname));
ASSERT(cache_peer1.NumTotalTemplateDeletes() == old_delete_count);
ASSERT(cache_peer2.Refcount(cache_key) == 2);
delete cache2;
}
ASSERT(cache_peer1.NumTotalTemplateDeletes() == ++old_delete_count);
}
static void TestCloneStringTemplates() {
TemplateCache cache1;
// Create & insert a string template
const string cache_key_a = "cache key a";
const string text = "Test template 1";
TemplateDictionary empty_dict("dict");
ASSERT(cache1.StringToTemplateCache(cache_key_a, text, DO_NOT_STRIP));
// Clone cache2 from cache1
TemplateCache* cache2 = cache1.Clone();
// Check that the string template was copied into cache2
const Template* cache2_tpl = cache2->GetTemplate(cache_key_a,
DO_NOT_STRIP);
ASSERT(cache2_tpl);
AssertExpandIs(cache2_tpl, &empty_dict, text, true);
delete cache2;
}
static void TestInclude() {
TemplateCache cache;
string incname = StringToTemplateFile("include & print file\n");
string tpl_file = StringToTemplateFile("hi {{>INC:h}} bar\n");
const Template* tpl = cache.GetTemplate(tpl_file, DO_NOT_STRIP);
ASSERT(tpl);
TemplateDictionary dict("dict");
AssertExpandWithCacheIs(&cache, tpl_file, DO_NOT_STRIP, &dict, NULL,
"hi bar\n", true);
dict.AddIncludeDictionary("INC")->SetFilename(incname);
AssertExpandWithCacheIs(&cache, tpl_file, DO_NOT_STRIP, &dict, NULL,
"hi include &amp; print file bar\n",
true);
}
// Make sure we don't deadlock when a template includes itself.
// This also tests we handle recursive indentation properly.
static void TestRecursiveInclude() {
TemplateCache cache;
string incname = StringToTemplateFile("hi {{>INC}} bar\n {{>INC}}!");
const Template* tpl = cache.GetTemplate(incname, DO_NOT_STRIP);
ASSERT(tpl);
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.
AssertExpandWithCacheIs(&cache, incname, DO_NOT_STRIP, &dict, NULL,
"hi hi bar\n ! bar\n hi bar\n !!",
true);
}
static void TestStringTemplateInclude() {
const string cache_key = "TestStringTemplateInclude";
const string cache_key_inc = "TestStringTemplateInclude-inc";
const string text = "<html>{{>INC}}</html>";
const string text_inc = "<div>\n<p>\nUser {{USER}}\n</div>";
TemplateCache cache;
ASSERT(cache.StringToTemplateCache(cache_key, text, DO_NOT_STRIP));
ASSERT(cache.StringToTemplateCache(cache_key_inc, text_inc, DO_NOT_STRIP));
const Template *tpl = cache.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>";
AssertExpandWithCacheIs(&cache, cache_key, DO_NOT_STRIP, &dict, NULL,
expected, true);
}
static void TestTemplateString() {
TemplateCache cache;
ASSERT(cache.StringToTemplateCache(kKey, kContent, DO_NOT_STRIP));
const Template *tpl = cache.GetTemplate(kKey, DO_NOT_STRIP);
ASSERT(tpl);
TemplateDictionary dict("dict");
AssertExpandWithCacheIs(&cache, "MY_KEY", DO_NOT_STRIP, &dict, NULL,
"content", true);
// Try retrieving with a char* rather than a TemplateString*.
tpl = cache.GetTemplate("MY_KEY", DO_NOT_STRIP);
ASSERT(tpl);
AssertExpandWithCacheIs(&cache, "MY_KEY", DO_NOT_STRIP, &dict, NULL,
"content", true);
// Delete with a char* rather than a TemplateString*.
cache.Delete("MY_KEY");
tpl = cache.GetTemplate("MY_KEY", DO_NOT_STRIP);
ASSERT(!tpl);
ASSERT(cache.StringToTemplateCache("MY_KEY", "content", DO_NOT_STRIP));
tpl = cache.GetTemplate(kKey, DO_NOT_STRIP);
ASSERT(tpl);
cache.Delete(kKey);
tpl = cache.GetTemplate("MY_KEY", DO_NOT_STRIP);
ASSERT(!tpl);
}
static void TestFreeze() {
TemplateCache cache;
TemplateDictionary dict("dict");
// Load some templates
string filename1 = StringToTemplateFile("{valid template}");
string filename2 = StringToTemplateFile("hi {{>INC:h}} bar\n");
const Template* cache_tpl1 = cache.GetTemplate(filename1, STRIP_WHITESPACE);
assert(cache_tpl1);
AssertExpandIs(cache_tpl1, &dict, "{valid template}", true);
const Template* cache_tpl2 = cache.GetTemplate(filename2, DO_NOT_STRIP);
assert(cache_tpl2);
static_cast<void>(cache_tpl2); // avoid unused var warning in opt mode
AssertExpandWithCacheIs(&cache, filename2, DO_NOT_STRIP, &dict, NULL,
"hi bar\n", true);
// Set the root directory
const string pathA = PathJoin(FLAGS_test_tmpdir, "a/");
CreateOrCleanTestDir(pathA);
cache.SetTemplateRootDirectory(pathA);
ASSERT(cache.template_root_directory() == pathA);
// Freeze the cache now, and test its impact.
cache.Freeze();
// 1. Loading new templates fails.
string filename3 = StringToTemplateFile("{yet another valid template}");
const Template* cache_tpl3 = cache.GetTemplate(filename3, STRIP_WHITESPACE);
assert(!cache_tpl3);
static_cast<void>(cache_tpl3); // avoid unused var warning in opt mode
// 2. Reloading existing templates fails.
StringToFile("{file1 contents changed}", filename1);
cache.ReloadAllIfChanged(TemplateCache::LAZY_RELOAD);
const Template* cache_tpl1_post_reload = cache.GetTemplate(
filename1, STRIP_WHITESPACE);
ASSERT(cache_tpl1_post_reload == cache_tpl1);
// Check that cache's tpl1 has the same old contents
AssertExpandIs(cache_tpl1_post_reload, &dict, "{valid template}",
true);
// 3. Cannot delete from a frozen cache.
cache.Delete(filename1);
ASSERT(cache.GetTemplate(filename1, STRIP_WHITESPACE));
// 4. Expand won't load an included template on-demand.
string incname = StringToTemplateFile("include & print file\n");
dict.AddIncludeDictionary("INC")->SetFilename(incname);
AssertExpandWithCacheIs(&cache, filename2, DO_NOT_STRIP, &dict, NULL,
"hi bar\n", false);
// 5. Cannot change template root directory.
const string pathB = PathJoin(FLAGS_test_tmpdir, "b/");
CreateOrCleanTestDir(pathB);
cache.SetTemplateRootDirectory(pathB);
ASSERT(cache.template_root_directory() == pathA); // Still the old path
CreateOrCleanTestDir(pathA);
CreateOrCleanTestDir(pathB);
}
};
int main(int argc, char** argv) {
CreateOrCleanTestDirAndSetAsTmpdir(FLAGS_test_tmpdir);
TemplateCacheUnittest::TestGetTemplate();
TemplateCacheUnittest::TestLoadTemplate();
TemplateCacheUnittest::TestStringGetTemplate();
TemplateCacheUnittest::TestStringToTemplateCacheWithStrip();
TemplateCacheUnittest::TestExpandNoLoad();
TemplateCacheUnittest::TestTemplateSearchPath();
TemplateCacheUnittest::TestDelete();
TemplateCacheUnittest::TestTemplateCache();
TemplateCacheUnittest::TestReloadAllIfChangedLazyLoad();
TemplateCacheUnittest::TestReloadAllIfChangedImmediateLoad();
TemplateCacheUnittest::TestReloadImmediateWithDifferentSearchPaths();
TemplateCacheUnittest::TestReloadLazyWithDifferentSearchPaths();
TemplateCacheUnittest::TestRefcounting();
TemplateCacheUnittest::TestDoneWithGetTemplatePtrs();
TemplateCacheUnittest::TestCloneStringTemplates();
TemplateCacheUnittest::TestInclude();
TemplateCacheUnittest::TestRecursiveInclude();
TemplateCacheUnittest::TestStringTemplateInclude();
TemplateCacheUnittest::TestTemplateString();
TemplateCacheUnittest::TestFreeze();
printf("DONE\n");
return 0;
}