#include "absl/strings/str_join.h"
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <functional>
#include <initializer_list>
#include <iterator>
#include <map>
#include <memory>
#include <ostream>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include "gtest/gtest.h"
#include "absl/base/macros.h"
#include "absl/memory/memory.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
namespace {
TEST(StrJoin, APIExamples) {
{
std::vector<std::string> v = {"foo", "bar", "baz"};
EXPECT_EQ("foo-bar-baz", absl::StrJoin(v, "-"));
}
{
std::vector<absl::string_view> v = {"foo", "bar", "baz"};
EXPECT_EQ("foo-bar-baz", absl::StrJoin(v, "-"));
}
{
std::vector<const char*> v = {"foo", "bar", "baz"};
EXPECT_EQ("foo-bar-baz", absl::StrJoin(v, "-"));
}
{
std::string a = "foo", b = "bar", c = "baz";
std::vector<char*> v = {&a[0], &b[0], &c[0]};
EXPECT_EQ("foo-bar-baz", absl::StrJoin(v, "-"));
}
{
std::vector<int> v = {1, 2, 3, -4};
EXPECT_EQ("1-2-3--4", absl::StrJoin(v, "-"));
}
{
std::string s = absl::StrJoin({"a", "b", "c"}, "-");
EXPECT_EQ("a-b-c", s);
}
{
std::string s = absl::StrJoin(std::make_tuple(123, "abc", 0.456), "-");
EXPECT_EQ("123-abc-0.456", s);
}
{
std::vector<std::unique_ptr<int>> v;
v.emplace_back(new int(1));
v.emplace_back(new int(2));
v.emplace_back(new int(3));
EXPECT_EQ("1-2-3", absl::StrJoin(v, "-"));
}
{
const int a[] = {1, 2, 3, -4};
EXPECT_EQ("1-2-3--4", absl::StrJoin(a, a + ABSL_ARRAYSIZE(a), "-"));
}
{
int x = 1, y = 2, z = 3;
std::vector<int*> v = {&x, &y, &z};
EXPECT_EQ("1-2-3", absl::StrJoin(v, "-"));
}
{
int x = 1, y = 2, z = 3;
int *px = &x, *py = &y, *pz = &z;
std::vector<int**> v = {&px, &py, &pz};
EXPECT_EQ("1-2-3", absl::StrJoin(v, "-"));
}
{
std::string a("a"), b("b");
std::vector<std::string*> v = {&a, &b};
EXPECT_EQ("a-b", absl::StrJoin(v, "-"));
}
{
std::map<std::string, int> m = {{"a", 1}, {"b", 2}, {"c", 3}};
EXPECT_EQ("a=1,b=2,c=3", absl::StrJoin(m, ",", absl::PairFormatter("=")));
}
{
const std::string s = "a=b=c=d";
EXPECT_EQ("a-b-c-d", absl::StrJoin(absl::StrSplit(s, "="), "-"));
}
{
std::vector<std::string> v;
EXPECT_EQ("", absl::StrJoin(v, "-"));
}
{
std::vector<std::string> v = {"foo"};
EXPECT_EQ("foo", absl::StrJoin(v, "-"));
}
{
std::vector<std::string> v = {""};
EXPECT_EQ("", absl::StrJoin(v, "-"));
}
{
std::vector<std::string> v = {"a", ""};
EXPECT_EQ("a-", absl::StrJoin(v, "-"));
}
{
std::vector<std::string> v = {"", ""};
EXPECT_EQ("-", absl::StrJoin(v, "-"));
}
{
std::vector<bool> v = {true, false, true};
EXPECT_EQ("1-0-1", absl::StrJoin(v, "-"));
}
}
TEST(StrJoin, CustomFormatter) {
std::vector<std::string> v{"One", "Two", "Three"};
{
std::string joined =
absl::StrJoin(v, "", [](std::string* out, const std::string& in) {
absl::StrAppend(out, "(", in, ")");
});
EXPECT_EQ("(One)(Two)(Three)", joined);
}
{
class ImmovableFormatter {
public:
void operator()(std::string* out, const std::string& in) {
absl::StrAppend(out, "(", in, ")");
}
ImmovableFormatter() {}
ImmovableFormatter(const ImmovableFormatter&) = delete;
};
EXPECT_EQ("(One)(Two)(Three)", absl::StrJoin(v, "", ImmovableFormatter()));
}
{
class OverloadedFormatter {
public:
void operator()(std::string* out, const std::string& in) {
absl::StrAppend(out, "(", in, ")");
}
void operator()(std::string* out, const std::string& in) const {
absl::StrAppend(out, "[", in, "]");
}
};
EXPECT_EQ("(One)(Two)(Three)", absl::StrJoin(v, "", OverloadedFormatter()));
const OverloadedFormatter fmt = {};
EXPECT_EQ("[One][Two][Three]", absl::StrJoin(v, "", fmt));
}
}
TEST(AlphaNumFormatter, FormatterAPI) {
auto f = absl::AlphaNumFormatter();
std::string s;
f(&s, "Testing: ");
f(&s, static_cast<int>(1));
f(&s, static_cast<int16_t>(2));
f(&s, static_cast<int64_t>(3));
f(&s, static_cast<float>(4));
f(&s, static_cast<double>(5));
f(&s, static_cast<unsigned>(6));
f(&s, static_cast<size_t>(7));
f(&s, absl::string_view(" OK"));
EXPECT_EQ("Testing: 1234567 OK", s);
}
TEST(AlphaNumFormatter, VectorOfBool) {
auto f = absl::AlphaNumFormatter();
std::string s;
std::vector<bool> v = {true, false, true};
f(&s, *v.cbegin());
f(&s, *v.begin());
f(&s, v[1]);
EXPECT_EQ("110", s);
}
TEST(AlphaNumFormatter, AlphaNum) {
auto f = absl::AlphaNumFormatter();
std::string s;
f(&s, absl::AlphaNum("hello"));
EXPECT_EQ("hello", s);
}
struct StreamableType {
std::string contents;
};
inline std::ostream& operator<<(std::ostream& os, const StreamableType& t) {
os << "Streamable:" << t.contents;
return os;
}
TEST(StreamFormatter, FormatterAPI) {
auto f = absl::StreamFormatter();
std::string s;
f(&s, "Testing: ");
f(&s, static_cast<int>(1));
f(&s, static_cast<int16_t>(2));
f(&s, static_cast<int64_t>(3));
f(&s, static_cast<float>(4));
f(&s, static_cast<double>(5));
f(&s, static_cast<unsigned>(6));
f(&s, static_cast<size_t>(7));
f(&s, absl::string_view(" OK "));
StreamableType streamable = {"object"};
f(&s, streamable);
EXPECT_EQ("Testing: 1234567 OK Streamable:object", s);
}
struct TestingParenFormatter {
template <typename T>
void operator()(std::string* s, const T& t) {
absl::StrAppend(s, "(", t, ")");
}
};
TEST(PairFormatter, FormatterAPI) {
{
const auto f = absl::PairFormatter("=");
std::string s;
f(&s, std::make_pair("a", "b"));
f(&s, std::make_pair(1, 2));
EXPECT_EQ("a=b1=2", s);
}
{
auto f = absl::PairFormatter(TestingParenFormatter(), "=",
TestingParenFormatter());
std::string s;
f(&s, std::make_pair("a", "b"));
f(&s, std::make_pair(1, 2));
EXPECT_EQ("(a)=(b)(1)=(2)", s);
}
}
TEST(DereferenceFormatter, FormatterAPI) {
{
const absl::strings_internal::DereferenceFormatterImpl<
absl::strings_internal::AlphaNumFormatterImpl>
f;
int x = 1, y = 2, z = 3;
std::string s;
f(&s, &x);
f(&s, &y);
f(&s, &z);
EXPECT_EQ("123", s);
}
{
absl::strings_internal::DereferenceFormatterImpl<
absl::strings_internal::DefaultFormatter<std::string>::Type>
f;
std::string x = "x";
std::string y = "y";
std::string z = "z";
std::string s;
f(&s, &x);
f(&s, &y);
f(&s, &z);
EXPECT_EQ(s, "xyz");
}
{
auto f = absl::DereferenceFormatter(TestingParenFormatter());
int x = 1, y = 2, z = 3;
std::string s;
f(&s, &x);
f(&s, &y);
f(&s, &z);
EXPECT_EQ("(1)(2)(3)", s);
}
{
absl::strings_internal::DereferenceFormatterImpl<
absl::strings_internal::AlphaNumFormatterImpl>
f;
auto x = std::unique_ptr<int>(new int(1));
auto y = std::unique_ptr<int>(new int(2));
auto z = std::unique_ptr<int>(new int(3));
std::string s;
f(&s, x);
f(&s, y);
f(&s, z);
EXPECT_EQ("123", s);
}
}
TEST(StrJoin, PublicAPIOverloads) {
std::vector<std::string> v = {"a", "b", "c"};
EXPECT_EQ("a-b-c",
absl::StrJoin(v.begin(), v.end(), "-", absl::AlphaNumFormatter()));
EXPECT_EQ("a-b-c", absl::StrJoin(v, "-", absl::AlphaNumFormatter()));
EXPECT_EQ("a-b-c", absl::StrJoin(v.begin(), v.end(), "-"));
EXPECT_EQ("a-b-c", absl::StrJoin(v, "-"));
}
TEST(StrJoin, Array) {
const absl::string_view a[] = {"a", "b", "c"};
EXPECT_EQ("a-b-c", absl::StrJoin(a, "-"));
}
TEST(StrJoin, InitializerList) {
{ EXPECT_EQ("a-b-c", absl::StrJoin({"a", "b", "c"}, "-")); }
{
auto a = {"a", "b", "c"};
EXPECT_EQ("a-b-c", absl::StrJoin(a, "-"));
}
{
std::initializer_list<const char*> a = {"a", "b", "c"};
EXPECT_EQ("a-b-c", absl::StrJoin(a, "-"));
}
{
std::initializer_list<std::string> a = {"a", "b", "c"};
EXPECT_EQ("a-b-c", absl::StrJoin(a, "-"));
}
{
std::initializer_list<absl::string_view> a = {"a", "b", "c"};
EXPECT_EQ("a-b-c", absl::StrJoin(a, "-"));
}
{
auto a = {"a", "b", "c"};
TestingParenFormatter f;
EXPECT_EQ("(a)-(b)-(c)", absl::StrJoin(a, "-", f));
}
{
EXPECT_EQ("1-2-3", absl::StrJoin({1, 2, 3}, "-"));
}
{
auto a = {1, 2, 3};
TestingParenFormatter f;
EXPECT_EQ("(1)-(2)-(3)", absl::StrJoin(a, "-", f));
}
}
TEST(StrJoin, StringViewInitializerList) {
{
std::string b = "b";
EXPECT_EQ("a-b-c", absl::StrJoin({"a", b, "c"}, "-"));
}
{
TestingParenFormatter f;
std::string b = "b";
EXPECT_EQ("(a)-(b)-(c)", absl::StrJoin({"a", b, "c"}, "-", f));
}
class NoCopy {
public:
explicit NoCopy(absl::string_view view) : view_(view) {}
NoCopy(const NoCopy&) = delete;
operator absl::string_view() { return view_; } private:
absl::string_view view_;
};
{
EXPECT_EQ("a-b-c",
absl::StrJoin({NoCopy("a"), NoCopy("b"), NoCopy("c")}, "-"));
}
{
TestingParenFormatter f;
EXPECT_EQ("(a)-(b)-(c)",
absl::StrJoin({NoCopy("a"), NoCopy("b"), NoCopy("c")}, "-", f));
}
}
TEST(StrJoin, Tuple) {
EXPECT_EQ("", absl::StrJoin(std::make_tuple(), "-"));
EXPECT_EQ("hello", absl::StrJoin(std::make_tuple("hello"), "-"));
int x(10);
std::string y("hello");
double z(3.14);
EXPECT_EQ("10-hello-3.14", absl::StrJoin(std::make_tuple(x, y, z), "-"));
EXPECT_EQ("10-hello-3.14",
absl::StrJoin(std::make_tuple(x, std::cref(y), z), "-"));
struct TestFormatter {
char buffer[128];
void operator()(std::string* out, int v) {
snprintf(buffer, sizeof(buffer), "%#.8x", v);
out->append(buffer);
}
void operator()(std::string* out, double v) {
snprintf(buffer, sizeof(buffer), "%#.0f", v);
out->append(buffer);
}
void operator()(std::string* out, const std::string& v) {
snprintf(buffer, sizeof(buffer), "%.4s", v.c_str());
out->append(buffer);
}
};
EXPECT_EQ("0x0000000a-hell-3.",
absl::StrJoin(std::make_tuple(x, y, z), "-", TestFormatter()));
EXPECT_EQ(
"0x0000000a-hell-3.",
absl::StrJoin(std::make_tuple(x, std::cref(y), z), "-", TestFormatter()));
EXPECT_EQ("0x0000000a-hell-3.",
absl::StrJoin(std::make_tuple(&x, &y, &z), "-",
absl::DereferenceFormatter(TestFormatter())));
EXPECT_EQ("0x0000000a-hell-3.",
absl::StrJoin(std::make_tuple(absl::make_unique<int>(x),
absl::make_unique<std::string>(y),
absl::make_unique<double>(z)),
"-", absl::DereferenceFormatter(TestFormatter())));
EXPECT_EQ("0x0000000a-hell-3.",
absl::StrJoin(std::make_tuple(absl::make_unique<int>(x), &y, &z),
"-", absl::DereferenceFormatter(TestFormatter())));
}
class TestValue {
public:
TestValue(const char* data, size_t size) : data_(data), size_(size) {}
const char* data() const { return data_; }
size_t size() const { return size_; }
private:
const char* data_;
size_t size_;
};
template <typename ValueT>
class TestIterator {
public:
using iterator_category = std::forward_iterator_tag;
using value_type = ValueT;
using pointer = void;
using reference = const value_type&;
using difference_type = int;
static TestIterator begin(const std::vector<absl::string_view>& data) {
return TestIterator(&data, 0);
}
static TestIterator end(const std::vector<absl::string_view>& data) {
return TestIterator(nullptr, data.size());
}
bool operator==(const TestIterator& other) const {
return pos_ == other.pos_;
}
bool operator!=(const TestIterator& other) const {
return pos_ != other.pos_;
}
value_type operator*() const {
return ValueT((*data_)[pos_].data(), (*data_)[pos_].size());
}
TestIterator& operator++() {
++pos_;
return *this;
}
TestIterator operator++(int) {
TestIterator result = *this;
++(*this);
return result;
}
TestIterator& operator--() {
--pos_;
return *this;
}
TestIterator operator--(int) {
TestIterator result = *this;
--(*this);
return result;
}
private:
TestIterator(const std::vector<absl::string_view>* data, size_t pos)
: data_(data), pos_(pos) {}
const std::vector<absl::string_view>* data_;
size_t pos_;
};
template <typename ValueT>
class TestIteratorRange {
public:
explicit TestIteratorRange(const std::vector<absl::string_view>& data)
: begin_(TestIterator<ValueT>::begin(data)),
end_(TestIterator<ValueT>::end(data)) {}
const TestIterator<ValueT>& begin() const { return begin_; }
const TestIterator<ValueT>& end() const { return end_; }
private:
TestIterator<ValueT> begin_;
TestIterator<ValueT> end_;
};
TEST(StrJoin, TestIteratorRequirementsNoFormatter) {
const std::vector<absl::string_view> a = {"a", "b", "c"};
EXPECT_EQ("a-b-c",
absl::StrJoin(TestIteratorRange<absl::string_view>(a), "-"));
}
TEST(StrJoin, TestIteratorRequirementsCustomFormatter) {
const std::vector<absl::string_view> a = {"a", "b", "c"};
EXPECT_EQ("a-b-c",
absl::StrJoin(TestIteratorRange<TestValue>(a), "-",
[](std::string* out, const TestValue& value) {
absl::StrAppend(
out,
absl::string_view(value.data(), value.size()));
}));
}
}