Brian Silverman | 70325d6 | 2015-09-20 17:00:43 -0400 | [diff] [blame] | 1 | // Copyright (c) 2005, Google Inc. |
| 2 | // All rights reserved. |
| 3 | // |
| 4 | // Redistribution and use in source and binary forms, with or without |
| 5 | // modification, are permitted provided that the following conditions are |
| 6 | // met: |
| 7 | // |
| 8 | // * Redistributions of source code must retain the above copyright |
| 9 | // notice, this list of conditions and the following disclaimer. |
| 10 | // * Redistributions in binary form must reproduce the above |
| 11 | // copyright notice, this list of conditions and the following disclaimer |
| 12 | // in the documentation and/or other materials provided with the |
| 13 | // distribution. |
| 14 | // * Neither the name of Google Inc. nor the names of its |
| 15 | // contributors may be used to endorse or promote products derived from |
| 16 | // this software without specific prior written permission. |
| 17 | // |
| 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 | |
| 30 | // --- |
| 31 | // Author: csilvers@google.com (Craig Silverstein) |
| 32 | // |
| 33 | // This code is written to not use the google testing framework |
| 34 | // as much as possible, to make it easier to opensource. |
| 35 | |
| 36 | #include "config_for_unittests.h" |
| 37 | #include <stdio.h> |
| 38 | #include <string.h> |
| 39 | #include <assert.h> |
| 40 | #include <vector> |
| 41 | #include "base/arena.h" |
| 42 | #include <ctemplate/template_dictionary.h> |
| 43 | #include <ctemplate/template_modifiers.h> |
| 44 | #include <ctemplate/per_expand_data.h> |
| 45 | #include "tests/template_test_util.h" |
| 46 | #include "base/util.h" |
| 47 | TEST_INIT // defines RUN_ALL_TESTS |
| 48 | |
| 49 | using std::string; |
| 50 | using std::vector; |
| 51 | using GOOGLE_NAMESPACE::UnsafeArena; |
| 52 | using GOOGLE_NAMESPACE::DO_NOT_STRIP; |
| 53 | using GOOGLE_NAMESPACE::ExpandEmitter; |
| 54 | using GOOGLE_NAMESPACE::PerExpandData; |
| 55 | using GOOGLE_NAMESPACE::StaticTemplateString; |
| 56 | using GOOGLE_NAMESPACE::StringToTemplateCache; |
| 57 | using GOOGLE_NAMESPACE::TemplateDictionary; |
| 58 | using GOOGLE_NAMESPACE::TemplateDictionaryInterface; |
| 59 | using GOOGLE_NAMESPACE::TemplateDictionaryPeer; |
| 60 | using GOOGLE_NAMESPACE::TemplateString; |
| 61 | |
| 62 | #define ASSERT_STRSTR(text, substr) do { \ |
| 63 | if (!strstr((text), (substr))) { \ |
| 64 | printf("%s: %d: ASSERT FAILED: '%s' not in '%s'\n", \ |
| 65 | __FILE__, __LINE__, (substr), (text)); \ |
| 66 | assert(strstr((text), (substr))); \ |
| 67 | exit(1); \ |
| 68 | } \ |
| 69 | } while (0) |
| 70 | |
| 71 | |
| 72 | // test escape-functor that replaces all input with "foo" |
| 73 | class FooEscaper : public GOOGLE_NAMESPACE::TemplateModifier { |
| 74 | public: |
| 75 | void Modify(const char* in, size_t inlen, |
| 76 | const PerExpandData*, |
| 77 | ExpandEmitter* outbuf, const string& arg) const { |
| 78 | assert(arg.empty()); // we don't take an argument |
| 79 | outbuf->Emit("foo"); |
| 80 | } |
| 81 | }; |
| 82 | |
| 83 | // test escape-functor that replaces all input with "" |
| 84 | class NullEscaper : public GOOGLE_NAMESPACE::TemplateModifier { |
| 85 | public: |
| 86 | void Modify(const char* in, size_t inlen, |
| 87 | const PerExpandData*, |
| 88 | ExpandEmitter* outbuf, const string& arg) const { |
| 89 | assert(arg.empty()); // we don't take an argument |
| 90 | } |
| 91 | }; |
| 92 | |
| 93 | // first does javascript-escaping, then html-escaping |
| 94 | class DoubleEscaper : public GOOGLE_NAMESPACE::TemplateModifier { |
| 95 | public: |
| 96 | void Modify(const char* in, size_t inlen, |
| 97 | const PerExpandData* data, |
| 98 | ExpandEmitter* outbuf, const string& arg) const { |
| 99 | assert(arg.empty()); // we don't take an argument |
| 100 | string tmp = GOOGLE_NAMESPACE::javascript_escape(in, inlen); |
| 101 | GOOGLE_NAMESPACE::html_escape.Modify(tmp.data(), tmp.size(), data, outbuf, ""); |
| 102 | } |
| 103 | }; |
| 104 | |
| 105 | namespace { |
| 106 | |
| 107 | static const TemplateDictionary* GetSectionDict( |
| 108 | const TemplateDictionary* d, const char* name, int i) { |
| 109 | TemplateDictionaryPeer peer(d); |
| 110 | vector<const TemplateDictionary*> dicts; |
| 111 | EXPECT_GE(peer.GetSectionDictionaries(name, &dicts), i); |
| 112 | return dicts[i]; |
| 113 | } |
| 114 | static const TemplateDictionary* GetIncludeDict( |
| 115 | const TemplateDictionary* d, const char* name, int i) { |
| 116 | TemplateDictionaryPeer peer(d); |
| 117 | vector<const TemplateDictionary*> dicts; |
| 118 | EXPECT_GE(peer.GetIncludeDictionaries(name, &dicts), i); |
| 119 | return dicts[i]; |
| 120 | } |
| 121 | |
| 122 | static void SetUp() { |
| 123 | TemplateDictionary::SetGlobalValue("GLOBAL", "top"); |
| 124 | } |
| 125 | |
| 126 | TEST(TemplateDictionary, SetValueAndTemplateStringAndArena) { |
| 127 | // Try both with the arena, and without. |
| 128 | UnsafeArena arena(100); |
| 129 | // We run the test with arena twice to double-check we don't ever delete it |
| 130 | UnsafeArena* arenas[] = {&arena, &arena, NULL}; |
| 131 | for (int i = 0; i < sizeof(arenas)/sizeof(*arenas); ++i) { |
| 132 | TemplateDictionary dict(string("test_arena") + char('0'+i), arenas[i]); |
| 133 | |
| 134 | // Test copying char*s, strings, and explicit TemplateStrings |
| 135 | dict.SetValue("FOO", "foo"); |
| 136 | dict.SetValue(string("FOO2"), TemplateString("foo2andmore", 4)); |
| 137 | dict["FOO3"] = "foo3"; |
| 138 | dict[string("FOO4")] = TemplateString("foo4andmore", 4); |
| 139 | dict["FOO5"] = string("Olaf"); |
| 140 | dict["FOO6"] = 6; |
| 141 | dict["FOO7"] = long(7); |
| 142 | |
| 143 | TemplateDictionaryPeer peer(&dict); |
| 144 | // verify what happened |
| 145 | EXPECT_TRUE(peer.ValueIs("FOO", "foo")); |
| 146 | EXPECT_TRUE(peer.ValueIs("FOO2", "foo2")); |
| 147 | string dump; |
| 148 | dict.DumpToString(&dump); |
| 149 | char expected[256]; |
| 150 | snprintf(expected, sizeof(expected), |
| 151 | ("global dictionary {\n" |
| 152 | " BI_NEWLINE: >\n" |
| 153 | "<\n" |
| 154 | " BI_SPACE: > <\n" |
| 155 | " GLOBAL: >top<\n" |
| 156 | "};\n" |
| 157 | "dictionary 'test_arena%d' {\n" |
| 158 | " FOO: >foo<\n" |
| 159 | " FOO2: >foo2<\n" |
| 160 | " FOO3: >foo3<\n" |
| 161 | " FOO4: >foo4<\n" |
| 162 | " FOO5: >Olaf<\n" |
| 163 | " FOO6: >6<\n" |
| 164 | " FOO7: >7<\n" |
| 165 | "}\n"), i); |
| 166 | EXPECT_STREQ(dump.c_str(), expected); |
| 167 | } |
| 168 | } |
| 169 | |
| 170 | TEST(TemplateDictionary, SetValueWithoutCopy) { |
| 171 | UnsafeArena arena(100); |
| 172 | TemplateDictionary dict("Test arena", &arena); |
| 173 | |
| 174 | char value[32]; |
| 175 | snprintf(value, sizeof(value), "%s", "value"); |
| 176 | |
| 177 | const void* const ptr = arena.Alloc(0); |
| 178 | dict.SetValueWithoutCopy("key", value); |
| 179 | // We shouldn't have copied the value string. |
| 180 | EXPECT_EQ(ptr, arena.Alloc(0)); |
| 181 | |
| 182 | TemplateDictionaryPeer peer(&dict); |
| 183 | EXPECT_TRUE(peer.ValueIs("key", "value")); |
| 184 | // If our content changes, so does what's in the dictionary -- but |
| 185 | // only the contents of the buffer, not its length! |
| 186 | snprintf(value, sizeof(value), "%s", "not_value"); |
| 187 | EXPECT_TRUE(peer.ValueIs("key", "not_v")); // sizeof("not_v") == sizeof("value") |
| 188 | } |
| 189 | |
| 190 | TEST(TemplateDictionary, SetIntValue) { |
| 191 | TemplateDictionary dict("test_SetIntValue", NULL); |
| 192 | TemplateDictionaryPeer peer(&dict); |
| 193 | |
| 194 | dict.SetIntValue("INT", 5); |
| 195 | // - is an illegal varname in templates, but perfectly fine in dicts |
| 196 | dict.SetIntValue("-INT", -5); |
| 197 | |
| 198 | EXPECT_TRUE(peer.ValueIs("INT", "5")); |
| 199 | EXPECT_TRUE(peer.ValueIs("-INT", "-5")); |
| 200 | string dump; |
| 201 | dict.DumpToString(&dump); |
| 202 | ASSERT_STRSTR(dump.c_str(), "\n INT: >5<\n"); |
| 203 | ASSERT_STRSTR(dump.c_str(), "\n -INT: >-5<\n"); |
| 204 | |
| 205 | } |
| 206 | |
| 207 | TEST(TemplateDictionary, SetFormattedValue) { |
| 208 | TemplateDictionary dict("test_SetFormattedValue", NULL); |
| 209 | TemplateDictionaryPeer peer(&dict); |
| 210 | |
| 211 | dict.SetFormattedValue(TemplateString("PRINTF", sizeof("PRINTF")-1), |
| 212 | "%s test %04d", "template test", 1); |
| 213 | |
| 214 | EXPECT_TRUE(peer.ValueIs("PRINTF", "template test test 0001")); |
| 215 | string dump; |
| 216 | dict.DumpToString(&dump); |
| 217 | ASSERT_STRSTR(dump.c_str(), "\n PRINTF: >template test test 0001<\n"); |
| 218 | |
| 219 | // Now test something of size 4k or so, where we can't use scratchbuf |
| 220 | dict.SetFormattedValue(TemplateString("PRINTF", sizeof("PRINTF")-1), |
| 221 | "%s test %04444d", "template test", 2); |
| 222 | string expected("template test test "); |
| 223 | for (int i = 0; i < 4443; ++i) |
| 224 | expected.append("0"); |
| 225 | expected.append("2"); |
| 226 | EXPECT_TRUE(peer.ValueIs("PRINTF", expected)); |
| 227 | string dump2; |
| 228 | dict.DumpToString(&dump2); |
| 229 | expected = string("\n PRINTF: >") + expected + string("<\n"); |
| 230 | ASSERT_STRSTR(dump2.c_str(), expected.c_str()); |
| 231 | } |
| 232 | |
| 233 | TEST(TemplateDictionary, SetEscapedValue) { |
| 234 | TemplateDictionary dict("test_SetEscapedValue", NULL); |
| 235 | TemplateDictionaryPeer peer(&dict); |
| 236 | |
| 237 | dict.SetEscapedValue("hardest HTML", |
| 238 | "<A HREF='foo'\nid=\"bar\t\t&&\vbaz\">", |
| 239 | GOOGLE_NAMESPACE::html_escape); |
| 240 | dict.SetEscapedValue("hardest JS", |
| 241 | ("f = 'foo';\r\n\tprint \"\\&foo = \b\", \"foo\""), |
| 242 | GOOGLE_NAMESPACE::javascript_escape); |
| 243 | dict.SetEscapedValue("query escape 0", "", |
| 244 | GOOGLE_NAMESPACE::url_query_escape); |
| 245 | |
| 246 | EXPECT_TRUE(peer.ValueIs("hardest HTML", |
| 247 | "<A HREF='foo' id="bar && " |
| 248 | "baz">")); |
| 249 | EXPECT_TRUE(peer.ValueIs("hardest JS", |
| 250 | "f \\x3d \\x27foo\\x27;\\r\\n\\tprint \\x22\\\\\\x26" |
| 251 | "foo \\x3d \\b\\x22, \\x22foo\\x22")); |
| 252 | EXPECT_TRUE(peer.ValueIs("query escape 0", "")); |
| 253 | |
| 254 | // Test using hand-made modifiers. |
| 255 | FooEscaper foo_escaper; |
| 256 | dict.SetEscapedValue("easy foo", "hello there!", |
| 257 | FooEscaper()); |
| 258 | dict.SetEscapedValue("harder foo", "so much to say\nso many foos", |
| 259 | foo_escaper); |
| 260 | DoubleEscaper double_escaper; |
| 261 | dict.SetEscapedValue("easy double", "doo", |
| 262 | double_escaper); |
| 263 | dict.SetEscapedValue("harder double", "<A HREF='foo'>\n", |
| 264 | DoubleEscaper()); |
| 265 | dict.SetEscapedValue("hardest double", |
| 266 | "print \"<A HREF='foo'>\";\r\n\\1;", |
| 267 | double_escaper); |
| 268 | |
| 269 | EXPECT_TRUE(peer.ValueIs("easy foo", "foo")); |
| 270 | EXPECT_TRUE(peer.ValueIs("harder foo", "foo")); |
| 271 | EXPECT_TRUE(peer.ValueIs("easy double", "doo")); |
| 272 | EXPECT_TRUE(peer.ValueIs("harder double", |
| 273 | "\\x3cA HREF\\x3d\\x27foo\\x27\\x3e\\n")); |
| 274 | EXPECT_TRUE(peer.ValueIs("hardest double", |
| 275 | "print \\x22\\x3cA HREF\\x3d\\x27foo\\x27\\x3e\\x22;" |
| 276 | "\\r\\n\\\\1;")); |
| 277 | } |
| 278 | |
| 279 | TEST(TemplateDictionary, SetEscapedFormattedValue) { |
| 280 | TemplateDictionary dict("test_SetEscapedFormattedValue", NULL); |
| 281 | TemplateDictionaryPeer peer(&dict); |
| 282 | |
| 283 | dict.SetEscapedFormattedValue("HTML", GOOGLE_NAMESPACE::html_escape, |
| 284 | "This is <%s> #%.4f", "a & b", 1.0/3); |
| 285 | dict.SetEscapedFormattedValue("PRE", GOOGLE_NAMESPACE::pre_escape, |
| 286 | "if %s x = %.4f;", "(a < 1 && b > 2)\n\t", 1.0/3); |
| 287 | dict.SetEscapedFormattedValue("URL", GOOGLE_NAMESPACE::url_query_escape, |
| 288 | "pageviews-%s", "r?egex"); |
| 289 | dict.SetEscapedFormattedValue("XML", GOOGLE_NAMESPACE::xml_escape, |
| 290 | "This&is%s -- ok?", "just&"); |
| 291 | |
| 292 | EXPECT_TRUE(peer.ValueIs("HTML", |
| 293 | "This is <a & b> #0.3333")); |
| 294 | EXPECT_TRUE(peer.ValueIs("PRE", |
| 295 | "if (a < 1 && b > 2)\n\t x = 0.3333;")); |
| 296 | EXPECT_TRUE(peer.ValueIs("URL", "pageviews-r%3Fegex")); |
| 297 | |
| 298 | EXPECT_TRUE(peer.ValueIs("XML", "This&isjust& -- ok?")); |
| 299 | } |
| 300 | |
| 301 | static const StaticTemplateString kSectName = |
| 302 | STS_INIT(kSectName, "test_SetAddSectionDictionary"); |
| 303 | |
| 304 | TEST(TemplateDictionary, AddSectionDictionary) { |
| 305 | // For fun, we'll make this constructor take a static template string. |
| 306 | TemplateDictionary dict(kSectName, NULL); |
| 307 | TemplateDictionaryPeer peer(&dict); |
| 308 | dict.SetValue("TOPLEVEL", "foo"); |
| 309 | dict.SetValue("TOPLEVEL2", "foo2"); |
| 310 | |
| 311 | TemplateDictionary* subdict_1a = dict.AddSectionDictionary("section1"); |
| 312 | // This is the same dict, but name is specified a different way. |
| 313 | TemplateDictionary* subdict_1b = dict.AddSectionDictionary( |
| 314 | TemplateString("section1__ignored__", strlen("section1"))); |
| 315 | TemplateDictionaryPeer subdict_1a_peer(subdict_1a); |
| 316 | TemplateDictionaryPeer subdict_1b_peer(subdict_1b); |
| 317 | subdict_1a->SetValue("SUBLEVEL", "subfoo"); |
| 318 | subdict_1b->SetValue("SUBLEVEL", "subbar"); |
| 319 | |
| 320 | TemplateDictionary* subdict_2 = dict.AddSectionDictionary("section2"); |
| 321 | TemplateDictionaryPeer subdict_2_peer(subdict_2); |
| 322 | subdict_2->SetValue("TOPLEVEL", "bar"); // overriding top dict |
| 323 | TemplateDictionary* subdict_2_1 = subdict_2->AddSectionDictionary("sub"); |
| 324 | TemplateDictionaryPeer subdict_2_1_peer(subdict_2_1); |
| 325 | subdict_2_1->SetIntValue("GLOBAL", 21); // overrides value in setUp() |
| 326 | |
| 327 | // Verify that all variables that should be look-up-able are, and that |
| 328 | // we have proper precedence. |
| 329 | EXPECT_TRUE(peer.ValueIs("GLOBAL", "top")); |
| 330 | EXPECT_TRUE(peer.ValueIs("TOPLEVEL", "foo")); |
| 331 | EXPECT_TRUE(peer.ValueIs("TOPLEVEL2", "foo2")); |
| 332 | EXPECT_TRUE(peer.ValueIs("SUBLEVEL", "")); |
| 333 | |
| 334 | EXPECT_TRUE(subdict_1a_peer.ValueIs("GLOBAL", "top")); |
| 335 | EXPECT_TRUE(subdict_1a_peer.ValueIs("TOPLEVEL", "foo")); |
| 336 | EXPECT_TRUE(subdict_1a_peer.ValueIs("TOPLEVEL2", "foo2")); |
| 337 | EXPECT_TRUE(subdict_1a_peer.ValueIs("SUBLEVEL", "subfoo")); |
| 338 | |
| 339 | EXPECT_TRUE(subdict_1b_peer.ValueIs("GLOBAL", "top")); |
| 340 | EXPECT_TRUE(subdict_1b_peer.ValueIs("TOPLEVEL", "foo")); |
| 341 | EXPECT_TRUE(subdict_1b_peer.ValueIs("TOPLEVEL2", "foo2")); |
| 342 | EXPECT_TRUE(subdict_1b_peer.ValueIs("SUBLEVEL", "subbar")); |
| 343 | |
| 344 | EXPECT_TRUE(subdict_2_peer.ValueIs("GLOBAL", "top")); |
| 345 | EXPECT_TRUE(subdict_2_peer.ValueIs("TOPLEVEL", "bar")); |
| 346 | EXPECT_TRUE(subdict_2_peer.ValueIs("TOPLEVEL2", "foo2")); |
| 347 | EXPECT_TRUE(subdict_2_peer.ValueIs("SUBLEVEL", "")); |
| 348 | |
| 349 | EXPECT_TRUE(subdict_2_1_peer.ValueIs("GLOBAL", "21")); |
| 350 | EXPECT_TRUE(subdict_2_1_peer.ValueIs("TOPLEVEL", "bar")); |
| 351 | EXPECT_TRUE(subdict_2_1_peer.ValueIs("TOPLEVEL2", "foo2")); |
| 352 | EXPECT_TRUE(subdict_2_1_peer.ValueIs("SUBLEVEL", "")); |
| 353 | |
| 354 | // Verify that everyone knows about its sub-dictionaries, and also |
| 355 | // that these go 'up the chain' on lookup failure |
| 356 | EXPECT_FALSE(peer.IsHiddenSection("section1")); |
| 357 | EXPECT_FALSE(peer.IsHiddenSection("section2")); |
| 358 | EXPECT_TRUE(peer.IsHiddenSection("section3")); |
| 359 | EXPECT_TRUE(peer.IsHiddenSection("sub")); |
| 360 | EXPECT_FALSE(subdict_1a_peer.IsHiddenSection("section1")); |
| 361 | EXPECT_TRUE(subdict_1a_peer.IsHiddenSection("sub")); |
| 362 | EXPECT_FALSE(subdict_2_peer.IsHiddenSection("sub")); |
| 363 | EXPECT_FALSE(subdict_2_1_peer.IsHiddenSection("sub")); |
| 364 | |
| 365 | // We should get the dictionary-lengths right as well |
| 366 | vector<const TemplateDictionary*> dummy; |
| 367 | EXPECT_EQ(2, peer.GetSectionDictionaries("section1", &dummy)); |
| 368 | EXPECT_EQ(1, peer.GetSectionDictionaries("section2", &dummy)); |
| 369 | EXPECT_EQ(1, subdict_2_peer.GetSectionDictionaries("sub", &dummy)); |
| 370 | // Test some of the values |
| 371 | EXPECT_TRUE(TemplateDictionaryPeer(GetSectionDict(&dict, "section1", 0)) |
| 372 | .ValueIs("SUBLEVEL", "subfoo")); |
| 373 | EXPECT_TRUE(TemplateDictionaryPeer(GetSectionDict(&dict, "section1", 1)) |
| 374 | .ValueIs("SUBLEVEL", "subbar")); |
| 375 | EXPECT_TRUE(TemplateDictionaryPeer(GetSectionDict(&dict, "section2", 0)) |
| 376 | .ValueIs("TOPLEVEL", "bar")); |
| 377 | EXPECT_TRUE(TemplateDictionaryPeer( |
| 378 | GetSectionDict(GetSectionDict(&dict, "section2", 0), "sub", 0)) |
| 379 | .ValueIs("TOPLEVEL", "bar")); |
| 380 | EXPECT_TRUE(TemplateDictionaryPeer( |
| 381 | GetSectionDict(GetSectionDict(&dict, "section2", 0), "sub", 0)) |
| 382 | .ValueIs("GLOBAL", "21")); |
| 383 | |
| 384 | // Make sure we're making descriptive names |
| 385 | EXPECT_STREQ(dict.name().c_str(), |
| 386 | "test_SetAddSectionDictionary"); |
| 387 | EXPECT_STREQ(subdict_1a->name().c_str(), |
| 388 | "test_SetAddSectionDictionary/section1#1"); |
| 389 | EXPECT_STREQ(subdict_1b->name().c_str(), |
| 390 | "test_SetAddSectionDictionary/section1#2"); |
| 391 | EXPECT_STREQ(subdict_2->name().c_str(), |
| 392 | "test_SetAddSectionDictionary/section2#1"); |
| 393 | EXPECT_STREQ(subdict_2_1->name().c_str(), |
| 394 | "test_SetAddSectionDictionary/section2#1/sub#1"); |
| 395 | |
| 396 | // Finally, we can test the whole kit and kaboodle |
| 397 | string dump; |
| 398 | dict.DumpToString(&dump); |
| 399 | const char* const expected = |
| 400 | ("global dictionary {\n" |
| 401 | " BI_NEWLINE: >\n" |
| 402 | "<\n" |
| 403 | " BI_SPACE: > <\n" |
| 404 | " GLOBAL: >top<\n" |
| 405 | "};\n" |
| 406 | "dictionary 'test_SetAddSectionDictionary' {\n" |
| 407 | " TOPLEVEL: >foo<\n" |
| 408 | " TOPLEVEL2: >foo2<\n" |
| 409 | " section section1 (dict 1 of 2) -->\n" |
| 410 | " dictionary 'test_SetAddSectionDictionary/section1#1' {\n" |
| 411 | " SUBLEVEL: >subfoo<\n" |
| 412 | " }\n" |
| 413 | " section section1 (dict 2 of 2) -->\n" |
| 414 | " dictionary 'test_SetAddSectionDictionary/section1#2' {\n" |
| 415 | " SUBLEVEL: >subbar<\n" |
| 416 | " }\n" |
| 417 | " section section2 (dict 1 of 1) -->\n" |
| 418 | " dictionary 'test_SetAddSectionDictionary/section2#1' {\n" |
| 419 | " TOPLEVEL: >bar<\n" |
| 420 | " section sub (dict 1 of 1) -->\n" |
| 421 | " dictionary 'test_SetAddSectionDictionary/section2#1/sub#1' {\n" |
| 422 | " GLOBAL: >21<\n" |
| 423 | " }\n" |
| 424 | " }\n" |
| 425 | "}\n"); |
| 426 | EXPECT_STREQ(dump.c_str(), expected); |
| 427 | } |
| 428 | |
| 429 | TEST(TemplateDictionary, ShowSection) { |
| 430 | TemplateDictionary dict("test_SetShowSection", NULL); |
| 431 | // Let's say what filename dict is associated with |
| 432 | dict.SetFilename("bigmamainclude!.tpl"); |
| 433 | dict.SetValue("TOPLEVEL", "foo"); |
| 434 | dict.SetValue("TOPLEVEL2", "foo2"); |
| 435 | dict.ShowSection("section1"); |
| 436 | dict.ShowSection("section2"); |
| 437 | // Test calling ShowSection twice on the same section |
| 438 | dict.ShowSection("section2"); |
| 439 | // Test that ShowSection is a no-op if called after AddSectionDictionary() |
| 440 | TemplateDictionary* subdict = dict.AddSectionDictionary("section3"); |
| 441 | TemplateDictionaryPeer subdict_peer(subdict); |
| 442 | subdict->SetValue("TOPLEVEL", "bar"); |
| 443 | dict.ShowSection("section3"); |
| 444 | |
| 445 | EXPECT_TRUE(subdict_peer.ValueIs("TOPLEVEL", "bar")); |
| 446 | |
| 447 | // Since ShowSection() doesn't return a sub-dict, the only way to |
| 448 | // probe what the dicts look like is via Dump() |
| 449 | string dump; |
| 450 | dict.DumpToString(&dump); |
| 451 | const char* const expected = |
| 452 | ("global dictionary {\n" |
| 453 | " BI_NEWLINE: >\n" |
| 454 | "<\n" |
| 455 | " BI_SPACE: > <\n" |
| 456 | " GLOBAL: >top<\n" |
| 457 | "};\n" |
| 458 | "dictionary 'test_SetShowSection (intended for bigmamainclude!.tpl)' {\n" |
| 459 | " TOPLEVEL: >foo<\n" |
| 460 | " TOPLEVEL2: >foo2<\n" |
| 461 | " section section1 (dict 1 of 1) -->\n" |
| 462 | " dictionary 'empty dictionary' {\n" |
| 463 | " }\n" |
| 464 | " section section2 (dict 1 of 1) -->\n" |
| 465 | " dictionary 'empty dictionary' {\n" |
| 466 | " }\n" |
| 467 | " section section3 (dict 1 of 1) -->\n" |
| 468 | " dictionary 'test_SetShowSection/section3#1' {\n" |
| 469 | " TOPLEVEL: >bar<\n" |
| 470 | " }\n" |
| 471 | "}\n"); |
| 472 | EXPECT_STREQ(dump.c_str(), expected); |
| 473 | } |
| 474 | |
| 475 | TEST(TemplateDictionary, SetValueAndShowSection) { |
| 476 | TemplateDictionary dict("test_SetValueAndShowSection"); |
| 477 | TemplateDictionaryPeer peer(&dict); |
| 478 | dict.SetValue("TOPLEVEL", "foo"); |
| 479 | |
| 480 | dict.SetValueAndShowSection("INSEC", "bar", "SEC1"); |
| 481 | dict.SetValueAndShowSection("NOTINSEC", "", "SEC2"); |
| 482 | dict.SetValueAndShowSection("NOTINSEC2", NULL, "SEC3"); |
| 483 | |
| 484 | EXPECT_FALSE(peer.IsHiddenSection("SEC1")); |
| 485 | EXPECT_TRUE(peer.IsHiddenSection("SEC2")); |
| 486 | EXPECT_TRUE(peer.IsHiddenSection("SEC3")); |
| 487 | |
| 488 | // Again, we don't get subdicts, so we have to dump to check values |
| 489 | string dump; |
| 490 | dict.DumpToString(&dump); |
| 491 | const char* const expected = |
| 492 | ("global dictionary {\n" |
| 493 | " BI_NEWLINE: >\n" |
| 494 | "<\n" |
| 495 | " BI_SPACE: > <\n" |
| 496 | " GLOBAL: >top<\n" |
| 497 | "};\n" |
| 498 | "dictionary 'test_SetValueAndShowSection' {\n" |
| 499 | " TOPLEVEL: >foo<\n" |
| 500 | " section SEC1 (dict 1 of 1) -->\n" |
| 501 | " dictionary 'test_SetValueAndShowSection/SEC1#1' {\n" |
| 502 | " INSEC: >bar<\n" |
| 503 | " }\n" |
| 504 | "}\n"); |
| 505 | EXPECT_STREQ(dump.c_str(), expected); |
| 506 | } |
| 507 | |
| 508 | TEST(TemplateDictionary, SetTemplateGlobalValue) { |
| 509 | // The functionality involving it passing across the included dictionaries |
| 510 | // is also tested in TestAddIncludeDictionary |
| 511 | TemplateDictionary dict("test_SetTemplateGlobalValue", NULL); |
| 512 | TemplateDictionary* subdict = dict.AddSectionDictionary("section1"); |
| 513 | TemplateDictionary* subsubdict = |
| 514 | subdict->AddSectionDictionary("section1's child"); |
| 515 | TemplateDictionary* includedict = dict.AddIncludeDictionary("include1"); |
| 516 | |
| 517 | TemplateDictionaryPeer peer(&dict); |
| 518 | TemplateDictionaryPeer subdict_peer(subdict); |
| 519 | TemplateDictionaryPeer subsubdict_peer(subsubdict); |
| 520 | TemplateDictionaryPeer includedict_peer(includedict); |
| 521 | |
| 522 | // Setting a template value after sub dictionaries are created should |
| 523 | // affect the sub dictionaries as well. |
| 524 | dict.SetTemplateGlobalValue("TEMPLATEVAL", "templateval"); |
| 525 | EXPECT_TRUE(peer.ValueIs("TEMPLATEVAL", "templateval")); |
| 526 | EXPECT_TRUE(subdict_peer.ValueIs("TEMPLATEVAL", "templateval")); |
| 527 | EXPECT_TRUE(subsubdict_peer.ValueIs("TEMPLATEVAL", "templateval")); |
| 528 | EXPECT_TRUE(includedict_peer.ValueIs("TEMPLATEVAL", "templateval")); |
| 529 | |
| 530 | // sub dictionaries after you set the template value should also |
| 531 | // get the template value |
| 532 | TemplateDictionary* subdict2 = dict.AddSectionDictionary("section2"); |
| 533 | TemplateDictionary* includedict2 = dict.AddIncludeDictionary("include2"); |
| 534 | TemplateDictionaryPeer subdict2_peer(subdict2); |
| 535 | TemplateDictionaryPeer includedict2_peer(includedict2); |
| 536 | |
| 537 | EXPECT_TRUE(subdict2_peer.ValueIs("TEMPLATEVAL", "templateval")); |
| 538 | EXPECT_TRUE(includedict2_peer.ValueIs("TEMPLATEVAL", "templateval")); |
| 539 | |
| 540 | // setting a template value on a sub dictionary should affect all the other |
| 541 | // sub dictionaries and the parent as well |
| 542 | subdict->SetTemplateGlobalValue("TEMPLATEVAL2", "templateval2"); |
| 543 | EXPECT_TRUE(peer.ValueIs("TEMPLATEVAL2", "templateval2")); |
| 544 | EXPECT_TRUE(subdict_peer.ValueIs("TEMPLATEVAL2", "templateval2")); |
| 545 | EXPECT_TRUE(subsubdict_peer.ValueIs("TEMPLATEVAL2", "templateval2")); |
| 546 | EXPECT_TRUE(includedict_peer.ValueIs("TEMPLATEVAL2", "templateval2")); |
| 547 | EXPECT_TRUE(subdict2_peer.ValueIs("TEMPLATEVAL2", "templateval2")); |
| 548 | EXPECT_TRUE(includedict2_peer.ValueIs("TEMPLATEVAL2", "templateval2")); |
| 549 | |
| 550 | includedict->SetTemplateGlobalValue("TEMPLATEVAL3", "templateval3"); |
| 551 | EXPECT_TRUE(peer.ValueIs("TEMPLATEVAL3", "templateval3")); |
| 552 | EXPECT_TRUE(subdict_peer.ValueIs("TEMPLATEVAL3", "templateval3")); |
| 553 | EXPECT_TRUE(subsubdict_peer.ValueIs("TEMPLATEVAL3", "templateval3")); |
| 554 | EXPECT_TRUE(includedict_peer.ValueIs("TEMPLATEVAL3", "templateval3")); |
| 555 | EXPECT_TRUE(subdict2_peer.ValueIs("TEMPLATEVAL3", "templateval3")); |
| 556 | EXPECT_TRUE(includedict2_peer.ValueIs("TEMPLATEVAL3", "templateval3")); |
| 557 | |
| 558 | // you should be able to override a template value with a regular value |
| 559 | // and the overwritten regular value should pass on to its children |
| 560 | subdict->SetValue("TEMPLATEVAL2", "subdictval"); |
| 561 | includedict->SetValue("TEMPLATEVAL2", "includedictval"); |
| 562 | EXPECT_TRUE(peer.ValueIs("TEMPLATEVAL2", "templateval2")); |
| 563 | EXPECT_TRUE(subdict_peer.ValueIs("TEMPLATEVAL2", "subdictval")); |
| 564 | EXPECT_TRUE(subsubdict_peer.ValueIs("TEMPLATEVAL2", "subdictval")); |
| 565 | EXPECT_TRUE(includedict_peer.ValueIs("TEMPLATEVAL2", "includedictval")); |
| 566 | EXPECT_TRUE(subdict2_peer.ValueIs("TEMPLATEVAL2", "templateval2")); |
| 567 | EXPECT_TRUE(includedict2_peer.ValueIs("TEMPLATEVAL2", "templateval2")); |
| 568 | |
| 569 | // A section shown template-globally will be shown in all its children. |
| 570 | dict.ShowTemplateGlobalSection("ShownTemplateGlobalSection"); |
| 571 | EXPECT_FALSE(peer.IsHiddenSection("ShownTemplateGlobalSection")); |
| 572 | |
| 573 | EXPECT_FALSE(subdict2_peer.IsHiddenSection("ShownTemplateGlobalSection")); |
| 574 | EXPECT_FALSE(subsubdict_peer.IsHiddenSection("ShownTemplateGlobalSection")); |
| 575 | |
| 576 | // Showing a template-global section in a child will show it in all templates |
| 577 | // in the tree |
| 578 | subdict->ShowTemplateGlobalSection("ShownFromAChild"); |
| 579 | EXPECT_FALSE(peer.IsHiddenSection("ShownFromAChild")); |
| 580 | EXPECT_FALSE(subsubdict_peer.IsHiddenSection("ShownFromAChild")); |
| 581 | |
| 582 | // Asking for a section that doesn't exist shouldn't cause infinite recursion |
| 583 | peer.IsHiddenSection("NAVBAR_SECTION"); |
| 584 | } |
| 585 | |
| 586 | TEST(TemplateDictionary, SetTemplateGlobalValueWithoutCopy) { |
| 587 | UnsafeArena arena(100); |
| 588 | TemplateDictionary dict("Test arena", &arena); |
| 589 | TemplateDictionaryPeer peer(&dict); |
| 590 | |
| 591 | char value[32]; |
| 592 | snprintf(value, sizeof(value), "%s", "value"); |
| 593 | |
| 594 | const void* const ptr = arena.Alloc(0); |
| 595 | dict.SetTemplateGlobalValueWithoutCopy("key", value); |
| 596 | // We shouldn't have copied the value string. |
| 597 | EXPECT_EQ(ptr, arena.Alloc(0)); |
| 598 | |
| 599 | EXPECT_TRUE(peer.ValueIs("key", "value")); |
| 600 | // If our content changes, so does what's in the dictionary -- but |
| 601 | // only the contents of the buffer, not its length! |
| 602 | snprintf(value, sizeof(value), "%s", "not_value"); |
| 603 | EXPECT_TRUE(peer.ValueIs("key", "not_v")); // "not_v" size == value" size |
| 604 | } |
| 605 | |
| 606 | TEST(TemplateDictionary, AddIncludeDictionary) { |
| 607 | TemplateDictionary dict("test_SetAddIncludeDictionary", NULL); |
| 608 | TemplateDictionaryPeer peer(&dict); |
| 609 | dict.SetValue("TOPLEVEL", "foo"); |
| 610 | dict.SetValue("TOPLEVEL2", "foo2"); |
| 611 | dict.SetTemplateGlobalValue("TEMPLATELEVEL", "foo3"); |
| 612 | |
| 613 | TemplateDictionary* subdict_1a = dict.AddIncludeDictionary("include1"); |
| 614 | TemplateDictionaryPeer subdict_1a_peer(subdict_1a); |
| 615 | subdict_1a->SetFilename("incfile1a"); |
| 616 | // This is the same dict, but name is specified a different way. |
| 617 | TemplateDictionary* subdict_1b = dict.AddIncludeDictionary( |
| 618 | TemplateString("include1__ignored__", strlen("include1"))); |
| 619 | TemplateDictionaryPeer subdict_1b_peer(subdict_1b); |
| 620 | // Let's try not calling SetFilename on this one. |
| 621 | subdict_1a->SetValue("SUBLEVEL", "subfoo"); |
| 622 | subdict_1b->SetValue("SUBLEVEL", "subbar"); |
| 623 | |
| 624 | TemplateDictionary* subdict_2 = dict.AddIncludeDictionary("include2"); |
| 625 | TemplateDictionaryPeer subdict_2_peer(subdict_2); |
| 626 | subdict_2->SetFilename("foo/bar"); |
| 627 | subdict_2->SetValue("TOPLEVEL", "bar"); // overriding top dict |
| 628 | // overriding template dict |
| 629 | subdict_2->SetValue("TEMPLATELEVEL", "subfoo3"); |
| 630 | TemplateDictionary* subdict_2_1 = subdict_2->AddIncludeDictionary("sub"); |
| 631 | TemplateDictionaryPeer subdict_2_1_peer(subdict_2_1); |
| 632 | subdict_2_1->SetFilename("baz"); |
| 633 | subdict_2_1->SetIntValue("GLOBAL", 21); // overrides value in setUp() |
| 634 | |
| 635 | // Verify that all variables that should be look-up-able are, and that |
| 636 | // we have proper precedence. Unlike with sections, includes lookups |
| 637 | // do not go 'up the chain'. |
| 638 | EXPECT_TRUE(peer.ValueIs("GLOBAL", "top")); |
| 639 | EXPECT_TRUE(peer.ValueIs("TOPLEVEL", "foo")); |
| 640 | EXPECT_TRUE(peer.ValueIs("TOPLEVEL2", "foo2")); |
| 641 | EXPECT_TRUE(peer.ValueIs("TEMPLATELEVEL", "foo3")); |
| 642 | EXPECT_TRUE(peer.ValueIs("SUBLEVEL", "")); |
| 643 | |
| 644 | EXPECT_TRUE(subdict_1a_peer.ValueIs("GLOBAL", "top")); |
| 645 | EXPECT_TRUE(subdict_1a_peer.ValueIs("TOPLEVEL", "")); |
| 646 | EXPECT_TRUE(subdict_1a_peer.ValueIs("TOPLEVEL2", "")); |
| 647 | EXPECT_TRUE(subdict_1a_peer.ValueIs("TEMPLATELEVEL", "foo3")); |
| 648 | EXPECT_TRUE(subdict_1a_peer.ValueIs("SUBLEVEL", "subfoo")); |
| 649 | |
| 650 | EXPECT_TRUE(subdict_1b_peer.ValueIs("GLOBAL", "top")); |
| 651 | EXPECT_TRUE(subdict_1b_peer.ValueIs("TOPLEVEL", "")); |
| 652 | EXPECT_TRUE(subdict_1b_peer.ValueIs("TOPLEVEL2", "")); |
| 653 | EXPECT_TRUE(subdict_1b_peer.ValueIs("SUBLEVEL", "subbar")); |
| 654 | |
| 655 | EXPECT_TRUE(subdict_2_peer.ValueIs("GLOBAL", "top")); |
| 656 | EXPECT_TRUE(subdict_2_peer.ValueIs("TOPLEVEL", "bar")); |
| 657 | EXPECT_TRUE(subdict_2_peer.ValueIs("TOPLEVEL2", "")); |
| 658 | EXPECT_TRUE(subdict_2_peer.ValueIs("TEMPLATELEVEL", "subfoo3")); |
| 659 | EXPECT_TRUE(subdict_2_peer.ValueIs("SUBLEVEL", "")); |
| 660 | |
| 661 | EXPECT_TRUE(subdict_2_1_peer.ValueIs("GLOBAL", "21")); |
| 662 | EXPECT_TRUE(subdict_2_1_peer.ValueIs("TOPLEVEL", "")); |
| 663 | EXPECT_TRUE(subdict_2_1_peer.ValueIs("TOPLEVEL2", "")); |
| 664 | EXPECT_TRUE(subdict_2_1_peer.ValueIs("SUBLEVEL", "")); |
| 665 | |
| 666 | // Verify that everyone knows about its sub-dictionaries, but that |
| 667 | // these do not try to go 'up the chain' on lookup failure |
| 668 | EXPECT_FALSE(peer.IsHiddenTemplate("include1")); |
| 669 | EXPECT_FALSE(peer.IsHiddenTemplate("include2")); |
| 670 | EXPECT_TRUE(peer.IsHiddenTemplate("include3")); |
| 671 | EXPECT_TRUE(peer.IsHiddenTemplate("sub")); |
| 672 | EXPECT_TRUE(subdict_1a_peer.IsHiddenTemplate("include1")); |
| 673 | EXPECT_TRUE(subdict_1a_peer.IsHiddenTemplate("sub")); |
| 674 | EXPECT_FALSE(subdict_2_peer.IsHiddenTemplate("sub")); |
| 675 | EXPECT_TRUE(subdict_2_1_peer.IsHiddenTemplate("sub")); |
| 676 | |
| 677 | // We should get the dictionary-lengths right as well |
| 678 | vector<const TemplateDictionary*> dummy; |
| 679 | EXPECT_EQ(2, peer.GetIncludeDictionaries("include1", &dummy)); |
| 680 | EXPECT_EQ(1, peer.GetIncludeDictionaries("include2", &dummy)); |
| 681 | EXPECT_EQ(1, subdict_2_peer.GetIncludeDictionaries("sub", &dummy)); |
| 682 | |
| 683 | // We can also test the include-files are right |
| 684 | EXPECT_EQ(2, peer.GetIncludeDictionaries("include1", &dummy)); |
| 685 | EXPECT_EQ(1, peer.GetIncludeDictionaries("include2", &dummy)); |
| 686 | EXPECT_EQ(1, subdict_2_peer.GetIncludeDictionaries("sub", &dummy)); |
| 687 | // Test some of the values |
| 688 | EXPECT_TRUE(TemplateDictionaryPeer(GetIncludeDict(&dict, "include1", 0)) |
| 689 | .ValueIs("SUBLEVEL", "subfoo")); |
| 690 | EXPECT_TRUE(TemplateDictionaryPeer(GetIncludeDict(&dict, "include1", 1)) |
| 691 | .ValueIs("SUBLEVEL", "subbar")); |
| 692 | EXPECT_TRUE(TemplateDictionaryPeer(GetIncludeDict(&dict, "include2", 0)) |
| 693 | .ValueIs("TOPLEVEL", "bar")); |
| 694 | EXPECT_TRUE(TemplateDictionaryPeer( |
| 695 | GetIncludeDict(GetIncludeDict(&dict, "include2", 0), "sub", 0)) |
| 696 | .ValueIs("TOPLEVEL", "")); |
| 697 | EXPECT_TRUE(TemplateDictionaryPeer( |
| 698 | GetIncludeDict(GetIncludeDict(&dict, "include2", 0), "sub", 0)) |
| 699 | .ValueIs("GLOBAL", "21")); |
| 700 | // We can test the include-names as well |
| 701 | EXPECT_STREQ(peer.GetIncludeTemplateName("include1", 0), "incfile1a"); |
| 702 | EXPECT_STREQ(peer.GetIncludeTemplateName("include1", 1), ""); |
| 703 | EXPECT_STREQ(peer.GetIncludeTemplateName("include2", 0), "foo/bar"); |
| 704 | EXPECT_STREQ(TemplateDictionaryPeer(GetIncludeDict(&dict, "include2", 0)) |
| 705 | .GetIncludeTemplateName("sub", 0), |
| 706 | "baz"); |
| 707 | |
| 708 | // Make sure we're making descriptive names |
| 709 | EXPECT_STREQ(dict.name().c_str(), |
| 710 | "test_SetAddIncludeDictionary"); |
| 711 | EXPECT_STREQ(subdict_1a->name().c_str(), |
| 712 | "test_SetAddIncludeDictionary/include1#1"); |
| 713 | EXPECT_STREQ(subdict_1b->name().c_str(), |
| 714 | "test_SetAddIncludeDictionary/include1#2"); |
| 715 | EXPECT_STREQ(subdict_2->name().c_str(), |
| 716 | "test_SetAddIncludeDictionary/include2#1"); |
| 717 | EXPECT_STREQ(subdict_2_1->name().c_str(), |
| 718 | "test_SetAddIncludeDictionary/include2#1/sub#1"); |
| 719 | |
| 720 | // Finally, we can test the whole kit and kaboodle |
| 721 | string dump; |
| 722 | dict.DumpToString(&dump); |
| 723 | const char* const expected = |
| 724 | ("global dictionary {\n" |
| 725 | " BI_NEWLINE: >\n" |
| 726 | "<\n" |
| 727 | " BI_SPACE: > <\n" |
| 728 | " GLOBAL: >top<\n" |
| 729 | "};\n" |
| 730 | "template dictionary {\n" |
| 731 | " TEMPLATELEVEL: >foo3<\n" |
| 732 | "};\n" |
| 733 | "dictionary 'test_SetAddIncludeDictionary' {\n" |
| 734 | " TOPLEVEL: >foo<\n" |
| 735 | " TOPLEVEL2: >foo2<\n" |
| 736 | " include-template include1 (dict 1 of 2, from incfile1a) -->\n" |
| 737 | " global dictionary {\n" |
| 738 | " BI_NEWLINE: >\n" |
| 739 | "<\n" |
| 740 | " BI_SPACE: > <\n" |
| 741 | " GLOBAL: >top<\n" |
| 742 | " };\n" |
| 743 | " dictionary 'test_SetAddIncludeDictionary/include1#1 (intended for incfile1a)' {\n" |
| 744 | " SUBLEVEL: >subfoo<\n" |
| 745 | " }\n" |
| 746 | " include-template include1 (dict 2 of 2, **NO FILENAME SET; THIS DICT WILL BE IGNORED**) -->\n" |
| 747 | " global dictionary {\n" |
| 748 | " BI_NEWLINE: >\n" |
| 749 | "<\n" |
| 750 | " BI_SPACE: > <\n" |
| 751 | " GLOBAL: >top<\n" |
| 752 | " };\n" |
| 753 | " dictionary 'test_SetAddIncludeDictionary/include1#2' {\n" |
| 754 | " SUBLEVEL: >subbar<\n" |
| 755 | " }\n" |
| 756 | " include-template include2 (dict 1 of 1, from foo/bar) -->\n" |
| 757 | " global dictionary {\n" |
| 758 | " BI_NEWLINE: >\n" |
| 759 | "<\n" |
| 760 | " BI_SPACE: > <\n" |
| 761 | " GLOBAL: >top<\n" |
| 762 | " };\n" |
| 763 | " dictionary 'test_SetAddIncludeDictionary/include2#1 (intended for foo/bar)' {\n" |
| 764 | " TEMPLATELEVEL: >subfoo3<\n" |
| 765 | " TOPLEVEL: >bar<\n" |
| 766 | " include-template sub (dict 1 of 1, from baz) -->\n" |
| 767 | " global dictionary {\n" |
| 768 | " BI_NEWLINE: >\n" |
| 769 | "<\n" |
| 770 | " BI_SPACE: > <\n" |
| 771 | " GLOBAL: >top<\n" |
| 772 | " };\n" |
| 773 | " dictionary 'test_SetAddIncludeDictionary/include2#1/sub#1 (intended for baz)' {\n" |
| 774 | " GLOBAL: >21<\n" |
| 775 | " }\n" |
| 776 | " }\n" |
| 777 | "}\n"); |
| 778 | EXPECT_STREQ(dump.c_str(), expected); |
| 779 | } |
| 780 | |
| 781 | static void TestMakeCopy(bool use_local_arena) { |
| 782 | UnsafeArena local_arena(1024); |
| 783 | UnsafeArena* arena = NULL; |
| 784 | if (use_local_arena) |
| 785 | arena = &local_arena; |
| 786 | |
| 787 | // First, let's make a non-trivial template dictionary (We use |
| 788 | // 'new' because later we'll test deleting this dict but keeping |
| 789 | // around the copy.) |
| 790 | TemplateDictionary* dict = new TemplateDictionary("testdict", arena); |
| 791 | |
| 792 | dict->SetValue("TOPLEVEL", "foo"); |
| 793 | |
| 794 | dict->SetTemplateGlobalValue("TEMPLATELEVEL", "foo3"); |
| 795 | |
| 796 | TemplateDictionary* subdict_1a = dict->AddIncludeDictionary("include1"); |
| 797 | subdict_1a->SetFilename("incfile1a"); |
| 798 | subdict_1a->SetValue("SUBLEVEL", "subfoo"); |
| 799 | TemplateDictionary* subdict_1b = dict->AddIncludeDictionary("include1"); |
| 800 | // Let's try not calling SetFilename on this one. |
| 801 | subdict_1b->SetValue("SUBLEVEL", "subbar"); |
| 802 | |
| 803 | TemplateDictionary* subdict_2a = dict->AddSectionDictionary("section1"); |
| 804 | TemplateDictionary* subdict_2b = dict->AddSectionDictionary("section1"); |
| 805 | subdict_2a->SetValue("SUBLEVEL", "subfoo"); |
| 806 | subdict_2b->SetValue("SUBLEVEL", "subbar"); |
| 807 | TemplateDictionary* subdict_3 = dict->AddSectionDictionary("section2"); |
| 808 | subdict_3->SetValue("TOPLEVEL", "bar"); // overriding top dict |
| 809 | TemplateDictionary* subdict_3_1 = subdict_3->AddSectionDictionary("sub"); |
| 810 | subdict_3_1->SetIntValue("GLOBAL", 21); // overrides value in setUp() |
| 811 | |
| 812 | string orig; |
| 813 | dict->DumpToString(&orig); |
| 814 | |
| 815 | // Make a copy |
| 816 | TemplateDictionary* dict_copy = dict->MakeCopy("testdict", NULL); |
| 817 | // Make sure it doesn't work to copy a sub-dictionary |
| 818 | EXPECT_TRUE(subdict_1a->MakeCopy("copy of subdict") == NULL); |
| 819 | EXPECT_TRUE(subdict_2a->MakeCopy("copy of subdict") == NULL); |
| 820 | |
| 821 | // Delete the original dict, to make sure the copy really is independent |
| 822 | delete dict; |
| 823 | dict = NULL; |
| 824 | string copy; |
| 825 | dict_copy->DumpToString(©); |
| 826 | delete dict_copy; |
| 827 | |
| 828 | EXPECT_STREQ(orig.c_str(), copy.c_str()); |
| 829 | } |
| 830 | |
| 831 | TEST(MakeCopy, UseLocalArena) { |
| 832 | TestMakeCopy(true); |
| 833 | } |
| 834 | |
| 835 | TEST(MakeCopy, DoNotUseLocalArena) { |
| 836 | TestMakeCopy(false); |
| 837 | } |
| 838 | |
| 839 | TEST(TemplateDictionary, SetModifierData) { |
| 840 | PerExpandData per_expand_data; |
| 841 | const void* data = "test"; |
| 842 | per_expand_data.InsertForModifiers("a", data); |
| 843 | EXPECT_EQ(data, per_expand_data.LookupForModifiers("a")); |
| 844 | } |
| 845 | |
| 846 | TEST(TemplateDictionary, Iterator) { |
| 847 | // Build up a nice community of TemplateDictionaries. |
| 848 | TemplateDictionary farm("Farm"); |
| 849 | TemplateDictionaryPeer farm_peer(&farm); |
| 850 | TemplateDictionaryInterface* grey_barn = |
| 851 | farm.AddIncludeDictionary("BARN"); |
| 852 | TemplateDictionaryInterface* duck_pond = |
| 853 | farm.AddIncludeDictionary("POND"); |
| 854 | TemplateDictionaryInterface* cattle_pond = |
| 855 | farm.AddIncludeDictionary("POND"); |
| 856 | TemplateDictionaryInterface* irrigation_pond = |
| 857 | farm.AddIncludeDictionary("POND"); |
| 858 | |
| 859 | // A section name with repeated sections |
| 860 | TemplateDictionaryInterface* lillies = farm.AddSectionDictionary("FLOWERS"); |
| 861 | TemplateDictionaryInterface* lilacs = farm.AddSectionDictionary("FLOWERS"); |
| 862 | TemplateDictionaryInterface* daisies = farm.AddSectionDictionary("FLOWERS"); |
| 863 | // A section name with one repeat |
| 864 | TemplateDictionaryInterface* wheat = farm.AddSectionDictionary("WHEAT"); |
| 865 | // A section name, just shown |
| 866 | farm.ShowSection("CORN"); |
| 867 | |
| 868 | // Check that the iterators expose all of the dictionaries. |
| 869 | TemplateDictionaryPeer::Iterator* barns = |
| 870 | farm_peer.CreateTemplateIterator("BARN"); |
| 871 | EXPECT_TRUE(barns->HasNext()); |
| 872 | EXPECT_EQ(&barns->Next(), grey_barn); |
| 873 | EXPECT_FALSE(barns->HasNext()); |
| 874 | delete barns; |
| 875 | |
| 876 | TemplateDictionaryPeer::Iterator* ponds = |
| 877 | farm_peer.CreateTemplateIterator("POND"); |
| 878 | EXPECT_TRUE(ponds->HasNext()); |
| 879 | EXPECT_EQ(&ponds->Next(), duck_pond); |
| 880 | EXPECT_TRUE(ponds->HasNext()); |
| 881 | EXPECT_EQ(&ponds->Next(), cattle_pond); |
| 882 | EXPECT_TRUE(ponds->HasNext()); |
| 883 | EXPECT_EQ(&ponds->Next(), irrigation_pond); |
| 884 | EXPECT_FALSE(ponds->HasNext()); |
| 885 | delete ponds; |
| 886 | |
| 887 | TemplateDictionaryPeer::Iterator* flowers = |
| 888 | farm_peer.CreateSectionIterator("FLOWERS"); |
| 889 | EXPECT_TRUE(flowers->HasNext()); |
| 890 | EXPECT_EQ(&flowers->Next(), lillies); |
| 891 | EXPECT_TRUE(flowers->HasNext()); |
| 892 | EXPECT_EQ(&flowers->Next(), lilacs); |
| 893 | EXPECT_TRUE(flowers->HasNext()); |
| 894 | EXPECT_EQ(&flowers->Next(), daisies); |
| 895 | EXPECT_FALSE(flowers->HasNext()); |
| 896 | delete flowers; |
| 897 | |
| 898 | TemplateDictionaryPeer::Iterator* crop = |
| 899 | farm_peer.CreateSectionIterator("WHEAT"); |
| 900 | EXPECT_TRUE(crop->HasNext()); |
| 901 | EXPECT_EQ(&crop->Next(), wheat); |
| 902 | EXPECT_FALSE(crop->HasNext()); |
| 903 | delete crop; |
| 904 | |
| 905 | TemplateDictionaryPeer::Iterator* corn_crop = |
| 906 | farm_peer.CreateSectionIterator("CORN"); |
| 907 | EXPECT_TRUE(corn_crop->HasNext()); |
| 908 | EXPECT_TRUE(&corn_crop->Next()); // ShowSection doesn't give us the dict back |
| 909 | EXPECT_FALSE(corn_crop->HasNext()); |
| 910 | delete corn_crop; |
| 911 | } |
| 912 | |
| 913 | TEST(TemplateDictionary, IsHiddenSectionDefault) { |
| 914 | TemplateDictionary dict("dict"); |
| 915 | TemplateDictionaryPeer peer(&dict); |
| 916 | EXPECT_TRUE(peer.IsHiddenSection("UNDEFINED")); |
| 917 | EXPECT_FALSE(peer.IsUnhiddenSection("UNDEFINED")); |
| 918 | dict.ShowSection("VISIBLE"); |
| 919 | EXPECT_FALSE(peer.IsHiddenSection("VISIBLE")); |
| 920 | EXPECT_TRUE(peer.IsUnhiddenSection("VISIBLE")); |
| 921 | } |
| 922 | |
| 923 | // This has to run last, since its SetGlobalValue modifies the global |
| 924 | // state, which can affect other tests (especially given the embedded |
| 925 | // NUL!) So we don't use the normal TEST() here, and call it manually |
| 926 | // in main(). |
| 927 | |
| 928 | void TestSetValueWithNUL() { |
| 929 | TemplateDictionary dict("test_SetValueWithNUL", NULL); |
| 930 | TemplateDictionaryPeer peer(&dict); |
| 931 | |
| 932 | // Test copying char*s, strings, and explicit TemplateStrings |
| 933 | dict.SetValue(string("FOO\0BAR", 7), string("QUX\0QUUX", 8)); |
| 934 | dict.SetGlobalValue(string("GOO\0GAR", 7), string("GUX\0GUUX", 8)); |
| 935 | |
| 936 | // FOO should not match FOO\0BAR |
| 937 | EXPECT_TRUE(peer.ValueIs("FOO", "")); |
| 938 | EXPECT_TRUE(peer.ValueIs("GOO", "")); |
| 939 | |
| 940 | EXPECT_TRUE(peer.ValueIs(string("FOO\0BAR", 7), string("QUX\0QUUX", 8))); |
| 941 | EXPECT_TRUE(peer.ValueIs(string("GOO\0GAR", 7), string("GUX\0GUUX", 8))); |
| 942 | |
| 943 | string dump; |
| 944 | dict.DumpToString(&dump); |
| 945 | // We can't use EXPECT_STREQ here because of the embedded NULs. |
| 946 | // They also require I count the length of the string by hand. :-( |
| 947 | string expected(("global dictionary {\n" |
| 948 | " BI_NEWLINE: >\n" |
| 949 | "<\n" |
| 950 | " BI_SPACE: > <\n" |
| 951 | " GLOBAL: >top<\n" |
| 952 | " GOO\0GAR: >GUX\0GUUX<\n" |
| 953 | "};\n" |
| 954 | "dictionary 'test_SetValueWithNUL' {\n" |
| 955 | " FOO\0BAR: >QUX\0QUUX<\n" |
| 956 | "}\n"), |
| 957 | 160); |
| 958 | EXPECT_EQ(dump, expected); |
| 959 | } |
| 960 | |
| 961 | TEST(TemplateDictionary, TestShowTemplateGlobalSection) { |
| 962 | StringToTemplateCache("test.tpl", "{{#sect}}OK{{/sect}}", DO_NOT_STRIP); |
| 963 | |
| 964 | TemplateDictionary dict("mydict"); |
| 965 | dict.ShowTemplateGlobalSection("sect"); |
| 966 | |
| 967 | string out; |
| 968 | ExpandTemplate("test.tpl", DO_NOT_STRIP, &dict, &out); |
| 969 | } |
| 970 | |
| 971 | TEST(TemplateDictionary, TestShowTemplateGlobalSection_Child) { |
| 972 | // The TemplateDictionary::template_global_dict_ behaves differently for child |
| 973 | // dictionaries than for the root parent dictionary. |
| 974 | StringToTemplateCache("test2.tpl", |
| 975 | "{{#foo}}{{#sect}}OK{{/sect}}{{/foo}}", |
| 976 | DO_NOT_STRIP); |
| 977 | |
| 978 | TemplateDictionary dict("mydict"); |
| 979 | dict.ShowTemplateGlobalSection("sect"); |
| 980 | |
| 981 | dict.AddSectionDictionary("foo"); |
| 982 | |
| 983 | string out; |
| 984 | ExpandTemplate("test2.tpl", DO_NOT_STRIP, &dict, &out); |
| 985 | } |
| 986 | |
| 987 | TEST(TemplateDictionary, TestShowTemplateGlobalSection_SectionDoesntExist) { |
| 988 | StringToTemplateCache("test3.tpl", |
| 989 | "{{#bad}}bad{{/bad}}", |
| 990 | DO_NOT_STRIP); |
| 991 | |
| 992 | TemplateDictionary dict("mydict"); |
| 993 | |
| 994 | string out; |
| 995 | ExpandTemplate("test3.tpl", DO_NOT_STRIP, &dict, &out); |
| 996 | } |
| 997 | |
| 998 | |
| 999 | } // unnamed namespace |
| 1000 | |
| 1001 | |
| 1002 | int main(int argc, char** argv) { |
| 1003 | |
| 1004 | SetUp(); |
| 1005 | |
| 1006 | const int retval = RUN_ALL_TESTS(); |
| 1007 | |
| 1008 | // This has to run last, so we run it manually |
| 1009 | TestSetValueWithNUL(); |
| 1010 | |
| 1011 | return retval; |
| 1012 | } |