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