• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 ()__anon99c93bf90111::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,Tuple)431 TEST(StrJoin, Tuple) {
432   EXPECT_EQ("", absl::StrJoin(std::make_tuple(), "-"));
433   EXPECT_EQ("hello", absl::StrJoin(std::make_tuple("hello"), "-"));
434 
435   int x(10);
436   std::string y("hello");
437   double z(3.14);
438   EXPECT_EQ("10-hello-3.14", absl::StrJoin(std::make_tuple(x, y, z), "-"));
439 
440   // Faster! Faster!!
441   EXPECT_EQ("10-hello-3.14",
442             absl::StrJoin(std::make_tuple(x, std::cref(y), z), "-"));
443 
444   struct TestFormatter {
445     char buffer[128];
446     void operator()(std::string* out, int v) {
447       snprintf(buffer, sizeof(buffer), "%#.8x", v);
448       out->append(buffer);
449     }
450     void operator()(std::string* out, double v) {
451       snprintf(buffer, sizeof(buffer), "%#.0f", v);
452       out->append(buffer);
453     }
454     void operator()(std::string* out, const std::string& v) {
455       snprintf(buffer, sizeof(buffer), "%.4s", v.c_str());
456       out->append(buffer);
457     }
458   };
459   EXPECT_EQ("0x0000000a-hell-3.",
460             absl::StrJoin(std::make_tuple(x, y, z), "-", TestFormatter()));
461   EXPECT_EQ(
462       "0x0000000a-hell-3.",
463       absl::StrJoin(std::make_tuple(x, std::cref(y), z), "-", TestFormatter()));
464   EXPECT_EQ("0x0000000a-hell-3.",
465             absl::StrJoin(std::make_tuple(&x, &y, &z), "-",
466                           absl::DereferenceFormatter(TestFormatter())));
467   EXPECT_EQ("0x0000000a-hell-3.",
468             absl::StrJoin(std::make_tuple(absl::make_unique<int>(x),
469                                           absl::make_unique<std::string>(y),
470                                           absl::make_unique<double>(z)),
471                           "-", absl::DereferenceFormatter(TestFormatter())));
472   EXPECT_EQ("0x0000000a-hell-3.",
473             absl::StrJoin(std::make_tuple(absl::make_unique<int>(x), &y, &z),
474                           "-", absl::DereferenceFormatter(TestFormatter())));
475 }
476 
477 // A minimal value type for `StrJoin` inputs.
478 // Used to ensure we do not excessively require more a specific type, such as a
479 // `string_view`.
480 //
481 // Anything that can be  `data()` and `size()` is OK.
482 class TestValue {
483  public:
TestValue(const char * data,size_t size)484   TestValue(const char* data, size_t size) : data_(data), size_(size) {}
data() const485   const char* data() const { return data_; }
size() const486   size_t size() const { return size_; }
487 
488  private:
489   const char* data_;
490   size_t size_;
491 };
492 
493 // A minimal C++20 forward iterator, used to test that we do not impose
494 // excessive requirements on StrJoin inputs.
495 //
496 // The 2 main differences between pre-C++20 LegacyForwardIterator and the
497 // C++20 ForwardIterator are:
498 // 1. `operator->` is not required in C++20.
499 // 2. `operator*` result does not need to be an lvalue (a reference).
500 //
501 // The `operator->` requirement was removed on page 17 in:
502 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1037r0.pdf
503 //
504 // See the `[iterator.requirements]` section of the C++ standard.
505 //
506 // The value type is a template parameter so that we can test the behaviour
507 // of `StrJoin` specializations, e.g. the NoFormatter specialization for
508 // `string_view`.
509 template <typename ValueT>
510 class TestIterator {
511  public:
512   using iterator_category = std::forward_iterator_tag;
513   using value_type = ValueT;
514   using pointer = void;
515   using reference = const value_type&;
516   using difference_type = int;
517 
518   // `data` must outlive the result.
begin(const std::vector<absl::string_view> & data)519   static TestIterator begin(const std::vector<absl::string_view>& data) {
520     return TestIterator(&data, 0);
521   }
522 
end(const std::vector<absl::string_view> & data)523   static TestIterator end(const std::vector<absl::string_view>& data) {
524     return TestIterator(nullptr, data.size());
525   }
526 
operator ==(const TestIterator & other) const527   bool operator==(const TestIterator& other) const {
528     return pos_ == other.pos_;
529   }
operator !=(const TestIterator & other) const530   bool operator!=(const TestIterator& other) const {
531     return pos_ != other.pos_;
532   }
533 
534   // This deliberately returns a `prvalue`.
535   // The requirement to return a reference was removed in C++20.
operator *() const536   value_type operator*() const {
537     return ValueT((*data_)[pos_].data(), (*data_)[pos_].size());
538   }
539 
540   // `operator->()` is deliberately omitted.
541   // The requirement to provide it was removed in C++20.
542 
operator ++()543   TestIterator& operator++() {
544     ++pos_;
545     return *this;
546   }
547 
operator ++(int)548   TestIterator operator++(int) {
549     TestIterator result = *this;
550     ++(*this);
551     return result;
552   }
553 
operator --()554   TestIterator& operator--() {
555     --pos_;
556     return *this;
557   }
558 
operator --(int)559   TestIterator operator--(int) {
560     TestIterator result = *this;
561     --(*this);
562     return result;
563   }
564 
565  private:
TestIterator(const std::vector<absl::string_view> * data,size_t pos)566   TestIterator(const std::vector<absl::string_view>* data, size_t pos)
567       : data_(data), pos_(pos) {}
568 
569   const std::vector<absl::string_view>* data_;
570   size_t pos_;
571 };
572 
573 template <typename ValueT>
574 class TestIteratorRange {
575  public:
576   // `data` must be non-null and must outlive the result.
TestIteratorRange(const std::vector<absl::string_view> & data)577   explicit TestIteratorRange(const std::vector<absl::string_view>& data)
578       : begin_(TestIterator<ValueT>::begin(data)),
579         end_(TestIterator<ValueT>::end(data)) {}
580 
begin() const581   const TestIterator<ValueT>& begin() const { return begin_; }
end() const582   const TestIterator<ValueT>& end() const { return end_; }
583 
584  private:
585   TestIterator<ValueT> begin_;
586   TestIterator<ValueT> end_;
587 };
588 
TEST(StrJoin,TestIteratorRequirementsNoFormatter)589 TEST(StrJoin, TestIteratorRequirementsNoFormatter) {
590   const std::vector<absl::string_view> a = {"a", "b", "c"};
591 
592   // When the value type is string-like (`std::string` or `string_view`),
593   // the NoFormatter template specialization is used internally.
594   EXPECT_EQ("a-b-c",
595             absl::StrJoin(TestIteratorRange<absl::string_view>(a), "-"));
596 }
597 
TEST(StrJoin,TestIteratorRequirementsCustomFormatter)598 TEST(StrJoin, TestIteratorRequirementsCustomFormatter) {
599   const std::vector<absl::string_view> a = {"a", "b", "c"};
600   EXPECT_EQ("a-b-c",
601             absl::StrJoin(TestIteratorRange<TestValue>(a), "-",
602                           [](std::string* out, const TestValue& value) {
603                             absl::StrAppend(
604                                 out,
605                                 absl::string_view(value.data(), value.size()));
606                           }));
607 }
608 
609 }  // namespace
610