1 // Copyright 2012 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 copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors
14 // may be used to endorse or promote products derived from this software
15 // without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 #include "utils/text/templates.hpp"
30
31 #include <fstream>
32 #include <sstream>
33
34 #include <atf-c++.hpp>
35
36 #include "utils/fs/operations.hpp"
37 #include "utils/fs/path.hpp"
38 #include "utils/text/exceptions.hpp"
39
40 namespace fs = utils::fs;
41 namespace text = utils::text;
42
43
44 namespace {
45
46
47 /// Applies a set of templates to an input string and validates the output.
48 ///
49 /// This fails the test case if exp_output does not match the document generated
50 /// by the application of the templates.
51 ///
52 /// \param templates The templates to apply.
53 /// \param input_str The input document to which to apply the templates.
54 /// \param exp_output The expected output document.
55 static void
do_test_ok(const text::templates_def & templates,const std::string & input_str,const std::string & exp_output)56 do_test_ok(const text::templates_def& templates, const std::string& input_str,
57 const std::string& exp_output)
58 {
59 std::istringstream input(input_str);
60 std::ostringstream output;
61
62 text::instantiate(templates, input, output);
63 ATF_REQUIRE_EQ(exp_output, output.str());
64 }
65
66
67 /// Applies a set of templates to an input string and checks for an error.
68 ///
69 /// This fails the test case if the exception raised by the template processing
70 /// does not match the expected message.
71 ///
72 /// \param templates The templates to apply.
73 /// \param input_str The input document to which to apply the templates.
74 /// \param exp_message The expected error message in the raised exception.
75 static void
do_test_fail(const text::templates_def & templates,const std::string & input_str,const std::string & exp_message)76 do_test_fail(const text::templates_def& templates, const std::string& input_str,
77 const std::string& exp_message)
78 {
79 std::istringstream input(input_str);
80 std::ostringstream output;
81
82 ATF_REQUIRE_THROW_RE(text::syntax_error, exp_message,
83 text::instantiate(templates, input, output));
84 }
85
86
87 } // anonymous namespace
88
89
90 ATF_TEST_CASE_WITHOUT_HEAD(templates_def__add_variable__first);
ATF_TEST_CASE_BODY(templates_def__add_variable__first)91 ATF_TEST_CASE_BODY(templates_def__add_variable__first)
92 {
93 text::templates_def templates;
94 templates.add_variable("the-name", "first-value");
95 ATF_REQUIRE_EQ("first-value", templates.get_variable("the-name"));
96 }
97
98
99 ATF_TEST_CASE_WITHOUT_HEAD(templates_def__add_variable__replace);
ATF_TEST_CASE_BODY(templates_def__add_variable__replace)100 ATF_TEST_CASE_BODY(templates_def__add_variable__replace)
101 {
102 text::templates_def templates;
103 templates.add_variable("the-name", "first-value");
104 templates.add_variable("the-name", "second-value");
105 ATF_REQUIRE_EQ("second-value", templates.get_variable("the-name"));
106 }
107
108
109 ATF_TEST_CASE_WITHOUT_HEAD(templates_def__remove_variable);
ATF_TEST_CASE_BODY(templates_def__remove_variable)110 ATF_TEST_CASE_BODY(templates_def__remove_variable)
111 {
112 text::templates_def templates;
113 templates.add_variable("the-name", "the-value");
114 templates.get_variable("the-name"); // Should not throw.
115 templates.remove_variable("the-name");
116 ATF_REQUIRE_THROW(text::syntax_error, templates.get_variable("the-name"));
117 }
118
119
120 ATF_TEST_CASE_WITHOUT_HEAD(templates_def__add_vector__first);
ATF_TEST_CASE_BODY(templates_def__add_vector__first)121 ATF_TEST_CASE_BODY(templates_def__add_vector__first)
122 {
123 text::templates_def templates;
124 templates.add_vector("the-name");
125 ATF_REQUIRE(templates.get_vector("the-name").empty());
126 }
127
128
129 ATF_TEST_CASE_WITHOUT_HEAD(templates_def__add_vector__replace);
ATF_TEST_CASE_BODY(templates_def__add_vector__replace)130 ATF_TEST_CASE_BODY(templates_def__add_vector__replace)
131 {
132 text::templates_def templates;
133 templates.add_vector("the-name");
134 templates.add_to_vector("the-name", "foo");
135 ATF_REQUIRE(!templates.get_vector("the-name").empty());
136 templates.add_vector("the-name");
137 ATF_REQUIRE(templates.get_vector("the-name").empty());
138 }
139
140
141 ATF_TEST_CASE_WITHOUT_HEAD(templates_def__add_to_vector);
ATF_TEST_CASE_BODY(templates_def__add_to_vector)142 ATF_TEST_CASE_BODY(templates_def__add_to_vector)
143 {
144 text::templates_def templates;
145 templates.add_vector("the-name");
146 ATF_REQUIRE_EQ(0, templates.get_vector("the-name").size());
147 templates.add_to_vector("the-name", "first");
148 ATF_REQUIRE_EQ(1, templates.get_vector("the-name").size());
149 templates.add_to_vector("the-name", "second");
150 ATF_REQUIRE_EQ(2, templates.get_vector("the-name").size());
151 templates.add_to_vector("the-name", "third");
152 ATF_REQUIRE_EQ(3, templates.get_vector("the-name").size());
153
154 std::vector< std::string > expected;
155 expected.push_back("first");
156 expected.push_back("second");
157 expected.push_back("third");
158 ATF_REQUIRE(expected == templates.get_vector("the-name"));
159 }
160
161
162 ATF_TEST_CASE_WITHOUT_HEAD(templates_def__exists__variable);
ATF_TEST_CASE_BODY(templates_def__exists__variable)163 ATF_TEST_CASE_BODY(templates_def__exists__variable)
164 {
165 text::templates_def templates;
166 ATF_REQUIRE(!templates.exists("some-name"));
167 templates.add_variable("some-name ", "foo");
168 ATF_REQUIRE(!templates.exists("some-name"));
169 templates.add_variable("some-name", "foo");
170 ATF_REQUIRE(templates.exists("some-name"));
171 }
172
173
174 ATF_TEST_CASE_WITHOUT_HEAD(templates_def__exists__vector);
ATF_TEST_CASE_BODY(templates_def__exists__vector)175 ATF_TEST_CASE_BODY(templates_def__exists__vector)
176 {
177 text::templates_def templates;
178 ATF_REQUIRE(!templates.exists("some-name"));
179 templates.add_vector("some-name ");
180 ATF_REQUIRE(!templates.exists("some-name"));
181 templates.add_vector("some-name");
182 ATF_REQUIRE(templates.exists("some-name"));
183 }
184
185
186 ATF_TEST_CASE_WITHOUT_HEAD(templates_def__get_variable__ok);
ATF_TEST_CASE_BODY(templates_def__get_variable__ok)187 ATF_TEST_CASE_BODY(templates_def__get_variable__ok)
188 {
189 text::templates_def templates;
190 templates.add_variable("foo", "");
191 templates.add_variable("bar", " baz ");
192 ATF_REQUIRE_EQ("", templates.get_variable("foo"));
193 ATF_REQUIRE_EQ(" baz ", templates.get_variable("bar"));
194 }
195
196
197 ATF_TEST_CASE_WITHOUT_HEAD(templates_def__get_variable__unknown);
ATF_TEST_CASE_BODY(templates_def__get_variable__unknown)198 ATF_TEST_CASE_BODY(templates_def__get_variable__unknown)
199 {
200 text::templates_def templates;
201 templates.add_variable("foo", "");
202 ATF_REQUIRE_THROW_RE(text::syntax_error, "Unknown variable 'foo '",
203 templates.get_variable("foo "));
204 }
205
206
207 ATF_TEST_CASE_WITHOUT_HEAD(templates_def__get_vector__ok);
ATF_TEST_CASE_BODY(templates_def__get_vector__ok)208 ATF_TEST_CASE_BODY(templates_def__get_vector__ok)
209 {
210 text::templates_def templates;
211 templates.add_vector("foo");
212 templates.add_vector("bar");
213 templates.add_to_vector("bar", "baz");
214 ATF_REQUIRE_EQ(0, templates.get_vector("foo").size());
215 ATF_REQUIRE_EQ(1, templates.get_vector("bar").size());
216 }
217
218
219 ATF_TEST_CASE_WITHOUT_HEAD(templates_def__get_vector__unknown);
ATF_TEST_CASE_BODY(templates_def__get_vector__unknown)220 ATF_TEST_CASE_BODY(templates_def__get_vector__unknown)
221 {
222 text::templates_def templates;
223 templates.add_vector("foo");
224 ATF_REQUIRE_THROW_RE(text::syntax_error, "Unknown vector 'foo '",
225 templates.get_vector("foo "));
226 }
227
228
229 ATF_TEST_CASE_WITHOUT_HEAD(templates_def__evaluate__variable__ok);
ATF_TEST_CASE_BODY(templates_def__evaluate__variable__ok)230 ATF_TEST_CASE_BODY(templates_def__evaluate__variable__ok)
231 {
232 text::templates_def templates;
233 templates.add_variable("foo", "");
234 templates.add_variable("bar", " baz ");
235 ATF_REQUIRE_EQ("", templates.evaluate("foo"));
236 ATF_REQUIRE_EQ(" baz ", templates.evaluate("bar"));
237 }
238
239
240 ATF_TEST_CASE_WITHOUT_HEAD(templates_def__evaluate__variable__unknown);
ATF_TEST_CASE_BODY(templates_def__evaluate__variable__unknown)241 ATF_TEST_CASE_BODY(templates_def__evaluate__variable__unknown)
242 {
243 text::templates_def templates;
244 templates.add_variable("foo", "");
245 ATF_REQUIRE_THROW_RE(text::syntax_error, "Unknown variable 'foo1'",
246 templates.evaluate("foo1"));
247 }
248
249
250 ATF_TEST_CASE_WITHOUT_HEAD(templates_def__evaluate__vector__ok);
ATF_TEST_CASE_BODY(templates_def__evaluate__vector__ok)251 ATF_TEST_CASE_BODY(templates_def__evaluate__vector__ok)
252 {
253 text::templates_def templates;
254 templates.add_vector("v");
255 templates.add_to_vector("v", "foo");
256 templates.add_to_vector("v", "bar");
257 templates.add_to_vector("v", "baz");
258
259 templates.add_variable("index", "0");
260 ATF_REQUIRE_EQ("foo", templates.evaluate("v(index)"));
261 templates.add_variable("index", "1");
262 ATF_REQUIRE_EQ("bar", templates.evaluate("v(index)"));
263 templates.add_variable("index", "2");
264 ATF_REQUIRE_EQ("baz", templates.evaluate("v(index)"));
265 }
266
267
268 ATF_TEST_CASE_WITHOUT_HEAD(templates_def__evaluate__vector__unknown_vector);
ATF_TEST_CASE_BODY(templates_def__evaluate__vector__unknown_vector)269 ATF_TEST_CASE_BODY(templates_def__evaluate__vector__unknown_vector)
270 {
271 text::templates_def templates;
272 templates.add_vector("v");
273 templates.add_to_vector("v", "foo");
274 templates.add_variable("index", "0");
275 ATF_REQUIRE_THROW_RE(text::syntax_error, "Unknown vector 'fooz'",
276 templates.evaluate("fooz(index)"));
277 }
278
279
280 ATF_TEST_CASE_WITHOUT_HEAD(templates_def__evaluate__vector__unknown_index);
ATF_TEST_CASE_BODY(templates_def__evaluate__vector__unknown_index)281 ATF_TEST_CASE_BODY(templates_def__evaluate__vector__unknown_index)
282 {
283 text::templates_def templates;
284 templates.add_vector("v");
285 templates.add_to_vector("v", "foo");
286 templates.add_variable("index", "0");
287 ATF_REQUIRE_THROW_RE(text::syntax_error, "Unknown variable 'indexz'",
288 templates.evaluate("v(indexz)"));
289 }
290
291
292 ATF_TEST_CASE_WITHOUT_HEAD(templates_def__evaluate__vector__out_of_range);
ATF_TEST_CASE_BODY(templates_def__evaluate__vector__out_of_range)293 ATF_TEST_CASE_BODY(templates_def__evaluate__vector__out_of_range)
294 {
295 text::templates_def templates;
296 templates.add_vector("v");
297 templates.add_to_vector("v", "foo");
298 templates.add_variable("index", "1");
299 ATF_REQUIRE_THROW_RE(text::syntax_error, "Index 'index' out of range "
300 "at position '1'", templates.evaluate("v(index)"));
301 }
302
303
304 ATF_TEST_CASE_WITHOUT_HEAD(templates_def__evaluate__defined);
ATF_TEST_CASE_BODY(templates_def__evaluate__defined)305 ATF_TEST_CASE_BODY(templates_def__evaluate__defined)
306 {
307 text::templates_def templates;
308 templates.add_vector("the-variable");
309 templates.add_vector("the-vector");
310 ATF_REQUIRE_EQ("false", templates.evaluate("defined(the-variabl)"));
311 ATF_REQUIRE_EQ("false", templates.evaluate("defined(the-vecto)"));
312 ATF_REQUIRE_EQ("true", templates.evaluate("defined(the-variable)"));
313 ATF_REQUIRE_EQ("true", templates.evaluate("defined(the-vector)"));
314 }
315
316
317 ATF_TEST_CASE_WITHOUT_HEAD(templates_def__evaluate__length__ok);
ATF_TEST_CASE_BODY(templates_def__evaluate__length__ok)318 ATF_TEST_CASE_BODY(templates_def__evaluate__length__ok)
319 {
320 text::templates_def templates;
321 templates.add_vector("v");
322 ATF_REQUIRE_EQ("0", templates.evaluate("length(v)"));
323 templates.add_to_vector("v", "foo");
324 ATF_REQUIRE_EQ("1", templates.evaluate("length(v)"));
325 templates.add_to_vector("v", "bar");
326 ATF_REQUIRE_EQ("2", templates.evaluate("length(v)"));
327 templates.add_to_vector("v", "baz");
328 ATF_REQUIRE_EQ("3", templates.evaluate("length(v)"));
329 }
330
331
332 ATF_TEST_CASE_WITHOUT_HEAD(templates_def__evaluate__length__unknown_vector);
ATF_TEST_CASE_BODY(templates_def__evaluate__length__unknown_vector)333 ATF_TEST_CASE_BODY(templates_def__evaluate__length__unknown_vector)
334 {
335 text::templates_def templates;
336 templates.add_vector("foo1");
337 ATF_REQUIRE_THROW_RE(text::syntax_error, "Unknown vector 'foo'",
338 templates.evaluate("length(foo)"));
339 }
340
341
342 ATF_TEST_CASE_WITHOUT_HEAD(templates_def__evaluate__parenthesis_error);
ATF_TEST_CASE_BODY(templates_def__evaluate__parenthesis_error)343 ATF_TEST_CASE_BODY(templates_def__evaluate__parenthesis_error)
344 {
345 text::templates_def templates;
346 ATF_REQUIRE_THROW_RE(text::syntax_error,
347 "Expected '\\)' in.*'foo\\(abc'",
348 templates.evaluate("foo(abc"));
349 ATF_REQUIRE_THROW_RE(text::syntax_error,
350 "Unexpected text.*'\\)' in.*'a\\(b\\)c'",
351 templates.evaluate("a(b)c"));
352 }
353
354
355 ATF_TEST_CASE_WITHOUT_HEAD(instantiate__empty_input);
ATF_TEST_CASE_BODY(instantiate__empty_input)356 ATF_TEST_CASE_BODY(instantiate__empty_input)
357 {
358 const text::templates_def templates;
359 do_test_ok(templates, "", "");
360 }
361
362
363 ATF_TEST_CASE_WITHOUT_HEAD(instantiate__value__ok);
ATF_TEST_CASE_BODY(instantiate__value__ok)364 ATF_TEST_CASE_BODY(instantiate__value__ok)
365 {
366 const std::string input =
367 "first line\n"
368 "%%testvar1%%\n"
369 "third line\n"
370 "%%testvar2%% %%testvar3%%%%testvar4%%\n"
371 "fifth line\n";
372
373 const std::string exp_output =
374 "first line\n"
375 "second line\n"
376 "third line\n"
377 "fourth line.\n"
378 "fifth line\n";
379
380 text::templates_def templates;
381 templates.add_variable("testvar1", "second line");
382 templates.add_variable("testvar2", "fourth");
383 templates.add_variable("testvar3", "line");
384 templates.add_variable("testvar4", ".");
385
386 do_test_ok(templates, input, exp_output);
387 }
388
389
390 ATF_TEST_CASE_WITHOUT_HEAD(instantiate__value__unknown_variable);
ATF_TEST_CASE_BODY(instantiate__value__unknown_variable)391 ATF_TEST_CASE_BODY(instantiate__value__unknown_variable)
392 {
393 const std::string input =
394 "%%testvar1%%\n";
395
396 text::templates_def templates;
397 templates.add_variable("testvar2", "fourth line");
398
399 do_test_fail(templates, input, "Unknown variable 'testvar1'");
400 }
401
402
403 ATF_TEST_CASE_WITHOUT_HEAD(instantiate__vector_length__ok);
ATF_TEST_CASE_BODY(instantiate__vector_length__ok)404 ATF_TEST_CASE_BODY(instantiate__vector_length__ok)
405 {
406 const std::string input =
407 "%%length(testvector1)%%\n"
408 "%%length(testvector2)%% - %%length(testvector3)%%\n";
409
410 const std::string exp_output =
411 "4\n"
412 "0 - 1\n";
413
414 text::templates_def templates;
415 templates.add_vector("testvector1");
416 templates.add_to_vector("testvector1", "000");
417 templates.add_to_vector("testvector1", "111");
418 templates.add_to_vector("testvector1", "543");
419 templates.add_to_vector("testvector1", "999");
420 templates.add_vector("testvector2");
421 templates.add_vector("testvector3");
422 templates.add_to_vector("testvector3", "123");
423
424 do_test_ok(templates, input, exp_output);
425 }
426
427
428 ATF_TEST_CASE_WITHOUT_HEAD(instantiate__vector_length__unknown_vector);
ATF_TEST_CASE_BODY(instantiate__vector_length__unknown_vector)429 ATF_TEST_CASE_BODY(instantiate__vector_length__unknown_vector)
430 {
431 const std::string input =
432 "%%length(testvector)%%\n";
433
434 text::templates_def templates;
435 templates.add_vector("testvector2");
436
437 do_test_fail(templates, input, "Unknown vector 'testvector'");
438 }
439
440
441 ATF_TEST_CASE_WITHOUT_HEAD(instantiate__vector_value__ok);
ATF_TEST_CASE_BODY(instantiate__vector_value__ok)442 ATF_TEST_CASE_BODY(instantiate__vector_value__ok)
443 {
444 const std::string input =
445 "first line\n"
446 "%%testvector1(i)%%\n"
447 "third line\n"
448 "%%testvector2(j)%%\n"
449 "fifth line\n";
450
451 const std::string exp_output =
452 "first line\n"
453 "543\n"
454 "third line\n"
455 "123\n"
456 "fifth line\n";
457
458 text::templates_def templates;
459 templates.add_variable("i", "2");
460 templates.add_variable("j", "0");
461 templates.add_vector("testvector1");
462 templates.add_to_vector("testvector1", "000");
463 templates.add_to_vector("testvector1", "111");
464 templates.add_to_vector("testvector1", "543");
465 templates.add_to_vector("testvector1", "999");
466 templates.add_vector("testvector2");
467 templates.add_to_vector("testvector2", "123");
468
469 do_test_ok(templates, input, exp_output);
470 }
471
472
473 ATF_TEST_CASE_WITHOUT_HEAD(instantiate__vector_value__unknown_vector);
ATF_TEST_CASE_BODY(instantiate__vector_value__unknown_vector)474 ATF_TEST_CASE_BODY(instantiate__vector_value__unknown_vector)
475 {
476 const std::string input =
477 "%%testvector(j)%%\n";
478
479 text::templates_def templates;
480 templates.add_vector("testvector2");
481
482 do_test_fail(templates, input, "Unknown vector 'testvector'");
483 }
484
485
486 ATF_TEST_CASE_WITHOUT_HEAD(instantiate__vector_value__out_of_range__empty);
ATF_TEST_CASE_BODY(instantiate__vector_value__out_of_range__empty)487 ATF_TEST_CASE_BODY(instantiate__vector_value__out_of_range__empty)
488 {
489 const std::string input =
490 "%%testvector(j)%%\n";
491
492 text::templates_def templates;
493 templates.add_vector("testvector");
494 templates.add_variable("j", "0");
495
496 do_test_fail(templates, input, "Index 'j' out of range at position '0'");
497 }
498
499
500 ATF_TEST_CASE_WITHOUT_HEAD(instantiate__vector_value__out_of_range__not_empty);
ATF_TEST_CASE_BODY(instantiate__vector_value__out_of_range__not_empty)501 ATF_TEST_CASE_BODY(instantiate__vector_value__out_of_range__not_empty)
502 {
503 const std::string input =
504 "%%testvector(j)%%\n";
505
506 text::templates_def templates;
507 templates.add_vector("testvector");
508 templates.add_to_vector("testvector", "a");
509 templates.add_to_vector("testvector", "b");
510 templates.add_variable("j", "2");
511
512 do_test_fail(templates, input, "Index 'j' out of range at position '2'");
513 }
514
515
516 ATF_TEST_CASE_WITHOUT_HEAD(instantiate__if__one_level__taken);
ATF_TEST_CASE_BODY(instantiate__if__one_level__taken)517 ATF_TEST_CASE_BODY(instantiate__if__one_level__taken)
518 {
519 const std::string input =
520 "first line\n"
521 "%if defined(some_var)\n"
522 "hello from within the variable conditional\n"
523 "%endif\n"
524 "%if defined(some_vector)\n"
525 "hello from within the vector conditional\n"
526 "%else\n"
527 "bye from within the vector conditional\n"
528 "%endif\n"
529 "some more\n";
530
531 const std::string exp_output =
532 "first line\n"
533 "hello from within the variable conditional\n"
534 "hello from within the vector conditional\n"
535 "some more\n";
536
537 text::templates_def templates;
538 templates.add_variable("some_var", "zzz");
539 templates.add_vector("some_vector");
540
541 do_test_ok(templates, input, exp_output);
542 }
543
544
545 ATF_TEST_CASE_WITHOUT_HEAD(instantiate__if__one_level__not_taken);
ATF_TEST_CASE_BODY(instantiate__if__one_level__not_taken)546 ATF_TEST_CASE_BODY(instantiate__if__one_level__not_taken)
547 {
548 const std::string input =
549 "first line\n"
550 "%if defined(some_var)\n"
551 "hello from within the variable conditional\n"
552 "%endif\n"
553 "%if defined(some_vector)\n"
554 "hello from within the vector conditional\n"
555 "%else\n"
556 "bye from within the vector conditional\n"
557 "%endif\n"
558 "some more\n";
559
560 const std::string exp_output =
561 "first line\n"
562 "bye from within the vector conditional\n"
563 "some more\n";
564
565 text::templates_def templates;
566
567 do_test_ok(templates, input, exp_output);
568 }
569
570
571 ATF_TEST_CASE_WITHOUT_HEAD(instantiate__if__multiple_levels__taken);
ATF_TEST_CASE_BODY(instantiate__if__multiple_levels__taken)572 ATF_TEST_CASE_BODY(instantiate__if__multiple_levels__taken)
573 {
574 const std::string input =
575 "first line\n"
576 "%if defined(var1)\n"
577 "first before\n"
578 "%if length(var2)\n"
579 "second before\n"
580 "%if defined(var3)\n"
581 "third before\n"
582 "hello from within the conditional\n"
583 "third after\n"
584 "%endif\n"
585 "second after\n"
586 "%else\n"
587 "second after not shown\n"
588 "%endif\n"
589 "first after\n"
590 "%endif\n"
591 "some more\n";
592
593 const std::string exp_output =
594 "first line\n"
595 "first before\n"
596 "second before\n"
597 "third before\n"
598 "hello from within the conditional\n"
599 "third after\n"
600 "second after\n"
601 "first after\n"
602 "some more\n";
603
604 text::templates_def templates;
605 templates.add_variable("var1", "false");
606 templates.add_vector("var2");
607 templates.add_to_vector("var2", "not-empty");
608 templates.add_variable("var3", "foobar");
609
610 do_test_ok(templates, input, exp_output);
611 }
612
613
614 ATF_TEST_CASE_WITHOUT_HEAD(instantiate__if__multiple_levels__not_taken);
ATF_TEST_CASE_BODY(instantiate__if__multiple_levels__not_taken)615 ATF_TEST_CASE_BODY(instantiate__if__multiple_levels__not_taken)
616 {
617 const std::string input =
618 "first line\n"
619 "%if defined(var1)\n"
620 "first before\n"
621 "%if length(var2)\n"
622 "second before\n"
623 "%if defined(var3)\n"
624 "third before\n"
625 "hello from within the conditional\n"
626 "third after\n"
627 "%else\n"
628 "will not be shown either\n"
629 "%endif\n"
630 "second after\n"
631 "%else\n"
632 "second after shown\n"
633 "%endif\n"
634 "first after\n"
635 "%endif\n"
636 "some more\n";
637
638 const std::string exp_output =
639 "first line\n"
640 "first before\n"
641 "second after shown\n"
642 "first after\n"
643 "some more\n";
644
645 text::templates_def templates;
646 templates.add_variable("var1", "false");
647 templates.add_vector("var2");
648 templates.add_vector("var3");
649
650 do_test_ok(templates, input, exp_output);
651 }
652
653
654 ATF_TEST_CASE_WITHOUT_HEAD(instantiate__loop__no_iterations);
ATF_TEST_CASE_BODY(instantiate__loop__no_iterations)655 ATF_TEST_CASE_BODY(instantiate__loop__no_iterations)
656 {
657 const std::string input =
658 "first line\n"
659 "%loop table1 i\n"
660 "hello\n"
661 "value in vector: %%table1(i)%%\n"
662 "%if defined(var1)\n" "some other text\n" "%endif\n"
663 "%endloop\n"
664 "some more\n";
665
666 const std::string exp_output =
667 "first line\n"
668 "some more\n";
669
670 text::templates_def templates;
671 templates.add_variable("var1", "defined");
672 templates.add_vector("table1");
673
674 do_test_ok(templates, input, exp_output);
675 }
676
677
678 ATF_TEST_CASE_WITHOUT_HEAD(instantiate__loop__multiple_iterations);
ATF_TEST_CASE_BODY(instantiate__loop__multiple_iterations)679 ATF_TEST_CASE_BODY(instantiate__loop__multiple_iterations)
680 {
681 const std::string input =
682 "first line\n"
683 "%loop table1 i\n"
684 "hello %%table1(i)%% %%table2(i)%%\n"
685 "%endloop\n"
686 "some more\n";
687
688 const std::string exp_output =
689 "first line\n"
690 "hello foo1 foo2\n"
691 "hello bar1 bar2\n"
692 "some more\n";
693
694 text::templates_def templates;
695 templates.add_vector("table1");
696 templates.add_to_vector("table1", "foo1");
697 templates.add_to_vector("table1", "bar1");
698 templates.add_vector("table2");
699 templates.add_to_vector("table2", "foo2");
700 templates.add_to_vector("table2", "bar2");
701
702 do_test_ok(templates, input, exp_output);
703 }
704
705
706 ATF_TEST_CASE_WITHOUT_HEAD(instantiate__loop__nested__no_iterations);
ATF_TEST_CASE_BODY(instantiate__loop__nested__no_iterations)707 ATF_TEST_CASE_BODY(instantiate__loop__nested__no_iterations)
708 {
709 const std::string input =
710 "first line\n"
711 "%loop table1 i\n"
712 "before: %%table1(i)%%\n"
713 "%loop table2 j\n"
714 "before: %%table2(j)%%\n"
715 "%loop table3 k\n"
716 "%%table3(k)%%\n"
717 "%endloop\n"
718 "after: %%table2(i)%%\n"
719 "%endloop\n"
720 "after: %%table1(i)%%\n"
721 "%endloop\n"
722 "some more\n";
723
724 const std::string exp_output =
725 "first line\n"
726 "before: a\n"
727 "after: a\n"
728 "before: b\n"
729 "after: b\n"
730 "some more\n";
731
732 text::templates_def templates;
733 templates.add_vector("table1");
734 templates.add_to_vector("table1", "a");
735 templates.add_to_vector("table1", "b");
736 templates.add_vector("table2");
737 templates.add_vector("table3");
738 templates.add_to_vector("table3", "1");
739
740 do_test_ok(templates, input, exp_output);
741 }
742
743
744 ATF_TEST_CASE_WITHOUT_HEAD(instantiate__loop__nested__multiple_iterations);
ATF_TEST_CASE_BODY(instantiate__loop__nested__multiple_iterations)745 ATF_TEST_CASE_BODY(instantiate__loop__nested__multiple_iterations)
746 {
747 const std::string input =
748 "first line\n"
749 "%loop table1 i\n"
750 "%loop table2 j\n"
751 "%%table1(i)%% %%table2(j)%%\n"
752 "%endloop\n"
753 "%endloop\n"
754 "some more\n";
755
756 const std::string exp_output =
757 "first line\n"
758 "a 1\n"
759 "a 2\n"
760 "a 3\n"
761 "b 1\n"
762 "b 2\n"
763 "b 3\n"
764 "some more\n";
765
766 text::templates_def templates;
767 templates.add_vector("table1");
768 templates.add_to_vector("table1", "a");
769 templates.add_to_vector("table1", "b");
770 templates.add_vector("table2");
771 templates.add_to_vector("table2", "1");
772 templates.add_to_vector("table2", "2");
773 templates.add_to_vector("table2", "3");
774
775 do_test_ok(templates, input, exp_output);
776 }
777
778
779 ATF_TEST_CASE_WITHOUT_HEAD(instantiate__loop__sequential);
ATF_TEST_CASE_BODY(instantiate__loop__sequential)780 ATF_TEST_CASE_BODY(instantiate__loop__sequential)
781 {
782 const std::string input =
783 "first line\n"
784 "%loop table1 iter\n"
785 "1: %%table1(iter)%%\n"
786 "%endloop\n"
787 "divider\n"
788 "%loop table2 iter\n"
789 "2: %%table2(iter)%%\n"
790 "%endloop\n"
791 "divider\n"
792 "%loop table3 iter\n"
793 "3: %%table3(iter)%%\n"
794 "%endloop\n"
795 "divider\n"
796 "%loop table4 iter\n"
797 "4: %%table4(iter)%%\n"
798 "%endloop\n"
799 "some more\n";
800
801 const std::string exp_output =
802 "first line\n"
803 "1: a\n"
804 "1: b\n"
805 "divider\n"
806 "divider\n"
807 "divider\n"
808 "4: 1\n"
809 "4: 2\n"
810 "4: 3\n"
811 "some more\n";
812
813 text::templates_def templates;
814 templates.add_vector("table1");
815 templates.add_to_vector("table1", "a");
816 templates.add_to_vector("table1", "b");
817 templates.add_vector("table2");
818 templates.add_vector("table3");
819 templates.add_vector("table4");
820 templates.add_to_vector("table4", "1");
821 templates.add_to_vector("table4", "2");
822 templates.add_to_vector("table4", "3");
823
824 do_test_ok(templates, input, exp_output);
825 }
826
827
828 ATF_TEST_CASE_WITHOUT_HEAD(instantiate__loop__scoping);
ATF_TEST_CASE_BODY(instantiate__loop__scoping)829 ATF_TEST_CASE_BODY(instantiate__loop__scoping)
830 {
831 const std::string input =
832 "%loop table1 i\n"
833 "%if defined(i)\n" "i defined inside scope 1\n" "%endif\n"
834 "%loop table2 j\n"
835 "%if defined(i)\n" "i defined inside scope 2\n" "%endif\n"
836 "%if defined(j)\n" "j defined inside scope 2\n" "%endif\n"
837 "%endloop\n"
838 "%if defined(j)\n" "j defined inside scope 1\n" "%endif\n"
839 "%endloop\n"
840 "%if defined(i)\n" "i defined outside\n" "%endif\n"
841 "%if defined(j)\n" "j defined outside\n" "%endif\n";
842
843 const std::string exp_output =
844 "i defined inside scope 1\n"
845 "i defined inside scope 2\n"
846 "j defined inside scope 2\n"
847 "i defined inside scope 1\n"
848 "i defined inside scope 2\n"
849 "j defined inside scope 2\n";
850
851 text::templates_def templates;
852 templates.add_vector("table1");
853 templates.add_to_vector("table1", "first");
854 templates.add_to_vector("table1", "second");
855 templates.add_vector("table2");
856 templates.add_to_vector("table2", "first");
857
858 do_test_ok(templates, input, exp_output);
859 }
860
861
862 ATF_TEST_CASE_WITHOUT_HEAD(instantiate__mismatched_delimiters);
ATF_TEST_CASE_BODY(instantiate__mismatched_delimiters)863 ATF_TEST_CASE_BODY(instantiate__mismatched_delimiters)
864 {
865 const std::string input =
866 "this is some %% text\n"
867 "and this is %%var%% text%%\n";
868
869 const std::string exp_output =
870 "this is some %% text\n"
871 "and this is some more text%%\n";
872
873 text::templates_def templates;
874 templates.add_variable("var", "some more");
875
876 do_test_ok(templates, input, exp_output);
877 }
878
879
880 ATF_TEST_CASE_WITHOUT_HEAD(instantiate__empty_statement);
ATF_TEST_CASE_BODY(instantiate__empty_statement)881 ATF_TEST_CASE_BODY(instantiate__empty_statement)
882 {
883 do_test_fail(text::templates_def(), "%\n", "Empty statement");
884 }
885
886
887 ATF_TEST_CASE_WITHOUT_HEAD(instantiate__unknown_statement);
ATF_TEST_CASE_BODY(instantiate__unknown_statement)888 ATF_TEST_CASE_BODY(instantiate__unknown_statement)
889 {
890 do_test_fail(text::templates_def(), "%if2\n", "Unknown statement 'if2'");
891 }
892
893
894 ATF_TEST_CASE_WITHOUT_HEAD(instantiate__invalid_narguments);
ATF_TEST_CASE_BODY(instantiate__invalid_narguments)895 ATF_TEST_CASE_BODY(instantiate__invalid_narguments)
896 {
897 do_test_fail(text::templates_def(), "%if a b\n",
898 "Invalid number of arguments for statement 'if'");
899 }
900
901
902 ATF_TEST_CASE_WITHOUT_HEAD(instantiate__files__ok);
ATF_TEST_CASE_BODY(instantiate__files__ok)903 ATF_TEST_CASE_BODY(instantiate__files__ok)
904 {
905 text::templates_def templates;
906 templates.add_variable("string", "Hello, world!");
907
908 atf::utils::create_file("input.txt", "The string is: %%string%%\n");
909
910 text::instantiate(templates, fs::path("input.txt"), fs::path("output.txt"));
911
912 std::ifstream output("output.txt");
913 std::string line;
914 ATF_REQUIRE(std::getline(output, line).good());
915 ATF_REQUIRE_EQ(line, "The string is: Hello, world!");
916 ATF_REQUIRE(std::getline(output, line).eof());
917 }
918
919
920 ATF_TEST_CASE_WITHOUT_HEAD(instantiate__files__input_error);
ATF_TEST_CASE_BODY(instantiate__files__input_error)921 ATF_TEST_CASE_BODY(instantiate__files__input_error)
922 {
923 text::templates_def templates;
924 ATF_REQUIRE_THROW_RE(text::error, "Failed to open input.txt for read",
925 text::instantiate(templates, fs::path("input.txt"),
926 fs::path("output.txt")));
927 }
928
929
930 ATF_TEST_CASE(instantiate__files__output_error);
ATF_TEST_CASE_HEAD(instantiate__files__output_error)931 ATF_TEST_CASE_HEAD(instantiate__files__output_error)
932 {
933 set_md_var("require.user", "unprivileged");
934 }
ATF_TEST_CASE_BODY(instantiate__files__output_error)935 ATF_TEST_CASE_BODY(instantiate__files__output_error)
936 {
937 text::templates_def templates;
938
939 atf::utils::create_file("input.txt", "");
940
941 fs::mkdir(fs::path("dir"), 0444);
942
943 ATF_REQUIRE_THROW_RE(text::error, "Failed to open dir/output.txt for write",
944 text::instantiate(templates, fs::path("input.txt"),
945 fs::path("dir/output.txt")));
946 }
947
948
ATF_INIT_TEST_CASES(tcs)949 ATF_INIT_TEST_CASES(tcs)
950 {
951 ATF_ADD_TEST_CASE(tcs, templates_def__add_variable__first);
952 ATF_ADD_TEST_CASE(tcs, templates_def__add_variable__replace);
953 ATF_ADD_TEST_CASE(tcs, templates_def__remove_variable);
954 ATF_ADD_TEST_CASE(tcs, templates_def__add_vector__first);
955 ATF_ADD_TEST_CASE(tcs, templates_def__add_vector__replace);
956 ATF_ADD_TEST_CASE(tcs, templates_def__add_to_vector);
957 ATF_ADD_TEST_CASE(tcs, templates_def__exists__variable);
958 ATF_ADD_TEST_CASE(tcs, templates_def__exists__vector);
959 ATF_ADD_TEST_CASE(tcs, templates_def__get_variable__ok);
960 ATF_ADD_TEST_CASE(tcs, templates_def__get_variable__unknown);
961 ATF_ADD_TEST_CASE(tcs, templates_def__get_vector__ok);
962 ATF_ADD_TEST_CASE(tcs, templates_def__get_vector__unknown);
963 ATF_ADD_TEST_CASE(tcs, templates_def__evaluate__variable__ok);
964 ATF_ADD_TEST_CASE(tcs, templates_def__evaluate__variable__unknown);
965 ATF_ADD_TEST_CASE(tcs, templates_def__evaluate__vector__ok);
966 ATF_ADD_TEST_CASE(tcs, templates_def__evaluate__vector__unknown_vector);
967 ATF_ADD_TEST_CASE(tcs, templates_def__evaluate__vector__unknown_index);
968 ATF_ADD_TEST_CASE(tcs, templates_def__evaluate__vector__out_of_range);
969 ATF_ADD_TEST_CASE(tcs, templates_def__evaluate__defined);
970 ATF_ADD_TEST_CASE(tcs, templates_def__evaluate__length__ok);
971 ATF_ADD_TEST_CASE(tcs, templates_def__evaluate__length__unknown_vector);
972 ATF_ADD_TEST_CASE(tcs, templates_def__evaluate__parenthesis_error);
973
974 ATF_ADD_TEST_CASE(tcs, instantiate__empty_input);
975 ATF_ADD_TEST_CASE(tcs, instantiate__value__ok);
976 ATF_ADD_TEST_CASE(tcs, instantiate__value__unknown_variable);
977 ATF_ADD_TEST_CASE(tcs, instantiate__vector_length__ok);
978 ATF_ADD_TEST_CASE(tcs, instantiate__vector_length__unknown_vector);
979 ATF_ADD_TEST_CASE(tcs, instantiate__vector_value__ok);
980 ATF_ADD_TEST_CASE(tcs, instantiate__vector_value__unknown_vector);
981 ATF_ADD_TEST_CASE(tcs, instantiate__vector_value__out_of_range__empty);
982 ATF_ADD_TEST_CASE(tcs, instantiate__vector_value__out_of_range__not_empty);
983 ATF_ADD_TEST_CASE(tcs, instantiate__if__one_level__taken);
984 ATF_ADD_TEST_CASE(tcs, instantiate__if__one_level__not_taken);
985 ATF_ADD_TEST_CASE(tcs, instantiate__if__multiple_levels__taken);
986 ATF_ADD_TEST_CASE(tcs, instantiate__if__multiple_levels__not_taken);
987 ATF_ADD_TEST_CASE(tcs, instantiate__loop__no_iterations);
988 ATF_ADD_TEST_CASE(tcs, instantiate__loop__multiple_iterations);
989 ATF_ADD_TEST_CASE(tcs, instantiate__loop__nested__no_iterations);
990 ATF_ADD_TEST_CASE(tcs, instantiate__loop__nested__multiple_iterations);
991 ATF_ADD_TEST_CASE(tcs, instantiate__loop__sequential);
992 ATF_ADD_TEST_CASE(tcs, instantiate__loop__scoping);
993 ATF_ADD_TEST_CASE(tcs, instantiate__mismatched_delimiters);
994 ATF_ADD_TEST_CASE(tcs, instantiate__empty_statement);
995 ATF_ADD_TEST_CASE(tcs, instantiate__unknown_statement);
996 ATF_ADD_TEST_CASE(tcs, instantiate__invalid_narguments);
997
998 ATF_ADD_TEST_CASE(tcs, instantiate__files__ok);
999 ATF_ADD_TEST_CASE(tcs, instantiate__files__input_error);
1000 ATF_ADD_TEST_CASE(tcs, instantiate__files__output_error);
1001 }
1002