Brian Silverman | 70325d6 | 2015-09-20 17:00:43 -0400 | [diff] [blame^] | 1 | // Copyright (c) 2006, 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 | |
| 32 | #include "config_for_unittests.h" |
| 33 | #include "base/mutex.h" // must come first, for _XOPEN_SOURCE |
| 34 | #include "tests/template_test_util.h" |
| 35 | #include <assert.h> // for assert() |
| 36 | #ifdef HAVE_DIRENT_H |
| 37 | # include <dirent.h> // for opendir() etc |
| 38 | #else |
| 39 | # define dirent direct |
| 40 | # ifdef HAVE_SYS_NDIR_H |
| 41 | # include <sys/ndir.h> |
| 42 | # endif |
| 43 | # ifdef HAVE_SYS_DIR_H |
| 44 | # include <sys/dir.h> |
| 45 | # endif |
| 46 | # ifdef HAVE_NDIR_H |
| 47 | # include <ndir.h> |
| 48 | # endif |
| 49 | #endif // for DIR, dirent, closedir(), opendir(), etc |
| 50 | #include <stdio.h> // for printf(), FILE, fclose(), fopen(), etc |
| 51 | #include <stdlib.h> // for exit() |
| 52 | #include <string.h> // for strcmp(), strcpy(), strstr() |
| 53 | #include <sys/stat.h> // for mkdir() |
| 54 | #include <sys/types.h> // for mode_t |
| 55 | #include <time.h> // for time_t |
| 56 | #ifdef HAVE_UTIME_H |
| 57 | # include <utime.h> |
| 58 | #endif // for utime() |
| 59 | #ifdef HAVE_UNISTD_H |
| 60 | # include <unistd.h> |
| 61 | #endif // for unlink() |
| 62 | #include <vector> // for vector<>, vector<>::size_type |
| 63 | #include <ctemplate/template.h> // for Template |
| 64 | #include <ctemplate/template_dictionary.h> // for TemplateDictionary |
| 65 | #include <ctemplate/template_dictionary_interface.h> |
| 66 | #include <ctemplate/template_enums.h> // for Strip |
| 67 | #include <ctemplate/template_namelist.h> // for TemplateNamelist, etc |
| 68 | #include <ctemplate/template_pathops.h> // for PathJoin() |
| 69 | #include "base/util.h" // for down_cast() |
| 70 | |
| 71 | using std::string; |
| 72 | using std::vector; |
| 73 | |
| 74 | #ifdef ASSERT |
| 75 | # undef ASSERT |
| 76 | #endif |
| 77 | #define ASSERT(cond) do { \ |
| 78 | if (!(cond)) { \ |
| 79 | printf("ASSERT FAILED, line %d: %s\n", __LINE__, #cond); \ |
| 80 | assert(cond); \ |
| 81 | exit(1); \ |
| 82 | } \ |
| 83 | } while (0) |
| 84 | |
| 85 | namespace ctemplate { |
| 86 | |
| 87 | // Deletes all files named *template* in dir, and sets up dir as the |
| 88 | // place where StringToTemplate writes. |
| 89 | static char* g_tmpdir = NULL; |
| 90 | |
| 91 | #ifndef USING_PORT_CC /* windows defines its own version in windows/port.cc */ |
| 92 | void CreateOrCleanTestDir(const string& dirname) { |
| 93 | DIR* dir = opendir(dirname.c_str()); |
| 94 | if (!dir) { // directory doesn't exist or something like that |
| 95 | mkdir(dirname.c_str(), 0755); // make the dir if we can |
| 96 | return; |
| 97 | } |
| 98 | while (struct dirent* d = readdir(dir)) { |
| 99 | if (strstr(d->d_name, "template")) |
| 100 | unlink(PathJoin(dirname, d->d_name).c_str()); |
| 101 | } |
| 102 | closedir(dir); |
| 103 | } |
| 104 | |
| 105 | static string TmpFile(const char* basename) { |
| 106 | return string("/tmp/") + basename; |
| 107 | } |
| 108 | |
| 109 | #endif // #ifndef USING_PORT_CC |
| 110 | |
| 111 | void CreateOrCleanTestDirAndSetAsTmpdir(const string& dirname) { |
| 112 | CreateOrCleanTestDir(dirname); |
| 113 | delete[] g_tmpdir; |
| 114 | g_tmpdir = new char[dirname.length() + 1]; |
| 115 | strcpy(g_tmpdir, dirname.c_str()); |
| 116 | } |
| 117 | |
| 118 | const string FLAGS_test_tmpdir(TmpFile("template_unittest_dir")); |
| 119 | |
| 120 | // This writes s to the given file. We want to make sure that every |
| 121 | // time we create a file, it has a different mtime (just like would |
| 122 | // be the case in real life), so we use a mock clock. |
| 123 | static Mutex g_time_mutex(base::LINKER_INITIALIZED); |
| 124 | static time_t mock_time = 946713600; // jan 1, 2000, in california |
| 125 | |
| 126 | void StringToFile(const string& s, const string& filename) { |
| 127 | FILE* fp = fopen(filename.c_str(), "wb"); |
| 128 | ASSERT(fp); |
| 129 | size_t r = fwrite(s.data(), 1, s.length(), fp); |
| 130 | ASSERT(r == s.length()); |
| 131 | fclose(fp); |
| 132 | |
| 133 | g_time_mutex.Lock(); |
| 134 | const time_t file_time = mock_time++; |
| 135 | g_time_mutex.Unlock(); |
| 136 | struct utimbuf timbuf = { file_time, file_time }; |
| 137 | utime(filename.c_str(), &timbuf); |
| 138 | } |
| 139 | |
| 140 | time_t Now() { |
| 141 | g_time_mutex.Lock(); |
| 142 | const time_t now = mock_time; |
| 143 | g_time_mutex.Unlock(); |
| 144 | return now; |
| 145 | } |
| 146 | |
| 147 | // This writes s to a file and returns the filename. |
| 148 | string StringToTemplateFile(const string& s) { |
| 149 | static int filenum = 0; |
| 150 | char buf[16]; |
| 151 | snprintf(buf, sizeof(buf), "%03d", ++filenum); |
| 152 | string filename = PathJoin(g_tmpdir ? g_tmpdir : "", |
| 153 | string("template.") + buf); |
| 154 | StringToFile(s, filename); |
| 155 | return filename; |
| 156 | } |
| 157 | |
| 158 | // This writes s to a file and then loads it into a template object. |
| 159 | Template* StringToTemplate(const string& s, Strip strip) { |
| 160 | return Template::GetTemplate(StringToTemplateFile(s), strip); |
| 161 | } |
| 162 | |
| 163 | // This is esp. useful for calling from within gdb. |
| 164 | // The gdb nice-ness is balanced by the need for the caller to delete the buf. |
| 165 | |
| 166 | const char* ExpandIs(const Template* tpl, const TemplateDictionary *dict, |
| 167 | PerExpandData* per_expand_data, bool expected) { |
| 168 | string outstring; |
| 169 | if (per_expand_data) |
| 170 | ASSERT(expected == tpl->ExpandWithData(&outstring, dict, per_expand_data)); |
| 171 | else |
| 172 | ASSERT(expected == tpl->Expand(&outstring, dict)); |
| 173 | |
| 174 | |
| 175 | char* buf = new char[outstring.size()+1]; |
| 176 | strcpy(buf, outstring.c_str()); |
| 177 | return buf; |
| 178 | } |
| 179 | |
| 180 | const char* ExpandWithCacheIs(TemplateCache* cache, |
| 181 | const string& filename, Strip strip, |
| 182 | const TemplateDictionary *dict, |
| 183 | PerExpandData* per_expand_data, bool expected) { |
| 184 | string outstring; |
| 185 | ASSERT(expected == cache->ExpandWithData(filename, strip, dict, |
| 186 | per_expand_data, &outstring)); |
| 187 | |
| 188 | |
| 189 | char* buf = new char[outstring.size()+1]; |
| 190 | strcpy(buf, outstring.c_str()); |
| 191 | return buf; |
| 192 | } |
| 193 | |
| 194 | void AssertExpandWithDataIs(const Template* tpl, |
| 195 | const TemplateDictionary *dict, |
| 196 | PerExpandData* per_expand_data, |
| 197 | const string& is, bool expected) { |
| 198 | const char* buf = ExpandIs(tpl, dict, per_expand_data, expected); |
| 199 | if (strcmp(buf, is.c_str())) { |
| 200 | printf("expected = '%s'\n", is.c_str()); |
| 201 | printf("actual = '%s'\n", buf); |
| 202 | } |
| 203 | ASSERT(string(buf) == is); |
| 204 | delete [] buf; |
| 205 | } |
| 206 | |
| 207 | void AssertExpandIs(const Template* tpl, const TemplateDictionary *dict, |
| 208 | const string& is, bool expected) { |
| 209 | AssertExpandWithDataIs(tpl, dict, NULL, is, expected); |
| 210 | } |
| 211 | |
| 212 | void AssertExpandWithCacheIs(TemplateCache* cache, |
| 213 | const string& filename, Strip strip, |
| 214 | const TemplateDictionary *dict, |
| 215 | PerExpandData* per_expand_data, |
| 216 | const string& is, bool expected) { |
| 217 | const char* buf = ExpandWithCacheIs(cache, filename, strip, dict, |
| 218 | per_expand_data, expected); |
| 219 | if (strcmp(buf, is.c_str())) { |
| 220 | printf("expected = '%s'\n", is.c_str()); |
| 221 | printf("actual = '%s'\n", buf); |
| 222 | } |
| 223 | ASSERT(string(buf) == is); |
| 224 | delete [] buf; |
| 225 | } |
| 226 | |
| 227 | TemporaryRegisterTemplate::TemporaryRegisterTemplate(const char* name) { |
| 228 | old_namelist_ = TemplateNamelist::namelist_; |
| 229 | if (old_namelist_) { |
| 230 | namelist_ = *old_namelist_; |
| 231 | } |
| 232 | |
| 233 | namelist_.insert(name); |
| 234 | TemplateNamelist::namelist_ = &namelist_; |
| 235 | } |
| 236 | |
| 237 | TemporaryRegisterTemplate::~TemporaryRegisterTemplate() { |
| 238 | TemplateNamelist::namelist_ = old_namelist_; |
| 239 | } |
| 240 | |
| 241 | const char* TemplateDictionaryPeer::GetSectionValue( |
| 242 | const TemplateString& variable) |
| 243 | const { |
| 244 | // Luckily, TemplateDictionary stores all values with a trailing NUL. |
| 245 | return dict_->GetValue(variable).data(); |
| 246 | } |
| 247 | |
| 248 | bool TemplateDictionaryPeer::ValueIs(const TemplateString& variable, |
| 249 | const TemplateString& expected) const { |
| 250 | return dict_->GetValue(variable) == expected; |
| 251 | } |
| 252 | |
| 253 | bool TemplateDictionaryPeer::IsHiddenSection( |
| 254 | const TemplateString& name) const { |
| 255 | return dict_->IsHiddenSection(name); |
| 256 | } |
| 257 | |
| 258 | bool TemplateDictionaryPeer::IsUnhiddenSection( |
| 259 | const TemplateString& name) const { |
| 260 | return dict_->IsUnhiddenSection(name); |
| 261 | } |
| 262 | |
| 263 | bool TemplateDictionaryPeer::IsHiddenTemplate( |
| 264 | const TemplateString& name) const { |
| 265 | return dict_->IsHiddenTemplate(name); |
| 266 | } |
| 267 | |
| 268 | int TemplateDictionaryPeer::GetSectionDictionaries( |
| 269 | const TemplateString& section_name, |
| 270 | vector<const TemplateDictionary*>* dicts) const { |
| 271 | dicts->clear(); |
| 272 | if (dict_->IsHiddenSection(section_name)) |
| 273 | return 0; |
| 274 | |
| 275 | TemplateDictionaryInterface::Iterator* di = |
| 276 | dict_->CreateSectionIterator(section_name); |
| 277 | while (di->HasNext()) |
| 278 | dicts->push_back(down_cast<const TemplateDictionary*>(&di->Next())); |
| 279 | delete di; |
| 280 | |
| 281 | return static_cast<int>(dicts->size()); |
| 282 | } |
| 283 | |
| 284 | int TemplateDictionaryPeer::GetIncludeDictionaries( |
| 285 | const TemplateString& section_name, |
| 286 | vector<const TemplateDictionary*>* dicts) const { |
| 287 | dicts->clear(); |
| 288 | if (dict_->IsHiddenTemplate(section_name)) |
| 289 | return 0; |
| 290 | |
| 291 | TemplateDictionaryInterface::Iterator* di = |
| 292 | dict_->CreateTemplateIterator(section_name); |
| 293 | while (di->HasNext()) |
| 294 | dicts->push_back(down_cast<const TemplateDictionary*>(&di->Next())); |
| 295 | delete di; |
| 296 | |
| 297 | return static_cast<int>(dicts->size()); |
| 298 | } |
| 299 | |
| 300 | const char* TemplateDictionaryPeer::GetIncludeTemplateName( |
| 301 | const TemplateString& variable, int dictnum) const { |
| 302 | return dict_->GetIncludeTemplateName(variable, dictnum); |
| 303 | } |
| 304 | |
| 305 | const char* TemplateDictionaryPeer::GetFilename() const { |
| 306 | return dict_->filename_; |
| 307 | } |
| 308 | |
| 309 | } |