1// Copyright 2012 The Kyua Authors.
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
40namespace fs = utils::fs;
41namespace text = utils::text;
42
43
44namespace {
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.
55static void
56do_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.
75static void
76do_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
90ATF_TEST_CASE_WITHOUT_HEAD(templates_def__add_variable__first);
91ATF_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
99ATF_TEST_CASE_WITHOUT_HEAD(templates_def__add_variable__replace);
100ATF_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
109ATF_TEST_CASE_WITHOUT_HEAD(templates_def__remove_variable);
110ATF_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
120ATF_TEST_CASE_WITHOUT_HEAD(templates_def__add_vector__first);
121ATF_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
129ATF_TEST_CASE_WITHOUT_HEAD(templates_def__add_vector__replace);
130ATF_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
141ATF_TEST_CASE_WITHOUT_HEAD(templates_def__add_to_vector);
142ATF_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
162ATF_TEST_CASE_WITHOUT_HEAD(templates_def__exists__variable);
163ATF_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
174ATF_TEST_CASE_WITHOUT_HEAD(templates_def__exists__vector);
175ATF_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
186ATF_TEST_CASE_WITHOUT_HEAD(templates_def__get_variable__ok);
187ATF_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
197ATF_TEST_CASE_WITHOUT_HEAD(templates_def__get_variable__unknown);
198ATF_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
207ATF_TEST_CASE_WITHOUT_HEAD(templates_def__get_vector__ok);
208ATF_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
219ATF_TEST_CASE_WITHOUT_HEAD(templates_def__get_vector__unknown);
220ATF_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
229ATF_TEST_CASE_WITHOUT_HEAD(templates_def__evaluate__variable__ok);
230ATF_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
240ATF_TEST_CASE_WITHOUT_HEAD(templates_def__evaluate__variable__unknown);
241ATF_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
250ATF_TEST_CASE_WITHOUT_HEAD(templates_def__evaluate__vector__ok);
251ATF_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
268ATF_TEST_CASE_WITHOUT_HEAD(templates_def__evaluate__vector__unknown_vector);
269ATF_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
280ATF_TEST_CASE_WITHOUT_HEAD(templates_def__evaluate__vector__unknown_index);
281ATF_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
292ATF_TEST_CASE_WITHOUT_HEAD(templates_def__evaluate__vector__out_of_range);
293ATF_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
304ATF_TEST_CASE_WITHOUT_HEAD(templates_def__evaluate__defined);
305ATF_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
317ATF_TEST_CASE_WITHOUT_HEAD(templates_def__evaluate__length__ok);
318ATF_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
332ATF_TEST_CASE_WITHOUT_HEAD(templates_def__evaluate__length__unknown_vector);
333ATF_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
342ATF_TEST_CASE_WITHOUT_HEAD(templates_def__evaluate__parenthesis_error);
343ATF_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
355ATF_TEST_CASE_WITHOUT_HEAD(instantiate__empty_input);
356ATF_TEST_CASE_BODY(instantiate__empty_input)
357{
358    const text::templates_def templates;
359    do_test_ok(templates, "", "");
360}
361
362
363ATF_TEST_CASE_WITHOUT_HEAD(instantiate__value__ok);
364ATF_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
390ATF_TEST_CASE_WITHOUT_HEAD(instantiate__value__unknown_variable);
391ATF_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
403ATF_TEST_CASE_WITHOUT_HEAD(instantiate__vector_length__ok);
404ATF_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
428ATF_TEST_CASE_WITHOUT_HEAD(instantiate__vector_length__unknown_vector);
429ATF_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
441ATF_TEST_CASE_WITHOUT_HEAD(instantiate__vector_value__ok);
442ATF_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
473ATF_TEST_CASE_WITHOUT_HEAD(instantiate__vector_value__unknown_vector);
474ATF_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
486ATF_TEST_CASE_WITHOUT_HEAD(instantiate__vector_value__out_of_range__empty);
487ATF_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
500ATF_TEST_CASE_WITHOUT_HEAD(instantiate__vector_value__out_of_range__not_empty);
501ATF_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
516ATF_TEST_CASE_WITHOUT_HEAD(instantiate__if__one_level__taken);
517ATF_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
545ATF_TEST_CASE_WITHOUT_HEAD(instantiate__if__one_level__not_taken);
546ATF_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
571ATF_TEST_CASE_WITHOUT_HEAD(instantiate__if__multiple_levels__taken);
572ATF_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
614ATF_TEST_CASE_WITHOUT_HEAD(instantiate__if__multiple_levels__not_taken);
615ATF_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
654ATF_TEST_CASE_WITHOUT_HEAD(instantiate__loop__no_iterations);
655ATF_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
678ATF_TEST_CASE_WITHOUT_HEAD(instantiate__loop__multiple_iterations);
679ATF_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
706ATF_TEST_CASE_WITHOUT_HEAD(instantiate__loop__nested__no_iterations);
707ATF_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
744ATF_TEST_CASE_WITHOUT_HEAD(instantiate__loop__nested__multiple_iterations);
745ATF_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
779ATF_TEST_CASE_WITHOUT_HEAD(instantiate__loop__sequential);
780ATF_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
828ATF_TEST_CASE_WITHOUT_HEAD(instantiate__loop__scoping);
829ATF_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
862ATF_TEST_CASE_WITHOUT_HEAD(instantiate__mismatched_delimiters);
863ATF_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
880ATF_TEST_CASE_WITHOUT_HEAD(instantiate__empty_statement);
881ATF_TEST_CASE_BODY(instantiate__empty_statement)
882{
883    do_test_fail(text::templates_def(), "%\n", "Empty statement");
884}
885
886
887ATF_TEST_CASE_WITHOUT_HEAD(instantiate__unknown_statement);
888ATF_TEST_CASE_BODY(instantiate__unknown_statement)
889{
890    do_test_fail(text::templates_def(), "%if2\n", "Unknown statement 'if2'");
891}
892
893
894ATF_TEST_CASE_WITHOUT_HEAD(instantiate__invalid_narguments);
895ATF_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
902ATF_TEST_CASE_WITHOUT_HEAD(instantiate__files__ok);
903ATF_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
920ATF_TEST_CASE_WITHOUT_HEAD(instantiate__files__input_error);
921ATF_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
930ATF_TEST_CASE(instantiate__files__output_error);
931ATF_TEST_CASE_HEAD(instantiate__files__output_error)
932{
933    set_md_var("require.user", "unprivileged");
934}
935ATF_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
949ATF_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