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
TEST(StrJoin,APIExamples)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
TEST(StrJoin,CustomFormatter)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
TEST(AlphaNumFormatter,FormatterAPI)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().
TEST(AlphaNumFormatter,VectorOfBool)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
TEST(AlphaNumFormatter,AlphaNum)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 };
operator <<(std::ostream & os,const StreamableType & t)256 inline std::ostream& operator<<(std::ostream& os, const StreamableType& t) {
257 os << "Streamable:" << t.contents;
258 return os;
259 }
260
TEST(StreamFormatter,FormatterAPI)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>
operator ()__anonf0c5f2db0111::TestingParenFormatter282 void operator()(std::string* s, const T& t) {
283 absl::StrAppend(s, "(", t, ")");
284 }
285 };
286
TEST(PairFormatter,FormatterAPI)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
TEST(DereferenceFormatter,FormatterAPI)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 //
TEST(StrJoin,PublicAPIOverloads)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
TEST(StrJoin,Array)383 TEST(StrJoin, Array) {
384 const absl::string_view a[] = {"a", "b", "c"};
385 EXPECT_EQ("a-b-c", absl::StrJoin(a, "-"));
386 }
387
TEST(StrJoin,InitializerList)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
TEST(StrJoin,StringViewInitializerList)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
TEST(StrJoin,Tuple)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:
TestValue(const char * data,size_t size)520 TestValue(const char* data, size_t size) : data_(data), size_(size) {}
data() const521 const char* data() const { return data_; }
size() const522 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.
begin(const std::vector<absl::string_view> & data)555 static TestIterator begin(const std::vector<absl::string_view>& data) {
556 return TestIterator(&data, 0);
557 }
558
end(const std::vector<absl::string_view> & data)559 static TestIterator end(const std::vector<absl::string_view>& data) {
560 return TestIterator(nullptr, data.size());
561 }
562
operator ==(const TestIterator & other) const563 bool operator==(const TestIterator& other) const {
564 return pos_ == other.pos_;
565 }
operator !=(const TestIterator & other) const566 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.
operator *() const572 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
operator ++()579 TestIterator& operator++() {
580 ++pos_;
581 return *this;
582 }
583
operator ++(int)584 TestIterator operator++(int) {
585 TestIterator result = *this;
586 ++(*this);
587 return result;
588 }
589
operator --()590 TestIterator& operator--() {
591 --pos_;
592 return *this;
593 }
594
operator --(int)595 TestIterator operator--(int) {
596 TestIterator result = *this;
597 --(*this);
598 return result;
599 }
600
601 private:
TestIterator(const std::vector<absl::string_view> * data,size_t pos)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.
TestIteratorRange(const std::vector<absl::string_view> & data)613 explicit TestIteratorRange(const std::vector<absl::string_view>& data)
614 : begin_(TestIterator<ValueT>::begin(data)),
615 end_(TestIterator<ValueT>::end(data)) {}
616
begin() const617 const TestIterator<ValueT>& begin() const { return begin_; }
end() const618 const TestIterator<ValueT>& end() const { return end_; }
619
620 private:
621 TestIterator<ValueT> begin_;
622 TestIterator<ValueT> end_;
623 };
624
TEST(StrJoin,TestIteratorRequirementsNoFormatter)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
TEST(StrJoin,TestIteratorRequirementsCustomFormatter)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
646