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 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