Home | History | Annotate | Line # | Download | only in text
templates_test.cpp revision 1.1.1.1.4.2
      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
     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
     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);
     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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    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);
    931 ATF_TEST_CASE_HEAD(instantiate__files__output_error)
    932 {
    933     set_md_var("require.user", "unprivileged");
    934 }
    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 
    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