Austin Schuh | 36244a1 | 2019-09-21 17:52:38 -0700 | [diff] [blame^] | 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 <map> |
| 25 | #include <memory> |
| 26 | #include <ostream> |
| 27 | #include <tuple> |
| 28 | #include <type_traits> |
| 29 | #include <vector> |
| 30 | |
| 31 | #include "gtest/gtest.h" |
| 32 | #include "absl/base/macros.h" |
| 33 | #include "absl/memory/memory.h" |
| 34 | #include "absl/strings/str_cat.h" |
| 35 | #include "absl/strings/str_split.h" |
| 36 | |
| 37 | namespace { |
| 38 | |
| 39 | TEST(StrJoin, APIExamples) { |
| 40 | { |
| 41 | // Collection of strings |
| 42 | std::vector<std::string> v = {"foo", "bar", "baz"}; |
| 43 | EXPECT_EQ("foo-bar-baz", absl::StrJoin(v, "-")); |
| 44 | } |
| 45 | |
| 46 | { |
| 47 | // Collection of absl::string_view |
| 48 | std::vector<absl::string_view> v = {"foo", "bar", "baz"}; |
| 49 | EXPECT_EQ("foo-bar-baz", absl::StrJoin(v, "-")); |
| 50 | } |
| 51 | |
| 52 | { |
| 53 | // Collection of const char* |
| 54 | std::vector<const char*> v = {"foo", "bar", "baz"}; |
| 55 | EXPECT_EQ("foo-bar-baz", absl::StrJoin(v, "-")); |
| 56 | } |
| 57 | |
| 58 | { |
| 59 | // Collection of non-const char* |
| 60 | std::string a = "foo", b = "bar", c = "baz"; |
| 61 | std::vector<char*> v = {&a[0], &b[0], &c[0]}; |
| 62 | EXPECT_EQ("foo-bar-baz", absl::StrJoin(v, "-")); |
| 63 | } |
| 64 | |
| 65 | { |
| 66 | // Collection of ints |
| 67 | std::vector<int> v = {1, 2, 3, -4}; |
| 68 | EXPECT_EQ("1-2-3--4", absl::StrJoin(v, "-")); |
| 69 | } |
| 70 | |
| 71 | { |
| 72 | // Literals passed as a std::initializer_list |
| 73 | std::string s = absl::StrJoin({"a", "b", "c"}, "-"); |
| 74 | EXPECT_EQ("a-b-c", s); |
| 75 | } |
| 76 | { |
| 77 | // Join a std::tuple<T...>. |
| 78 | std::string s = absl::StrJoin(std::make_tuple(123, "abc", 0.456), "-"); |
| 79 | EXPECT_EQ("123-abc-0.456", s); |
| 80 | } |
| 81 | |
| 82 | { |
| 83 | // Collection of unique_ptrs |
| 84 | std::vector<std::unique_ptr<int>> v; |
| 85 | v.emplace_back(new int(1)); |
| 86 | v.emplace_back(new int(2)); |
| 87 | v.emplace_back(new int(3)); |
| 88 | EXPECT_EQ("1-2-3", absl::StrJoin(v, "-")); |
| 89 | } |
| 90 | |
| 91 | { |
| 92 | // Array of ints |
| 93 | const int a[] = {1, 2, 3, -4}; |
| 94 | EXPECT_EQ("1-2-3--4", absl::StrJoin(a, a + ABSL_ARRAYSIZE(a), "-")); |
| 95 | } |
| 96 | |
| 97 | { |
| 98 | // Collection of pointers |
| 99 | int x = 1, y = 2, z = 3; |
| 100 | std::vector<int*> v = {&x, &y, &z}; |
| 101 | EXPECT_EQ("1-2-3", absl::StrJoin(v, "-")); |
| 102 | } |
| 103 | |
| 104 | { |
| 105 | // Collection of pointers to pointers |
| 106 | int x = 1, y = 2, z = 3; |
| 107 | int *px = &x, *py = &y, *pz = &z; |
| 108 | std::vector<int**> v = {&px, &py, &pz}; |
| 109 | EXPECT_EQ("1-2-3", absl::StrJoin(v, "-")); |
| 110 | } |
| 111 | |
| 112 | { |
| 113 | // Collection of pointers to std::string |
| 114 | std::string a("a"), b("b"); |
| 115 | std::vector<std::string*> v = {&a, &b}; |
| 116 | EXPECT_EQ("a-b", absl::StrJoin(v, "-")); |
| 117 | } |
| 118 | |
| 119 | { |
| 120 | // A std::map, which is a collection of std::pair<>s. |
| 121 | std::map<std::string, int> m = {{"a", 1}, {"b", 2}, {"c", 3}}; |
| 122 | EXPECT_EQ("a=1,b=2,c=3", absl::StrJoin(m, ",", absl::PairFormatter("="))); |
| 123 | } |
| 124 | |
| 125 | { |
| 126 | // Shows absl::StrSplit and absl::StrJoin working together. This example is |
| 127 | // equivalent to s/=/-/g. |
| 128 | const std::string s = "a=b=c=d"; |
| 129 | EXPECT_EQ("a-b-c-d", absl::StrJoin(absl::StrSplit(s, "="), "-")); |
| 130 | } |
| 131 | |
| 132 | // |
| 133 | // A few examples of edge cases |
| 134 | // |
| 135 | |
| 136 | { |
| 137 | // Empty range yields an empty std::string. |
| 138 | std::vector<std::string> v; |
| 139 | EXPECT_EQ("", absl::StrJoin(v, "-")); |
| 140 | } |
| 141 | |
| 142 | { |
| 143 | // A range of 1 element gives a std::string with that element but no |
| 144 | // separator. |
| 145 | std::vector<std::string> v = {"foo"}; |
| 146 | EXPECT_EQ("foo", absl::StrJoin(v, "-")); |
| 147 | } |
| 148 | |
| 149 | { |
| 150 | // A range with a single empty std::string element |
| 151 | std::vector<std::string> v = {""}; |
| 152 | EXPECT_EQ("", absl::StrJoin(v, "-")); |
| 153 | } |
| 154 | |
| 155 | { |
| 156 | // A range with 2 elements, one of which is an empty std::string |
| 157 | std::vector<std::string> v = {"a", ""}; |
| 158 | EXPECT_EQ("a-", absl::StrJoin(v, "-")); |
| 159 | } |
| 160 | |
| 161 | { |
| 162 | // A range with 2 empty elements. |
| 163 | std::vector<std::string> v = {"", ""}; |
| 164 | EXPECT_EQ("-", absl::StrJoin(v, "-")); |
| 165 | } |
| 166 | |
| 167 | { |
| 168 | // A std::vector of bool. |
| 169 | std::vector<bool> v = {true, false, true}; |
| 170 | EXPECT_EQ("1-0-1", absl::StrJoin(v, "-")); |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | TEST(StrJoin, CustomFormatter) { |
| 175 | std::vector<std::string> v{"One", "Two", "Three"}; |
| 176 | { |
| 177 | std::string joined = |
| 178 | absl::StrJoin(v, "", [](std::string* out, const std::string& in) { |
| 179 | absl::StrAppend(out, "(", in, ")"); |
| 180 | }); |
| 181 | EXPECT_EQ("(One)(Two)(Three)", joined); |
| 182 | } |
| 183 | { |
| 184 | class ImmovableFormatter { |
| 185 | public: |
| 186 | void operator()(std::string* out, const std::string& in) { |
| 187 | absl::StrAppend(out, "(", in, ")"); |
| 188 | } |
| 189 | ImmovableFormatter() {} |
| 190 | ImmovableFormatter(const ImmovableFormatter&) = delete; |
| 191 | }; |
| 192 | EXPECT_EQ("(One)(Two)(Three)", absl::StrJoin(v, "", ImmovableFormatter())); |
| 193 | } |
| 194 | { |
| 195 | class OverloadedFormatter { |
| 196 | public: |
| 197 | void operator()(std::string* out, const std::string& in) { |
| 198 | absl::StrAppend(out, "(", in, ")"); |
| 199 | } |
| 200 | void operator()(std::string* out, const std::string& in) const { |
| 201 | absl::StrAppend(out, "[", in, "]"); |
| 202 | } |
| 203 | }; |
| 204 | EXPECT_EQ("(One)(Two)(Three)", absl::StrJoin(v, "", OverloadedFormatter())); |
| 205 | const OverloadedFormatter fmt = {}; |
| 206 | EXPECT_EQ("[One][Two][Three]", absl::StrJoin(v, "", fmt)); |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | // |
| 211 | // Tests the Formatters |
| 212 | // |
| 213 | |
| 214 | TEST(AlphaNumFormatter, FormatterAPI) { |
| 215 | // Not an exhaustive test. See strings/strcat_test.h for the exhaustive test |
| 216 | // of what AlphaNum can convert. |
| 217 | auto f = absl::AlphaNumFormatter(); |
| 218 | std::string s; |
| 219 | f(&s, "Testing: "); |
| 220 | f(&s, static_cast<int>(1)); |
| 221 | f(&s, static_cast<int16_t>(2)); |
| 222 | f(&s, static_cast<int64_t>(3)); |
| 223 | f(&s, static_cast<float>(4)); |
| 224 | f(&s, static_cast<double>(5)); |
| 225 | f(&s, static_cast<unsigned>(6)); |
| 226 | f(&s, static_cast<size_t>(7)); |
| 227 | f(&s, absl::string_view(" OK")); |
| 228 | EXPECT_EQ("Testing: 1234567 OK", s); |
| 229 | } |
| 230 | |
| 231 | // Make sure people who are mistakenly using std::vector<bool> even though |
| 232 | // they're not memory-constrained can use absl::AlphaNumFormatter(). |
| 233 | TEST(AlphaNumFormatter, VectorOfBool) { |
| 234 | auto f = absl::AlphaNumFormatter(); |
| 235 | std::string s; |
| 236 | std::vector<bool> v = {true, false, true}; |
| 237 | f(&s, *v.cbegin()); |
| 238 | f(&s, *v.begin()); |
| 239 | f(&s, v[1]); |
| 240 | EXPECT_EQ("110", s); |
| 241 | } |
| 242 | |
| 243 | TEST(AlphaNumFormatter, AlphaNum) { |
| 244 | auto f = absl::AlphaNumFormatter(); |
| 245 | std::string s; |
| 246 | f(&s, absl::AlphaNum("hello")); |
| 247 | EXPECT_EQ("hello", s); |
| 248 | } |
| 249 | |
| 250 | struct StreamableType { |
| 251 | std::string contents; |
| 252 | }; |
| 253 | inline std::ostream& operator<<(std::ostream& os, const StreamableType& t) { |
| 254 | os << "Streamable:" << t.contents; |
| 255 | return os; |
| 256 | } |
| 257 | |
| 258 | TEST(StreamFormatter, FormatterAPI) { |
| 259 | auto f = absl::StreamFormatter(); |
| 260 | std::string s; |
| 261 | f(&s, "Testing: "); |
| 262 | f(&s, static_cast<int>(1)); |
| 263 | f(&s, static_cast<int16_t>(2)); |
| 264 | f(&s, static_cast<int64_t>(3)); |
| 265 | f(&s, static_cast<float>(4)); |
| 266 | f(&s, static_cast<double>(5)); |
| 267 | f(&s, static_cast<unsigned>(6)); |
| 268 | f(&s, static_cast<size_t>(7)); |
| 269 | f(&s, absl::string_view(" OK ")); |
| 270 | StreamableType streamable = {"object"}; |
| 271 | f(&s, streamable); |
| 272 | EXPECT_EQ("Testing: 1234567 OK Streamable:object", s); |
| 273 | } |
| 274 | |
| 275 | // A dummy formatter that wraps each element in parens. Used in some tests |
| 276 | // below. |
| 277 | struct TestingParenFormatter { |
| 278 | template <typename T> |
| 279 | void operator()(std::string* s, const T& t) { |
| 280 | absl::StrAppend(s, "(", t, ")"); |
| 281 | } |
| 282 | }; |
| 283 | |
| 284 | TEST(PairFormatter, FormatterAPI) { |
| 285 | { |
| 286 | // Tests default PairFormatter(sep) that uses AlphaNumFormatter for the |
| 287 | // 'first' and 'second' members. |
| 288 | const auto f = absl::PairFormatter("="); |
| 289 | std::string s; |
| 290 | f(&s, std::make_pair("a", "b")); |
| 291 | f(&s, std::make_pair(1, 2)); |
| 292 | EXPECT_EQ("a=b1=2", s); |
| 293 | } |
| 294 | |
| 295 | { |
| 296 | // Tests using a custom formatter for the 'first' and 'second' members. |
| 297 | auto f = absl::PairFormatter(TestingParenFormatter(), "=", |
| 298 | TestingParenFormatter()); |
| 299 | std::string s; |
| 300 | f(&s, std::make_pair("a", "b")); |
| 301 | f(&s, std::make_pair(1, 2)); |
| 302 | EXPECT_EQ("(a)=(b)(1)=(2)", s); |
| 303 | } |
| 304 | } |
| 305 | |
| 306 | TEST(DereferenceFormatter, FormatterAPI) { |
| 307 | { |
| 308 | // Tests wrapping the default AlphaNumFormatter. |
| 309 | const absl::strings_internal::DereferenceFormatterImpl< |
| 310 | absl::strings_internal::AlphaNumFormatterImpl> |
| 311 | f; |
| 312 | int x = 1, y = 2, z = 3; |
| 313 | std::string s; |
| 314 | f(&s, &x); |
| 315 | f(&s, &y); |
| 316 | f(&s, &z); |
| 317 | EXPECT_EQ("123", s); |
| 318 | } |
| 319 | |
| 320 | { |
| 321 | // Tests wrapping std::string's default formatter. |
| 322 | absl::strings_internal::DereferenceFormatterImpl< |
| 323 | absl::strings_internal::DefaultFormatter<std::string>::Type> |
| 324 | f; |
| 325 | |
| 326 | std::string x = "x"; |
| 327 | std::string y = "y"; |
| 328 | std::string z = "z"; |
| 329 | std::string s; |
| 330 | f(&s, &x); |
| 331 | f(&s, &y); |
| 332 | f(&s, &z); |
| 333 | EXPECT_EQ(s, "xyz"); |
| 334 | } |
| 335 | |
| 336 | { |
| 337 | // Tests wrapping a custom formatter. |
| 338 | auto f = absl::DereferenceFormatter(TestingParenFormatter()); |
| 339 | int x = 1, y = 2, z = 3; |
| 340 | std::string s; |
| 341 | f(&s, &x); |
| 342 | f(&s, &y); |
| 343 | f(&s, &z); |
| 344 | EXPECT_EQ("(1)(2)(3)", s); |
| 345 | } |
| 346 | |
| 347 | { |
| 348 | absl::strings_internal::DereferenceFormatterImpl< |
| 349 | absl::strings_internal::AlphaNumFormatterImpl> |
| 350 | f; |
| 351 | auto x = std::unique_ptr<int>(new int(1)); |
| 352 | auto y = std::unique_ptr<int>(new int(2)); |
| 353 | auto z = std::unique_ptr<int>(new int(3)); |
| 354 | std::string s; |
| 355 | f(&s, x); |
| 356 | f(&s, y); |
| 357 | f(&s, z); |
| 358 | EXPECT_EQ("123", s); |
| 359 | } |
| 360 | } |
| 361 | |
| 362 | // |
| 363 | // Tests the interfaces for the 4 public Join function overloads. The semantics |
| 364 | // of the algorithm is covered in the above APIExamples test. |
| 365 | // |
| 366 | TEST(StrJoin, PublicAPIOverloads) { |
| 367 | std::vector<std::string> v = {"a", "b", "c"}; |
| 368 | |
| 369 | // Iterators + formatter |
| 370 | EXPECT_EQ("a-b-c", |
| 371 | absl::StrJoin(v.begin(), v.end(), "-", absl::AlphaNumFormatter())); |
| 372 | // Range + formatter |
| 373 | EXPECT_EQ("a-b-c", absl::StrJoin(v, "-", absl::AlphaNumFormatter())); |
| 374 | // Iterators, no formatter |
| 375 | EXPECT_EQ("a-b-c", absl::StrJoin(v.begin(), v.end(), "-")); |
| 376 | // Range, no formatter |
| 377 | EXPECT_EQ("a-b-c", absl::StrJoin(v, "-")); |
| 378 | } |
| 379 | |
| 380 | TEST(StrJoin, Array) { |
| 381 | const absl::string_view a[] = {"a", "b", "c"}; |
| 382 | EXPECT_EQ("a-b-c", absl::StrJoin(a, "-")); |
| 383 | } |
| 384 | |
| 385 | TEST(StrJoin, InitializerList) { |
| 386 | { EXPECT_EQ("a-b-c", absl::StrJoin({"a", "b", "c"}, "-")); } |
| 387 | |
| 388 | { |
| 389 | auto a = {"a", "b", "c"}; |
| 390 | EXPECT_EQ("a-b-c", absl::StrJoin(a, "-")); |
| 391 | } |
| 392 | |
| 393 | { |
| 394 | std::initializer_list<const char*> a = {"a", "b", "c"}; |
| 395 | EXPECT_EQ("a-b-c", absl::StrJoin(a, "-")); |
| 396 | } |
| 397 | |
| 398 | { |
| 399 | std::initializer_list<std::string> a = {"a", "b", "c"}; |
| 400 | EXPECT_EQ("a-b-c", absl::StrJoin(a, "-")); |
| 401 | } |
| 402 | |
| 403 | { |
| 404 | std::initializer_list<absl::string_view> a = {"a", "b", "c"}; |
| 405 | EXPECT_EQ("a-b-c", absl::StrJoin(a, "-")); |
| 406 | } |
| 407 | |
| 408 | { |
| 409 | // Tests initializer_list with a non-default formatter |
| 410 | auto a = {"a", "b", "c"}; |
| 411 | TestingParenFormatter f; |
| 412 | EXPECT_EQ("(a)-(b)-(c)", absl::StrJoin(a, "-", f)); |
| 413 | } |
| 414 | |
| 415 | { |
| 416 | // initializer_list of ints |
| 417 | EXPECT_EQ("1-2-3", absl::StrJoin({1, 2, 3}, "-")); |
| 418 | } |
| 419 | |
| 420 | { |
| 421 | // Tests initializer_list of ints with a non-default formatter |
| 422 | auto a = {1, 2, 3}; |
| 423 | TestingParenFormatter f; |
| 424 | EXPECT_EQ("(1)-(2)-(3)", absl::StrJoin(a, "-", f)); |
| 425 | } |
| 426 | } |
| 427 | |
| 428 | TEST(StrJoin, Tuple) { |
| 429 | EXPECT_EQ("", absl::StrJoin(std::make_tuple(), "-")); |
| 430 | EXPECT_EQ("hello", absl::StrJoin(std::make_tuple("hello"), "-")); |
| 431 | |
| 432 | int x(10); |
| 433 | std::string y("hello"); |
| 434 | double z(3.14); |
| 435 | EXPECT_EQ("10-hello-3.14", absl::StrJoin(std::make_tuple(x, y, z), "-")); |
| 436 | |
| 437 | // Faster! Faster!! |
| 438 | EXPECT_EQ("10-hello-3.14", |
| 439 | absl::StrJoin(std::make_tuple(x, std::cref(y), z), "-")); |
| 440 | |
| 441 | struct TestFormatter { |
| 442 | char buffer[128]; |
| 443 | void operator()(std::string* out, int v) { |
| 444 | snprintf(buffer, sizeof(buffer), "%#.8x", v); |
| 445 | out->append(buffer); |
| 446 | } |
| 447 | void operator()(std::string* out, double v) { |
| 448 | snprintf(buffer, sizeof(buffer), "%#.0f", v); |
| 449 | out->append(buffer); |
| 450 | } |
| 451 | void operator()(std::string* out, const std::string& v) { |
| 452 | snprintf(buffer, sizeof(buffer), "%.4s", v.c_str()); |
| 453 | out->append(buffer); |
| 454 | } |
| 455 | }; |
| 456 | EXPECT_EQ("0x0000000a-hell-3.", |
| 457 | absl::StrJoin(std::make_tuple(x, y, z), "-", TestFormatter())); |
| 458 | EXPECT_EQ( |
| 459 | "0x0000000a-hell-3.", |
| 460 | absl::StrJoin(std::make_tuple(x, std::cref(y), z), "-", TestFormatter())); |
| 461 | EXPECT_EQ("0x0000000a-hell-3.", |
| 462 | absl::StrJoin(std::make_tuple(&x, &y, &z), "-", |
| 463 | absl::DereferenceFormatter(TestFormatter()))); |
| 464 | EXPECT_EQ("0x0000000a-hell-3.", |
| 465 | absl::StrJoin(std::make_tuple(absl::make_unique<int>(x), |
| 466 | absl::make_unique<std::string>(y), |
| 467 | absl::make_unique<double>(z)), |
| 468 | "-", absl::DereferenceFormatter(TestFormatter()))); |
| 469 | EXPECT_EQ("0x0000000a-hell-3.", |
| 470 | absl::StrJoin(std::make_tuple(absl::make_unique<int>(x), &y, &z), |
| 471 | "-", absl::DereferenceFormatter(TestFormatter()))); |
| 472 | } |
| 473 | |
| 474 | } // namespace |