xref: /aosp_15_r20/external/abseil-cpp/absl/strings/str_join_test.cc (revision 9356374a3709195abf420251b3e825997ff56c0f)
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