// Copyright 2012 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of Google Inc. nor the names of its contributors // may be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "utils/text/operations.ipp" #include #include #include #include #include #include "utils/text/exceptions.hpp" namespace text = utils::text; namespace { /// Tests text::refill() on an input string with a range of widths. /// /// \param expected The expected refilled paragraph. /// \param input The input paragraph to be refilled. /// \param first_width The first width to validate. /// \param last_width The last width to validate (inclusive). static void refill_test(const char* expected, const char* input, const std::size_t first_width, const std::size_t last_width) { for (std::size_t width = first_width; width <= last_width; ++width) { const std::vector< std::string > lines = text::split(expected, '\n'); std::cout << "Breaking at width " << width << '\n'; ATF_REQUIRE_EQ(expected, text::refill_as_string(input, width)); ATF_REQUIRE(lines == text::refill(input, width)); } } } // anonymous namespace ATF_TEST_CASE_WITHOUT_HEAD(quote__empty); ATF_TEST_CASE_BODY(quote__empty) { ATF_REQUIRE_EQ("''", text::quote("", '\'')); ATF_REQUIRE_EQ("##", text::quote("", '#')); } ATF_TEST_CASE_WITHOUT_HEAD(quote__no_escaping); ATF_TEST_CASE_BODY(quote__no_escaping) { ATF_REQUIRE_EQ("'Some text\"'", text::quote("Some text\"", '\'')); ATF_REQUIRE_EQ("#Another'string#", text::quote("Another'string", '#')); } ATF_TEST_CASE_WITHOUT_HEAD(quote__some_escaping); ATF_TEST_CASE_BODY(quote__some_escaping) { ATF_REQUIRE_EQ("'Some\\'text'", text::quote("Some'text", '\'')); ATF_REQUIRE_EQ("#Some\\#text#", text::quote("Some#text", '#')); ATF_REQUIRE_EQ("'More than one\\' quote\\''", text::quote("More than one' quote'", '\'')); ATF_REQUIRE_EQ("'Multiple quotes \\'\\'\\' together'", text::quote("Multiple quotes ''' together", '\'')); ATF_REQUIRE_EQ("'\\'escape at the beginning'", text::quote("'escape at the beginning", '\'')); ATF_REQUIRE_EQ("'escape at the end\\''", text::quote("escape at the end'", '\'')); } ATF_TEST_CASE_WITHOUT_HEAD(refill__empty); ATF_TEST_CASE_BODY(refill__empty) { ATF_REQUIRE_EQ(1, text::refill("", 0).size()); ATF_REQUIRE(text::refill("", 0)[0].empty()); ATF_REQUIRE_EQ("", text::refill_as_string("", 0)); ATF_REQUIRE_EQ(1, text::refill("", 10).size()); ATF_REQUIRE(text::refill("", 10)[0].empty()); ATF_REQUIRE_EQ("", text::refill_as_string("", 10)); } ATF_TEST_CASE_WITHOUT_HEAD(refill__no_changes); ATF_TEST_CASE_BODY(refill__no_changes) { std::vector< std::string > exp_lines; exp_lines.push_back("foo bar\nbaz"); ATF_REQUIRE(exp_lines == text::refill("foo bar\nbaz", 12)); ATF_REQUIRE_EQ("foo bar\nbaz", text::refill_as_string("foo bar\nbaz", 12)); ATF_REQUIRE(exp_lines == text::refill("foo bar\nbaz", 18)); ATF_REQUIRE_EQ("foo bar\nbaz", text::refill_as_string("foo bar\nbaz", 80)); } ATF_TEST_CASE_WITHOUT_HEAD(refill__break_one); ATF_TEST_CASE_BODY(refill__break_one) { refill_test("only break the\nfirst line", "only break the first line", 14, 19); } ATF_TEST_CASE_WITHOUT_HEAD(refill__break_one__not_first_word); ATF_TEST_CASE_BODY(refill__break_one__not_first_word) { refill_test("first-long-word\nother\nwords", "first-long-word other words", 6, 10); refill_test("first-long-word\nother words", "first-long-word other words", 11, 20); refill_test("first-long-word other\nwords", "first-long-word other words", 21, 26); refill_test("first-long-word other words", "first-long-word other words", 27, 28); } ATF_TEST_CASE_WITHOUT_HEAD(refill__break_many); ATF_TEST_CASE_BODY(refill__break_many) { refill_test("this is a long\nparagraph to be\nsplit into\npieces", "this is a long paragraph to be split into pieces", 15, 15); } ATF_TEST_CASE_WITHOUT_HEAD(refill__cannot_break); ATF_TEST_CASE_BODY(refill__cannot_break) { refill_test("this-is-a-long-string", "this-is-a-long-string", 5, 5); refill_test("this is\na-string-with-long-words", "this is a-string-with-long-words", 10, 10); } ATF_TEST_CASE_WITHOUT_HEAD(refill__preserve_whitespace); ATF_TEST_CASE_BODY(refill__preserve_whitespace) { refill_test("foo bar baz ", "foo bar baz ", 80, 80); refill_test("foo \n bar", "foo bar", 5, 5); std::vector< std::string > exp_lines; exp_lines.push_back("foo \n"); exp_lines.push_back(" bar"); ATF_REQUIRE(exp_lines == text::refill("foo \n bar", 5)); ATF_REQUIRE_EQ("foo \n\n bar", text::refill_as_string("foo \n bar", 5)); } ATF_TEST_CASE_WITHOUT_HEAD(join__empty); ATF_TEST_CASE_BODY(join__empty) { std::vector< std::string > lines; ATF_REQUIRE_EQ("", text::join(lines, " ")); } ATF_TEST_CASE_WITHOUT_HEAD(join__one); ATF_TEST_CASE_BODY(join__one) { std::vector< std::string > lines; lines.push_back("first line"); ATF_REQUIRE_EQ("first line", text::join(lines, "*")); } ATF_TEST_CASE_WITHOUT_HEAD(join__several); ATF_TEST_CASE_BODY(join__several) { std::vector< std::string > lines; lines.push_back("first abc"); lines.push_back("second"); lines.push_back("and last line"); ATF_REQUIRE_EQ("first abc second and last line", text::join(lines, " ")); ATF_REQUIRE_EQ("first abc***second***and last line", text::join(lines, "***")); } ATF_TEST_CASE_WITHOUT_HEAD(join__unordered); ATF_TEST_CASE_BODY(join__unordered) { std::set< std::string > lines; lines.insert("first"); lines.insert("second"); const std::string joined = text::join(lines, " "); ATF_REQUIRE(joined == "first second" || joined == "second first"); } ATF_TEST_CASE_WITHOUT_HEAD(split__empty); ATF_TEST_CASE_BODY(split__empty) { std::vector< std::string > words = text::split("", ' '); std::vector< std::string > exp_words; ATF_REQUIRE(exp_words == words); } ATF_TEST_CASE_WITHOUT_HEAD(split__one); ATF_TEST_CASE_BODY(split__one) { std::vector< std::string > words = text::split("foo", ' '); std::vector< std::string > exp_words; exp_words.push_back("foo"); ATF_REQUIRE(exp_words == words); } ATF_TEST_CASE_WITHOUT_HEAD(split__several__simple); ATF_TEST_CASE_BODY(split__several__simple) { std::vector< std::string > words = text::split("foo bar baz", ' '); std::vector< std::string > exp_words; exp_words.push_back("foo"); exp_words.push_back("bar"); exp_words.push_back("baz"); ATF_REQUIRE(exp_words == words); } ATF_TEST_CASE_WITHOUT_HEAD(split__several__delimiters); ATF_TEST_CASE_BODY(split__several__delimiters) { std::vector< std::string > words = text::split("XfooXXbarXXXbazXX", 'X'); std::vector< std::string > exp_words; exp_words.push_back(""); exp_words.push_back("foo"); exp_words.push_back(""); exp_words.push_back("bar"); exp_words.push_back(""); exp_words.push_back(""); exp_words.push_back("baz"); exp_words.push_back(""); exp_words.push_back(""); ATF_REQUIRE(exp_words == words); } ATF_TEST_CASE_WITHOUT_HEAD(to_type__ok__bool); ATF_TEST_CASE_BODY(to_type__ok__bool) { ATF_REQUIRE( text::to_type< bool >("true")); ATF_REQUIRE(!text::to_type< bool >("false")); } ATF_TEST_CASE_WITHOUT_HEAD(to_type__ok__numerical); ATF_TEST_CASE_BODY(to_type__ok__numerical) { ATF_REQUIRE_EQ(12, text::to_type< int >("12")); ATF_REQUIRE_EQ(18745, text::to_type< int >("18745")); ATF_REQUIRE_EQ(-12345, text::to_type< int >("-12345")); ATF_REQUIRE_EQ(12.0, text::to_type< double >("12")); ATF_REQUIRE_EQ(12.5, text::to_type< double >("12.5")); } ATF_TEST_CASE_WITHOUT_HEAD(to_type__ok__string); ATF_TEST_CASE_BODY(to_type__ok__string) { // While this seems redundant, having this particular specialization that // does nothing allows callers to delegate work to to_type without worrying // about the particular type being converted. ATF_REQUIRE_EQ("", text::to_type< std::string >("")); ATF_REQUIRE_EQ(" abcd ", text::to_type< std::string >(" abcd ")); } ATF_TEST_CASE_WITHOUT_HEAD(to_type__empty); ATF_TEST_CASE_BODY(to_type__empty) { ATF_REQUIRE_THROW(text::value_error, text::to_type< int >("")); } ATF_TEST_CASE_WITHOUT_HEAD(to_type__invalid__bool); ATF_TEST_CASE_BODY(to_type__invalid__bool) { ATF_REQUIRE_THROW(text::value_error, text::to_type< bool >("")); ATF_REQUIRE_THROW(text::value_error, text::to_type< bool >("true ")); ATF_REQUIRE_THROW(text::value_error, text::to_type< bool >("foo")); } ATF_TEST_CASE_WITHOUT_HEAD(to_type__invalid__numerical); ATF_TEST_CASE_BODY(to_type__invalid__numerical) { ATF_REQUIRE_THROW(text::value_error, text::to_type< int >(" 3")); ATF_REQUIRE_THROW(text::value_error, text::to_type< int >("3 ")); ATF_REQUIRE_THROW(text::value_error, text::to_type< int >("3a")); ATF_REQUIRE_THROW(text::value_error, text::to_type< int >("a3")); } ATF_INIT_TEST_CASES(tcs) { ATF_ADD_TEST_CASE(tcs, quote__empty); ATF_ADD_TEST_CASE(tcs, quote__no_escaping); ATF_ADD_TEST_CASE(tcs, quote__some_escaping); ATF_ADD_TEST_CASE(tcs, refill__empty); ATF_ADD_TEST_CASE(tcs, refill__no_changes); ATF_ADD_TEST_CASE(tcs, refill__break_one); ATF_ADD_TEST_CASE(tcs, refill__break_one__not_first_word); ATF_ADD_TEST_CASE(tcs, refill__break_many); ATF_ADD_TEST_CASE(tcs, refill__cannot_break); ATF_ADD_TEST_CASE(tcs, refill__preserve_whitespace); ATF_ADD_TEST_CASE(tcs, join__empty); ATF_ADD_TEST_CASE(tcs, join__one); ATF_ADD_TEST_CASE(tcs, join__several); ATF_ADD_TEST_CASE(tcs, join__unordered); ATF_ADD_TEST_CASE(tcs, split__empty); ATF_ADD_TEST_CASE(tcs, split__one); ATF_ADD_TEST_CASE(tcs, split__several__simple); ATF_ADD_TEST_CASE(tcs, split__several__delimiters); ATF_ADD_TEST_CASE(tcs, to_type__ok__bool); ATF_ADD_TEST_CASE(tcs, to_type__ok__numerical); ATF_ADD_TEST_CASE(tcs, to_type__ok__string); ATF_ADD_TEST_CASE(tcs, to_type__empty); ATF_ADD_TEST_CASE(tcs, to_type__invalid__bool); ATF_ADD_TEST_CASE(tcs, to_type__invalid__numerical); }