blob: 57f7f91d33a4c1d808d7176f72797376b0d89ba6 [file] [log] [blame]
Brian Silverman70325d62015-09-20 17:00:43 -04001// 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
71using std::string;
72using 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
85namespace ctemplate {
86
87// Deletes all files named *template* in dir, and sets up dir as the
88// place where StringToTemplate writes.
89static char* g_tmpdir = NULL;
90
91#ifndef USING_PORT_CC /* windows defines its own version in windows/port.cc */
92void 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
105static string TmpFile(const char* basename) {
106 return string("/tmp/") + basename;
107}
108
109#endif // #ifndef USING_PORT_CC
110
111void 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
118const 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.
123static Mutex g_time_mutex(base::LINKER_INITIALIZED);
124static time_t mock_time = 946713600; // jan 1, 2000, in california
125
126void 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
140time_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.
148string 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.
159Template* 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
166const 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
180const 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
194void 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
207void AssertExpandIs(const Template* tpl, const TemplateDictionary *dict,
208 const string& is, bool expected) {
209 AssertExpandWithDataIs(tpl, dict, NULL, is, expected);
210}
211
212void 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
227TemporaryRegisterTemplate::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
237TemporaryRegisterTemplate::~TemporaryRegisterTemplate() {
238 TemplateNamelist::namelist_ = old_namelist_;
239}
240
241const 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
248bool TemplateDictionaryPeer::ValueIs(const TemplateString& variable,
249 const TemplateString& expected) const {
250 return dict_->GetValue(variable) == expected;
251}
252
253bool TemplateDictionaryPeer::IsHiddenSection(
254 const TemplateString& name) const {
255 return dict_->IsHiddenSection(name);
256}
257
258bool TemplateDictionaryPeer::IsUnhiddenSection(
259 const TemplateString& name) const {
260 return dict_->IsUnhiddenSection(name);
261}
262
263bool TemplateDictionaryPeer::IsHiddenTemplate(
264 const TemplateString& name) const {
265 return dict_->IsHiddenTemplate(name);
266}
267
268int 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
284int 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
300const char* TemplateDictionaryPeer::GetIncludeTemplateName(
301 const TemplateString& variable, int dictnum) const {
302 return dict_->GetIncludeTemplateName(variable, dictnum);
303}
304
305const char* TemplateDictionaryPeer::GetFilename() const {
306 return dict_->filename_;
307}
308
309}