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/operations.ipp" 30 31 #include <iostream> 32 #include <set> 33 #include <string> 34 #include <vector> 35 36 #include <atf-c++.hpp> 37 38 #include "utils/text/exceptions.hpp" 39 40 namespace text = utils::text; 41 42 43 namespace { 44 45 46 /// Tests text::refill() on an input string with a range of widths. 47 /// 48 /// \param expected The expected refilled paragraph. 49 /// \param input The input paragraph to be refilled. 50 /// \param first_width The first width to validate. 51 /// \param last_width The last width to validate (inclusive). 52 static void 53 refill_test(const char* expected, const char* input, 54 const std::size_t first_width, const std::size_t last_width) 55 { 56 for (std::size_t width = first_width; width <= last_width; ++width) { 57 const std::vector< std::string > lines = text::split(expected, '\n'); 58 std::cout << "Breaking at width " << width << '\n'; 59 ATF_REQUIRE_EQ(expected, text::refill_as_string(input, width)); 60 ATF_REQUIRE(lines == text::refill(input, width)); 61 } 62 } 63 64 65 } // anonymous namespace 66 67 68 ATF_TEST_CASE_WITHOUT_HEAD(escape_xml__empty); 69 ATF_TEST_CASE_BODY(escape_xml__empty) 70 { 71 ATF_REQUIRE_EQ("", text::escape_xml("")); 72 } 73 74 75 ATF_TEST_CASE_WITHOUT_HEAD(escape_xml__no_escaping); 76 ATF_TEST_CASE_BODY(escape_xml__no_escaping) 77 { 78 ATF_REQUIRE_EQ("a", text::escape_xml("a")); 79 ATF_REQUIRE_EQ("Some text!", text::escape_xml("Some text!")); 80 ATF_REQUIRE_EQ("\n\t\r", text::escape_xml("\n\t\r")); 81 } 82 83 84 ATF_TEST_CASE_WITHOUT_HEAD(escape_xml__some_escaping); 85 ATF_TEST_CASE_BODY(escape_xml__some_escaping) 86 { 87 ATF_REQUIRE_EQ("'", text::escape_xml("'")); 88 89 ATF_REQUIRE_EQ("foo "bar& <tag> yay' baz", 90 text::escape_xml("foo \"bar& <tag> yay' baz")); 91 92 ATF_REQUIRE_EQ(""&<>'", text::escape_xml("\"&<>'")); 93 ATF_REQUIRE_EQ("&&&", text::escape_xml("&&&")); 94 ATF_REQUIRE_EQ("&#8;&#11;", text::escape_xml("\b\v")); 95 ATF_REQUIRE_EQ("\t&#127;BAR&", text::escape_xml("\t\x7f""BAR&")); 96 } 97 98 99 ATF_TEST_CASE_WITHOUT_HEAD(quote__empty); 100 ATF_TEST_CASE_BODY(quote__empty) 101 { 102 ATF_REQUIRE_EQ("''", text::quote("", '\'')); 103 ATF_REQUIRE_EQ("##", text::quote("", '#')); 104 } 105 106 107 ATF_TEST_CASE_WITHOUT_HEAD(quote__no_escaping); 108 ATF_TEST_CASE_BODY(quote__no_escaping) 109 { 110 ATF_REQUIRE_EQ("'Some text\"'", text::quote("Some text\"", '\'')); 111 ATF_REQUIRE_EQ("#Another'string#", text::quote("Another'string", '#')); 112 } 113 114 115 ATF_TEST_CASE_WITHOUT_HEAD(quote__some_escaping); 116 ATF_TEST_CASE_BODY(quote__some_escaping) 117 { 118 ATF_REQUIRE_EQ("'Some\\'text'", text::quote("Some'text", '\'')); 119 ATF_REQUIRE_EQ("#Some\\#text#", text::quote("Some#text", '#')); 120 121 ATF_REQUIRE_EQ("'More than one\\' quote\\''", 122 text::quote("More than one' quote'", '\'')); 123 ATF_REQUIRE_EQ("'Multiple quotes \\'\\'\\' together'", 124 text::quote("Multiple quotes ''' together", '\'')); 125 126 ATF_REQUIRE_EQ("'\\'escape at the beginning'", 127 text::quote("'escape at the beginning", '\'')); 128 ATF_REQUIRE_EQ("'escape at the end\\''", 129 text::quote("escape at the end'", '\'')); 130 } 131 132 133 ATF_TEST_CASE_WITHOUT_HEAD(refill__empty); 134 ATF_TEST_CASE_BODY(refill__empty) 135 { 136 ATF_REQUIRE_EQ(1, text::refill("", 0).size()); 137 ATF_REQUIRE(text::refill("", 0)[0].empty()); 138 ATF_REQUIRE_EQ("", text::refill_as_string("", 0)); 139 140 ATF_REQUIRE_EQ(1, text::refill("", 10).size()); 141 ATF_REQUIRE(text::refill("", 10)[0].empty()); 142 ATF_REQUIRE_EQ("", text::refill_as_string("", 10)); 143 } 144 145 146 ATF_TEST_CASE_WITHOUT_HEAD(refill__no_changes); 147 ATF_TEST_CASE_BODY(refill__no_changes) 148 { 149 std::vector< std::string > exp_lines; 150 exp_lines.push_back("foo bar\nbaz"); 151 152 ATF_REQUIRE(exp_lines == text::refill("foo bar\nbaz", 12)); 153 ATF_REQUIRE_EQ("foo bar\nbaz", text::refill_as_string("foo bar\nbaz", 12)); 154 155 ATF_REQUIRE(exp_lines == text::refill("foo bar\nbaz", 18)); 156 ATF_REQUIRE_EQ("foo bar\nbaz", text::refill_as_string("foo bar\nbaz", 80)); 157 } 158 159 160 ATF_TEST_CASE_WITHOUT_HEAD(refill__break_one); 161 ATF_TEST_CASE_BODY(refill__break_one) 162 { 163 refill_test("only break the\nfirst line", "only break the first line", 164 14, 19); 165 } 166 167 168 ATF_TEST_CASE_WITHOUT_HEAD(refill__break_one__not_first_word); 169 ATF_TEST_CASE_BODY(refill__break_one__not_first_word) 170 { 171 refill_test("first-long-word\nother\nwords", "first-long-word other words", 172 6, 10); 173 refill_test("first-long-word\nother words", "first-long-word other words", 174 11, 20); 175 refill_test("first-long-word other\nwords", "first-long-word other words", 176 21, 26); 177 refill_test("first-long-word other words", "first-long-word other words", 178 27, 28); 179 } 180 181 182 ATF_TEST_CASE_WITHOUT_HEAD(refill__break_many); 183 ATF_TEST_CASE_BODY(refill__break_many) 184 { 185 refill_test("this is a long\nparagraph to be\nsplit into\npieces", 186 "this is a long paragraph to be split into pieces", 187 15, 15); 188 } 189 190 191 ATF_TEST_CASE_WITHOUT_HEAD(refill__cannot_break); 192 ATF_TEST_CASE_BODY(refill__cannot_break) 193 { 194 refill_test("this-is-a-long-string", "this-is-a-long-string", 5, 5); 195 196 refill_test("this is\na-string-with-long-words", 197 "this is a-string-with-long-words", 10, 10); 198 } 199 200 201 ATF_TEST_CASE_WITHOUT_HEAD(refill__preserve_whitespace); 202 ATF_TEST_CASE_BODY(refill__preserve_whitespace) 203 { 204 refill_test("foo bar baz ", "foo bar baz ", 80, 80); 205 refill_test("foo \n bar", "foo bar", 5, 5); 206 207 std::vector< std::string > exp_lines; 208 exp_lines.push_back("foo \n"); 209 exp_lines.push_back(" bar"); 210 ATF_REQUIRE(exp_lines == text::refill("foo \n bar", 5)); 211 ATF_REQUIRE_EQ("foo \n\n bar", text::refill_as_string("foo \n bar", 5)); 212 } 213 214 215 ATF_TEST_CASE_WITHOUT_HEAD(join__empty); 216 ATF_TEST_CASE_BODY(join__empty) 217 { 218 std::vector< std::string > lines; 219 ATF_REQUIRE_EQ("", text::join(lines, " ")); 220 } 221 222 223 ATF_TEST_CASE_WITHOUT_HEAD(join__one); 224 ATF_TEST_CASE_BODY(join__one) 225 { 226 std::vector< std::string > lines; 227 lines.push_back("first line"); 228 ATF_REQUIRE_EQ("first line", text::join(lines, "*")); 229 } 230 231 232 ATF_TEST_CASE_WITHOUT_HEAD(join__several); 233 ATF_TEST_CASE_BODY(join__several) 234 { 235 std::vector< std::string > lines; 236 lines.push_back("first abc"); 237 lines.push_back("second"); 238 lines.push_back("and last line"); 239 ATF_REQUIRE_EQ("first abc second and last line", text::join(lines, " ")); 240 ATF_REQUIRE_EQ("first abc***second***and last line", 241 text::join(lines, "***")); 242 } 243 244 245 ATF_TEST_CASE_WITHOUT_HEAD(join__unordered); 246 ATF_TEST_CASE_BODY(join__unordered) 247 { 248 std::set< std::string > lines; 249 lines.insert("first"); 250 lines.insert("second"); 251 const std::string joined = text::join(lines, " "); 252 ATF_REQUIRE(joined == "first second" || joined == "second first"); 253 } 254 255 256 ATF_TEST_CASE_WITHOUT_HEAD(split__empty); 257 ATF_TEST_CASE_BODY(split__empty) 258 { 259 std::vector< std::string > words = text::split("", ' '); 260 std::vector< std::string > exp_words; 261 ATF_REQUIRE(exp_words == words); 262 } 263 264 265 ATF_TEST_CASE_WITHOUT_HEAD(split__one); 266 ATF_TEST_CASE_BODY(split__one) 267 { 268 std::vector< std::string > words = text::split("foo", ' '); 269 std::vector< std::string > exp_words; 270 exp_words.push_back("foo"); 271 ATF_REQUIRE(exp_words == words); 272 } 273 274 275 ATF_TEST_CASE_WITHOUT_HEAD(split__several__simple); 276 ATF_TEST_CASE_BODY(split__several__simple) 277 { 278 std::vector< std::string > words = text::split("foo bar baz", ' '); 279 std::vector< std::string > exp_words; 280 exp_words.push_back("foo"); 281 exp_words.push_back("bar"); 282 exp_words.push_back("baz"); 283 ATF_REQUIRE(exp_words == words); 284 } 285 286 287 ATF_TEST_CASE_WITHOUT_HEAD(split__several__delimiters); 288 ATF_TEST_CASE_BODY(split__several__delimiters) 289 { 290 std::vector< std::string > words = text::split("XfooXXbarXXXbazXX", 'X'); 291 std::vector< std::string > exp_words; 292 exp_words.push_back(""); 293 exp_words.push_back("foo"); 294 exp_words.push_back(""); 295 exp_words.push_back("bar"); 296 exp_words.push_back(""); 297 exp_words.push_back(""); 298 exp_words.push_back("baz"); 299 exp_words.push_back(""); 300 exp_words.push_back(""); 301 ATF_REQUIRE(exp_words == words); 302 } 303 304 305 ATF_TEST_CASE_WITHOUT_HEAD(replace_all__empty); 306 ATF_TEST_CASE_BODY(replace_all__empty) 307 { 308 ATF_REQUIRE_EQ("", text::replace_all("", "search", "replacement")); 309 } 310 311 312 ATF_TEST_CASE_WITHOUT_HEAD(replace_all__none); 313 ATF_TEST_CASE_BODY(replace_all__none) 314 { 315 ATF_REQUIRE_EQ("string without matches", 316 text::replace_all("string without matches", 317 "WITHOUT", "replacement")); 318 } 319 320 321 ATF_TEST_CASE_WITHOUT_HEAD(replace_all__one); 322 ATF_TEST_CASE_BODY(replace_all__one) 323 { 324 ATF_REQUIRE_EQ("string replacement matches", 325 text::replace_all("string without matches", 326 "without", "replacement")); 327 } 328 329 330 ATF_TEST_CASE_WITHOUT_HEAD(replace_all__several); 331 ATF_TEST_CASE_BODY(replace_all__several) 332 { 333 ATF_REQUIRE_EQ("OO fOO bar OOf baz OO", 334 text::replace_all("oo foo bar oof baz oo", 335 "oo", "OO")); 336 } 337 338 339 ATF_TEST_CASE_WITHOUT_HEAD(to_type__ok__bool); 340 ATF_TEST_CASE_BODY(to_type__ok__bool) 341 { 342 ATF_REQUIRE( text::to_type< bool >("true")); 343 ATF_REQUIRE(!text::to_type< bool >("false")); 344 } 345 346 347 ATF_TEST_CASE_WITHOUT_HEAD(to_type__ok__numerical); 348 ATF_TEST_CASE_BODY(to_type__ok__numerical) 349 { 350 ATF_REQUIRE_EQ(12, text::to_type< int >("12")); 351 ATF_REQUIRE_EQ(18745, text::to_type< int >("18745")); 352 ATF_REQUIRE_EQ(-12345, text::to_type< int >("-12345")); 353 354 ATF_REQUIRE_EQ(12.0, text::to_type< double >("12")); 355 ATF_REQUIRE_EQ(12.5, text::to_type< double >("12.5")); 356 } 357 358 359 ATF_TEST_CASE_WITHOUT_HEAD(to_type__ok__string); 360 ATF_TEST_CASE_BODY(to_type__ok__string) 361 { 362 // While this seems redundant, having this particular specialization that 363 // does nothing allows callers to delegate work to to_type without worrying 364 // about the particular type being converted. 365 ATF_REQUIRE_EQ("", text::to_type< std::string >("")); 366 ATF_REQUIRE_EQ(" abcd ", text::to_type< std::string >(" abcd ")); 367 } 368 369 370 ATF_TEST_CASE_WITHOUT_HEAD(to_type__empty); 371 ATF_TEST_CASE_BODY(to_type__empty) 372 { 373 ATF_REQUIRE_THROW(text::value_error, text::to_type< int >("")); 374 } 375 376 377 ATF_TEST_CASE_WITHOUT_HEAD(to_type__invalid__bool); 378 ATF_TEST_CASE_BODY(to_type__invalid__bool) 379 { 380 ATF_REQUIRE_THROW(text::value_error, text::to_type< bool >("")); 381 ATF_REQUIRE_THROW(text::value_error, text::to_type< bool >("true ")); 382 ATF_REQUIRE_THROW(text::value_error, text::to_type< bool >("foo")); 383 } 384 385 386 ATF_TEST_CASE_WITHOUT_HEAD(to_type__invalid__numerical); 387 ATF_TEST_CASE_BODY(to_type__invalid__numerical) 388 { 389 ATF_REQUIRE_THROW(text::value_error, text::to_type< int >(" 3")); 390 ATF_REQUIRE_THROW(text::value_error, text::to_type< int >("3 ")); 391 ATF_REQUIRE_THROW(text::value_error, text::to_type< int >("3a")); 392 ATF_REQUIRE_THROW(text::value_error, text::to_type< int >("a3")); 393 } 394 395 396 ATF_INIT_TEST_CASES(tcs) 397 { 398 ATF_ADD_TEST_CASE(tcs, escape_xml__empty); 399 ATF_ADD_TEST_CASE(tcs, escape_xml__no_escaping); 400 ATF_ADD_TEST_CASE(tcs, escape_xml__some_escaping); 401 402 ATF_ADD_TEST_CASE(tcs, quote__empty); 403 ATF_ADD_TEST_CASE(tcs, quote__no_escaping); 404 ATF_ADD_TEST_CASE(tcs, quote__some_escaping); 405 406 ATF_ADD_TEST_CASE(tcs, refill__empty); 407 ATF_ADD_TEST_CASE(tcs, refill__no_changes); 408 ATF_ADD_TEST_CASE(tcs, refill__break_one); 409 ATF_ADD_TEST_CASE(tcs, refill__break_one__not_first_word); 410 ATF_ADD_TEST_CASE(tcs, refill__break_many); 411 ATF_ADD_TEST_CASE(tcs, refill__cannot_break); 412 ATF_ADD_TEST_CASE(tcs, refill__preserve_whitespace); 413 414 ATF_ADD_TEST_CASE(tcs, join__empty); 415 ATF_ADD_TEST_CASE(tcs, join__one); 416 ATF_ADD_TEST_CASE(tcs, join__several); 417 ATF_ADD_TEST_CASE(tcs, join__unordered); 418 419 ATF_ADD_TEST_CASE(tcs, split__empty); 420 ATF_ADD_TEST_CASE(tcs, split__one); 421 ATF_ADD_TEST_CASE(tcs, split__several__simple); 422 ATF_ADD_TEST_CASE(tcs, split__several__delimiters); 423 424 ATF_ADD_TEST_CASE(tcs, replace_all__empty); 425 ATF_ADD_TEST_CASE(tcs, replace_all__none); 426 ATF_ADD_TEST_CASE(tcs, replace_all__one); 427 ATF_ADD_TEST_CASE(tcs, replace_all__several); 428 429 ATF_ADD_TEST_CASE(tcs, to_type__ok__bool); 430 ATF_ADD_TEST_CASE(tcs, to_type__ok__numerical); 431 ATF_ADD_TEST_CASE(tcs, to_type__ok__string); 432 ATF_ADD_TEST_CASE(tcs, to_type__empty); 433 ATF_ADD_TEST_CASE(tcs, to_type__invalid__bool); 434 ATF_ADD_TEST_CASE(tcs, to_type__invalid__numerical); 435 } 436