str_join_test.cc (18313B)
1 // Copyright 2017 The Abseil Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Unit tests for all join.h functions 16 17 #include "absl/strings/str_join.h" 18 19 #include <cstddef> 20 #include <cstdint> 21 #include <cstdio> 22 #include <functional> 23 #include <initializer_list> 24 #include <iterator> 25 #include <map> 26 #include <memory> 27 #include <ostream> 28 #include <string> 29 #include <tuple> 30 #include <utility> 31 #include <vector> 32 33 #include "gtest/gtest.h" 34 #include "absl/base/macros.h" 35 #include "absl/memory/memory.h" 36 #include "absl/strings/str_cat.h" 37 #include "absl/strings/str_split.h" 38 #include "absl/strings/string_view.h" 39 40 namespace { 41 42 TEST(StrJoin, APIExamples) { 43 { 44 // Collection of strings 45 std::vector<std::string> v = {"foo", "bar", "baz"}; 46 EXPECT_EQ("foo-bar-baz", absl::StrJoin(v, "-")); 47 } 48 49 { 50 // Collection of absl::string_view 51 std::vector<absl::string_view> v = {"foo", "bar", "baz"}; 52 EXPECT_EQ("foo-bar-baz", absl::StrJoin(v, "-")); 53 } 54 55 { 56 // Collection of const char* 57 std::vector<const char*> v = {"foo", "bar", "baz"}; 58 EXPECT_EQ("foo-bar-baz", absl::StrJoin(v, "-")); 59 } 60 61 { 62 // Collection of non-const char* 63 std::string a = "foo", b = "bar", c = "baz"; 64 std::vector<char*> v = {&a[0], &b[0], &c[0]}; 65 EXPECT_EQ("foo-bar-baz", absl::StrJoin(v, "-")); 66 } 67 68 { 69 // Collection of ints 70 std::vector<int> v = {1, 2, 3, -4}; 71 EXPECT_EQ("1-2-3--4", absl::StrJoin(v, "-")); 72 } 73 74 { 75 // Literals passed as a std::initializer_list 76 std::string s = absl::StrJoin({"a", "b", "c"}, "-"); 77 EXPECT_EQ("a-b-c", s); 78 } 79 { 80 // Join a std::tuple<T...>. 81 std::string s = absl::StrJoin(std::make_tuple(123, "abc", 0.456), "-"); 82 EXPECT_EQ("123-abc-0.456", s); 83 } 84 85 { 86 // Collection of unique_ptrs 87 std::vector<std::unique_ptr<int>> v; 88 v.emplace_back(new int(1)); 89 v.emplace_back(new int(2)); 90 v.emplace_back(new int(3)); 91 EXPECT_EQ("1-2-3", absl::StrJoin(v, "-")); 92 } 93 94 { 95 // Array of ints 96 const int a[] = {1, 2, 3, -4}; 97 EXPECT_EQ("1-2-3--4", absl::StrJoin(a, a + ABSL_ARRAYSIZE(a), "-")); 98 } 99 100 { 101 // Collection of pointers 102 int x = 1, y = 2, z = 3; 103 std::vector<int*> v = {&x, &y, &z}; 104 EXPECT_EQ("1-2-3", absl::StrJoin(v, "-")); 105 } 106 107 { 108 // Collection of pointers to pointers 109 int x = 1, y = 2, z = 3; 110 int *px = &x, *py = &y, *pz = &z; 111 std::vector<int**> v = {&px, &py, &pz}; 112 EXPECT_EQ("1-2-3", absl::StrJoin(v, "-")); 113 } 114 115 { 116 // Collection of pointers to std::string 117 std::string a("a"), b("b"); 118 std::vector<std::string*> v = {&a, &b}; 119 EXPECT_EQ("a-b", absl::StrJoin(v, "-")); 120 } 121 122 { 123 // A std::map, which is a collection of std::pair<>s. 124 std::map<std::string, int> m = {{"a", 1}, {"b", 2}, {"c", 3}}; 125 EXPECT_EQ("a=1,b=2,c=3", absl::StrJoin(m, ",", absl::PairFormatter("="))); 126 } 127 128 { 129 // Shows absl::StrSplit and absl::StrJoin working together. This example is 130 // equivalent to s/=/-/g. 131 const std::string s = "a=b=c=d"; 132 EXPECT_EQ("a-b-c-d", absl::StrJoin(absl::StrSplit(s, "="), "-")); 133 } 134 135 // 136 // A few examples of edge cases 137 // 138 139 { 140 // Empty range yields an empty string. 141 std::vector<std::string> v; 142 EXPECT_EQ("", absl::StrJoin(v, "-")); 143 } 144 145 { 146 // A range of 1 element gives a string with that element but no 147 // separator. 148 std::vector<std::string> v = {"foo"}; 149 EXPECT_EQ("foo", absl::StrJoin(v, "-")); 150 } 151 152 { 153 // A range with a single empty string element 154 std::vector<std::string> v = {""}; 155 EXPECT_EQ("", absl::StrJoin(v, "-")); 156 } 157 158 { 159 // A range with 2 elements, one of which is an empty string 160 std::vector<std::string> v = {"a", ""}; 161 EXPECT_EQ("a-", absl::StrJoin(v, "-")); 162 } 163 164 { 165 // A range with 2 empty elements. 166 std::vector<std::string> v = {"", ""}; 167 EXPECT_EQ("-", absl::StrJoin(v, "-")); 168 } 169 170 { 171 // A std::vector of bool. 172 std::vector<bool> v = {true, false, true}; 173 EXPECT_EQ("1-0-1", absl::StrJoin(v, "-")); 174 } 175 } 176 177 TEST(StrJoin, CustomFormatter) { 178 std::vector<std::string> v{"One", "Two", "Three"}; 179 { 180 std::string joined = 181 absl::StrJoin(v, "", [](std::string* out, const std::string& in) { 182 absl::StrAppend(out, "(", in, ")"); 183 }); 184 EXPECT_EQ("(One)(Two)(Three)", joined); 185 } 186 { 187 class ImmovableFormatter { 188 public: 189 void operator()(std::string* out, const std::string& in) { 190 absl::StrAppend(out, "(", in, ")"); 191 } 192 ImmovableFormatter() {} 193 ImmovableFormatter(const ImmovableFormatter&) = delete; 194 }; 195 EXPECT_EQ("(One)(Two)(Three)", absl::StrJoin(v, "", ImmovableFormatter())); 196 } 197 { 198 class OverloadedFormatter { 199 public: 200 void operator()(std::string* out, const std::string& in) { 201 absl::StrAppend(out, "(", in, ")"); 202 } 203 void operator()(std::string* out, const std::string& in) const { 204 absl::StrAppend(out, "[", in, "]"); 205 } 206 }; 207 EXPECT_EQ("(One)(Two)(Three)", absl::StrJoin(v, "", OverloadedFormatter())); 208 const OverloadedFormatter fmt = {}; 209 EXPECT_EQ("[One][Two][Three]", absl::StrJoin(v, "", fmt)); 210 } 211 } 212 213 // 214 // Tests the Formatters 215 // 216 217 TEST(AlphaNumFormatter, FormatterAPI) { 218 // Not an exhaustive test. See strings/strcat_test.h for the exhaustive test 219 // of what AlphaNum can convert. 220 auto f = absl::AlphaNumFormatter(); 221 std::string s; 222 f(&s, "Testing: "); 223 f(&s, static_cast<int>(1)); 224 f(&s, static_cast<int16_t>(2)); 225 f(&s, static_cast<int64_t>(3)); 226 f(&s, static_cast<float>(4)); 227 f(&s, static_cast<double>(5)); 228 f(&s, static_cast<unsigned>(6)); 229 f(&s, static_cast<size_t>(7)); 230 f(&s, absl::string_view(" OK")); 231 EXPECT_EQ("Testing: 1234567 OK", s); 232 } 233 234 // Make sure people who are mistakenly using std::vector<bool> even though 235 // they're not memory-constrained can use absl::AlphaNumFormatter(). 236 TEST(AlphaNumFormatter, VectorOfBool) { 237 auto f = absl::AlphaNumFormatter(); 238 std::string s; 239 std::vector<bool> v = {true, false, true}; 240 f(&s, *v.cbegin()); 241 f(&s, *v.begin()); 242 f(&s, v[1]); 243 EXPECT_EQ("110", s); 244 } 245 246 TEST(AlphaNumFormatter, AlphaNum) { 247 auto f = absl::AlphaNumFormatter(); 248 std::string s; 249 f(&s, absl::AlphaNum("hello")); 250 EXPECT_EQ("hello", s); 251 } 252 253 struct StreamableType { 254 std::string contents; 255 }; 256 inline std::ostream& operator<<(std::ostream& os, const StreamableType& t) { 257 os << "Streamable:" << t.contents; 258 return os; 259 } 260 261 TEST(StreamFormatter, FormatterAPI) { 262 auto f = absl::StreamFormatter(); 263 std::string s; 264 f(&s, "Testing: "); 265 f(&s, static_cast<int>(1)); 266 f(&s, static_cast<int16_t>(2)); 267 f(&s, static_cast<int64_t>(3)); 268 f(&s, static_cast<float>(4)); 269 f(&s, static_cast<double>(5)); 270 f(&s, static_cast<unsigned>(6)); 271 f(&s, static_cast<size_t>(7)); 272 f(&s, absl::string_view(" OK ")); 273 StreamableType streamable = {"object"}; 274 f(&s, streamable); 275 EXPECT_EQ("Testing: 1234567 OK Streamable:object", s); 276 } 277 278 // A dummy formatter that wraps each element in parens. Used in some tests 279 // below. 280 struct TestingParenFormatter { 281 template <typename T> 282 void operator()(std::string* s, const T& t) { 283 absl::StrAppend(s, "(", t, ")"); 284 } 285 }; 286 287 TEST(PairFormatter, FormatterAPI) { 288 { 289 // Tests default PairFormatter(sep) that uses AlphaNumFormatter for the 290 // 'first' and 'second' members. 291 const auto f = absl::PairFormatter("="); 292 std::string s; 293 f(&s, std::make_pair("a", "b")); 294 f(&s, std::make_pair(1, 2)); 295 EXPECT_EQ("a=b1=2", s); 296 } 297 298 { 299 // Tests using a custom formatter for the 'first' and 'second' members. 300 auto f = absl::PairFormatter(TestingParenFormatter(), "=", 301 TestingParenFormatter()); 302 std::string s; 303 f(&s, std::make_pair("a", "b")); 304 f(&s, std::make_pair(1, 2)); 305 EXPECT_EQ("(a)=(b)(1)=(2)", s); 306 } 307 } 308 309 TEST(DereferenceFormatter, FormatterAPI) { 310 { 311 // Tests wrapping the default AlphaNumFormatter. 312 const absl::strings_internal::DereferenceFormatterImpl< 313 absl::strings_internal::AlphaNumFormatterImpl> 314 f; 315 int x = 1, y = 2, z = 3; 316 std::string s; 317 f(&s, &x); 318 f(&s, &y); 319 f(&s, &z); 320 EXPECT_EQ("123", s); 321 } 322 323 { 324 // Tests wrapping std::string's default formatter. 325 absl::strings_internal::DereferenceFormatterImpl< 326 absl::strings_internal::DefaultFormatter<std::string>::Type> 327 f; 328 329 std::string x = "x"; 330 std::string y = "y"; 331 std::string z = "z"; 332 std::string s; 333 f(&s, &x); 334 f(&s, &y); 335 f(&s, &z); 336 EXPECT_EQ(s, "xyz"); 337 } 338 339 { 340 // Tests wrapping a custom formatter. 341 auto f = absl::DereferenceFormatter(TestingParenFormatter()); 342 int x = 1, y = 2, z = 3; 343 std::string s; 344 f(&s, &x); 345 f(&s, &y); 346 f(&s, &z); 347 EXPECT_EQ("(1)(2)(3)", s); 348 } 349 350 { 351 absl::strings_internal::DereferenceFormatterImpl< 352 absl::strings_internal::AlphaNumFormatterImpl> 353 f; 354 auto x = std::unique_ptr<int>(new int(1)); 355 auto y = std::unique_ptr<int>(new int(2)); 356 auto z = std::unique_ptr<int>(new int(3)); 357 std::string s; 358 f(&s, x); 359 f(&s, y); 360 f(&s, z); 361 EXPECT_EQ("123", s); 362 } 363 } 364 365 // 366 // Tests the interfaces for the 4 public Join function overloads. The semantics 367 // of the algorithm is covered in the above APIExamples test. 368 // 369 TEST(StrJoin, PublicAPIOverloads) { 370 std::vector<std::string> v = {"a", "b", "c"}; 371 372 // Iterators + formatter 373 EXPECT_EQ("a-b-c", 374 absl::StrJoin(v.begin(), v.end(), "-", absl::AlphaNumFormatter())); 375 // Range + formatter 376 EXPECT_EQ("a-b-c", absl::StrJoin(v, "-", absl::AlphaNumFormatter())); 377 // Iterators, no formatter 378 EXPECT_EQ("a-b-c", absl::StrJoin(v.begin(), v.end(), "-")); 379 // Range, no formatter 380 EXPECT_EQ("a-b-c", absl::StrJoin(v, "-")); 381 } 382 383 TEST(StrJoin, Array) { 384 const absl::string_view a[] = {"a", "b", "c"}; 385 EXPECT_EQ("a-b-c", absl::StrJoin(a, "-")); 386 } 387 388 TEST(StrJoin, InitializerList) { 389 { EXPECT_EQ("a-b-c", absl::StrJoin({"a", "b", "c"}, "-")); } 390 391 { 392 auto a = {"a", "b", "c"}; 393 EXPECT_EQ("a-b-c", absl::StrJoin(a, "-")); 394 } 395 396 { 397 std::initializer_list<const char*> a = {"a", "b", "c"}; 398 EXPECT_EQ("a-b-c", absl::StrJoin(a, "-")); 399 } 400 401 { 402 std::initializer_list<std::string> a = {"a", "b", "c"}; 403 EXPECT_EQ("a-b-c", absl::StrJoin(a, "-")); 404 } 405 406 { 407 std::initializer_list<absl::string_view> a = {"a", "b", "c"}; 408 EXPECT_EQ("a-b-c", absl::StrJoin(a, "-")); 409 } 410 411 { 412 // Tests initializer_list with a non-default formatter 413 auto a = {"a", "b", "c"}; 414 TestingParenFormatter f; 415 EXPECT_EQ("(a)-(b)-(c)", absl::StrJoin(a, "-", f)); 416 } 417 418 { 419 // initializer_list of ints 420 EXPECT_EQ("1-2-3", absl::StrJoin({1, 2, 3}, "-")); 421 } 422 423 { 424 // Tests initializer_list of ints with a non-default formatter 425 auto a = {1, 2, 3}; 426 TestingParenFormatter f; 427 EXPECT_EQ("(1)-(2)-(3)", absl::StrJoin(a, "-", f)); 428 } 429 } 430 431 TEST(StrJoin, StringViewInitializerList) { 432 { 433 // Tests initializer_list of string_views 434 std::string b = "b"; 435 EXPECT_EQ("a-b-c", absl::StrJoin({"a", b, "c"}, "-")); 436 } 437 { 438 // Tests initializer_list of string_views with a non-default formatter 439 TestingParenFormatter f; 440 std::string b = "b"; 441 EXPECT_EQ("(a)-(b)-(c)", absl::StrJoin({"a", b, "c"}, "-", f)); 442 } 443 444 class NoCopy { 445 public: 446 explicit NoCopy(absl::string_view view) : view_(view) {} 447 NoCopy(const NoCopy&) = delete; 448 operator absl::string_view() { return view_; } // NOLINT 449 private: 450 absl::string_view view_; 451 }; 452 { 453 // Tests initializer_list of string_views preferred over initializer_list<T> 454 // for T that is implicitly convertible to string_view 455 EXPECT_EQ("a-b-c", 456 absl::StrJoin({NoCopy("a"), NoCopy("b"), NoCopy("c")}, "-")); 457 } 458 { 459 // Tests initializer_list of string_views preferred over initializer_list<T> 460 // for T that is implicitly convertible to string_view 461 TestingParenFormatter f; 462 EXPECT_EQ("(a)-(b)-(c)", 463 absl::StrJoin({NoCopy("a"), NoCopy("b"), NoCopy("c")}, "-", f)); 464 } 465 } 466 467 TEST(StrJoin, Tuple) { 468 EXPECT_EQ("", absl::StrJoin(std::make_tuple(), "-")); 469 EXPECT_EQ("hello", absl::StrJoin(std::make_tuple("hello"), "-")); 470 471 int x(10); 472 std::string y("hello"); 473 double z(3.14); 474 EXPECT_EQ("10-hello-3.14", absl::StrJoin(std::make_tuple(x, y, z), "-")); 475 476 // Faster! Faster!! 477 EXPECT_EQ("10-hello-3.14", 478 absl::StrJoin(std::make_tuple(x, std::cref(y), z), "-")); 479 480 struct TestFormatter { 481 char buffer[128]; 482 void operator()(std::string* out, int v) { 483 snprintf(buffer, sizeof(buffer), "%#.8x", v); 484 out->append(buffer); 485 } 486 void operator()(std::string* out, double v) { 487 snprintf(buffer, sizeof(buffer), "%#.0f", v); 488 out->append(buffer); 489 } 490 void operator()(std::string* out, const std::string& v) { 491 snprintf(buffer, sizeof(buffer), "%.4s", v.c_str()); 492 out->append(buffer); 493 } 494 }; 495 EXPECT_EQ("0x0000000a-hell-3.", 496 absl::StrJoin(std::make_tuple(x, y, z), "-", TestFormatter())); 497 EXPECT_EQ( 498 "0x0000000a-hell-3.", 499 absl::StrJoin(std::make_tuple(x, std::cref(y), z), "-", TestFormatter())); 500 EXPECT_EQ("0x0000000a-hell-3.", 501 absl::StrJoin(std::make_tuple(&x, &y, &z), "-", 502 absl::DereferenceFormatter(TestFormatter()))); 503 EXPECT_EQ("0x0000000a-hell-3.", 504 absl::StrJoin(std::make_tuple(absl::make_unique<int>(x), 505 absl::make_unique<std::string>(y), 506 absl::make_unique<double>(z)), 507 "-", absl::DereferenceFormatter(TestFormatter()))); 508 EXPECT_EQ("0x0000000a-hell-3.", 509 absl::StrJoin(std::make_tuple(absl::make_unique<int>(x), &y, &z), 510 "-", absl::DereferenceFormatter(TestFormatter()))); 511 } 512 513 // A minimal value type for `StrJoin` inputs. 514 // Used to ensure we do not excessively require more a specific type, such as a 515 // `string_view`. 516 // 517 // Anything that can be `data()` and `size()` is OK. 518 class TestValue { 519 public: 520 TestValue(const char* data, size_t size) : data_(data), size_(size) {} 521 const char* data() const { return data_; } 522 size_t size() const { return size_; } 523 524 private: 525 const char* data_; 526 size_t size_; 527 }; 528 529 // A minimal C++20 forward iterator, used to test that we do not impose 530 // excessive requirements on StrJoin inputs. 531 // 532 // The 2 main differences between pre-C++20 LegacyForwardIterator and the 533 // C++20 ForwardIterator are: 534 // 1. `operator->` is not required in C++20. 535 // 2. `operator*` result does not need to be an lvalue (a reference). 536 // 537 // The `operator->` requirement was removed on page 17 in: 538 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1037r0.pdf 539 // 540 // See the `[iterator.requirements]` section of the C++ standard. 541 // 542 // The value type is a template parameter so that we can test the behaviour 543 // of `StrJoin` specializations, e.g. the NoFormatter specialization for 544 // `string_view`. 545 template <typename ValueT> 546 class TestIterator { 547 public: 548 using iterator_category = std::forward_iterator_tag; 549 using value_type = ValueT; 550 using pointer = void; 551 using reference = const value_type&; 552 using difference_type = int; 553 554 // `data` must outlive the result. 555 static TestIterator begin(const std::vector<absl::string_view>& data) { 556 return TestIterator(&data, 0); 557 } 558 559 static TestIterator end(const std::vector<absl::string_view>& data) { 560 return TestIterator(nullptr, data.size()); 561 } 562 563 bool operator==(const TestIterator& other) const { 564 return pos_ == other.pos_; 565 } 566 bool operator!=(const TestIterator& other) const { 567 return pos_ != other.pos_; 568 } 569 570 // This deliberately returns a `prvalue`. 571 // The requirement to return a reference was removed in C++20. 572 value_type operator*() const { 573 return ValueT((*data_)[pos_].data(), (*data_)[pos_].size()); 574 } 575 576 // `operator->()` is deliberately omitted. 577 // The requirement to provide it was removed in C++20. 578 579 TestIterator& operator++() { 580 ++pos_; 581 return *this; 582 } 583 584 TestIterator operator++(int) { 585 TestIterator result = *this; 586 ++(*this); 587 return result; 588 } 589 590 TestIterator& operator--() { 591 --pos_; 592 return *this; 593 } 594 595 TestIterator operator--(int) { 596 TestIterator result = *this; 597 --(*this); 598 return result; 599 } 600 601 private: 602 TestIterator(const std::vector<absl::string_view>* data, size_t pos) 603 : data_(data), pos_(pos) {} 604 605 const std::vector<absl::string_view>* data_; 606 size_t pos_; 607 }; 608 609 template <typename ValueT> 610 class TestIteratorRange { 611 public: 612 // `data` must be non-null and must outlive the result. 613 explicit TestIteratorRange(const std::vector<absl::string_view>& data) 614 : begin_(TestIterator<ValueT>::begin(data)), 615 end_(TestIterator<ValueT>::end(data)) {} 616 617 const TestIterator<ValueT>& begin() const { return begin_; } 618 const TestIterator<ValueT>& end() const { return end_; } 619 620 private: 621 TestIterator<ValueT> begin_; 622 TestIterator<ValueT> end_; 623 }; 624 625 TEST(StrJoin, TestIteratorRequirementsNoFormatter) { 626 const std::vector<absl::string_view> a = {"a", "b", "c"}; 627 628 // When the value type is string-like (`std::string` or `string_view`), 629 // the NoFormatter template specialization is used internally. 630 EXPECT_EQ("a-b-c", 631 absl::StrJoin(TestIteratorRange<absl::string_view>(a), "-")); 632 } 633 634 TEST(StrJoin, TestIteratorRequirementsCustomFormatter) { 635 const std::vector<absl::string_view> a = {"a", "b", "c"}; 636 EXPECT_EQ("a-b-c", 637 absl::StrJoin(TestIteratorRange<TestValue>(a), "-", 638 [](std::string* out, const TestValue& value) { 639 absl::StrAppend( 640 out, 641 absl::string_view(value.data(), value.size())); 642 })); 643 } 644 645 } // namespace