| // Copyright 2025 The Abseil Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "absl/log/internal/container.h" |
| |
| #include <cstdint> |
| #include <map> |
| #include <memory> |
| #include <ostream> |
| #include <set> |
| #include <sstream> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "gtest/gtest.h" |
| #include "absl/base/config.h" |
| #include "absl/strings/str_format.h" |
| #include "absl/strings/str_join.h" |
| #include "absl/types/span.h" |
| |
| namespace absl { |
| ABSL_NAMESPACE_BEGIN |
| namespace log_internal { |
| namespace { |
| |
| class ContainerLoggingTest : public ::testing::Test { |
| protected: |
| ContainerLoggingTest() : stream_(new std::stringstream) {} |
| std::ostream& stream() { return *stream_; } |
| std::string logged() { |
| std::string r = stream_->str(); |
| stream_ = std::make_unique<std::stringstream>(); |
| return r; |
| } |
| |
| private: |
| std::unique_ptr<std::stringstream> stream_; |
| }; |
| |
| TEST_F(ContainerLoggingTest, ShortRange) { |
| std::vector<std::string> words = {"hi", "hello"}; |
| LogRangeToStream(stream(), words.begin(), words.end(), LogMultiline()); |
| EXPECT_EQ("[\nhi\nhello\n]", logged()); |
| } |
| |
| TEST_F(ContainerLoggingTest, LegacyRange) { |
| std::vector<int> lengths = {1, 2}; |
| LogRangeToStream(stream(), lengths.begin(), lengths.end(), |
| LogLegacyUpTo100()); |
| EXPECT_EQ("1 2", logged()); |
| } |
| |
| TEST_F(ContainerLoggingTest, ToString) { |
| std::vector<int> lengths = {1, 2, 3, 4, 5}; |
| EXPECT_EQ(LogContainer(lengths).str(), "[1, 2, 3, 4, 5]"); |
| } |
| |
| class UserDefFriend { |
| public: |
| explicit UserDefFriend(int i) : i_(i) {} |
| |
| private: |
| friend std::ostream& operator<<(std::ostream& str, const UserDefFriend& i) { |
| return str << i.i_; |
| } |
| int i_; |
| }; |
| |
| TEST_F(ContainerLoggingTest, RangeOfUserDefined) { |
| std::vector<UserDefFriend> ints = {UserDefFriend(1), UserDefFriend(2), |
| UserDefFriend(3)}; |
| LogRangeToStream(stream(), ints.begin(), ints.begin() + 1, LogDefault()); |
| LogRangeToStream(stream(), ints.begin() + 1, ints.begin() + 2, |
| LogMultiline()); |
| LogRangeToStream(stream(), ints.begin() + 2, ints.begin() + 3, LogDefault()); |
| LogRangeToStream(stream(), ints.begin(), ints.begin(), LogMultiline()); |
| |
| EXPECT_EQ("[1][\n2\n][3][\n]", logged()); |
| } |
| |
| TEST_F(ContainerLoggingTest, FullContainer) { |
| std::vector<int> ints; |
| std::vector<int> ints100; |
| std::vector<int> ints123; |
| int64_t max_elements = 123; |
| std::string expected1; |
| std::string expected2; |
| std::string expected3; |
| std::string expected4; |
| std::string expected5; |
| std::string expected6; |
| std::string expected7; |
| std::string expected8; |
| std::string expected9; |
| for (int i = 0; i < 1000; ++i) { |
| ints.push_back(i); |
| if (i < 100) { |
| ints100.push_back(i); |
| } |
| if (i < max_elements) { |
| ints123.push_back(i); |
| } |
| } |
| expected1 = "[\n" + absl::StrJoin(ints, "\n") + "\n]"; |
| expected2 = "[" + absl::StrJoin(ints, ", ") + "]"; |
| expected3 = "[\n" + absl::StrJoin(ints100, "\n") + "\n...\n]"; |
| expected4 = "[" + absl::StrJoin(ints100, ", ") + ", ...]"; |
| expected5 = absl::StrJoin(ints100, " ") + " ..."; |
| expected6 = "[\n" + absl::StrJoin(ints, "\n") + "\n]"; |
| expected7 = "[\n" + absl::StrJoin(ints123, "\n") + "\n...\n]"; |
| expected8 = "[" + absl::StrJoin(ints, ", ") + "]"; |
| expected9 = "[" + absl::StrJoin(ints123, ", ") + ", ...]"; |
| |
| LogRangeToStream(stream(), ints.begin(), ints.end(), LogMultiline()); |
| EXPECT_EQ(expected1, logged()); |
| LogRangeToStream(stream(), ints.begin(), ints.end(), LogShort()); |
| EXPECT_EQ(expected2, logged()); |
| LogRangeToStream(stream(), ints.begin(), ints.end(), LogMultilineUpTo100()); |
| EXPECT_EQ(expected3, logged()); |
| LogRangeToStream(stream(), ints.begin(), ints.end(), LogShortUpTo100()); |
| EXPECT_EQ(expected4, logged()); |
| LogRangeToStream(stream(), ints.begin(), ints.end(), LogLegacyUpTo100()); |
| EXPECT_EQ(expected5, logged()); |
| |
| LogRangeToStream(stream(), ints.begin(), ints.end(), |
| LogMultilineUpToN(ints.size())); |
| EXPECT_EQ(expected6, logged()); |
| LogRangeToStream(stream(), ints.begin(), ints.end(), |
| LogMultilineUpToN(max_elements)); |
| EXPECT_EQ(expected7, logged()); |
| LogRangeToStream(stream(), ints.begin(), ints.end(), |
| LogShortUpToN(ints.size())); |
| EXPECT_EQ(expected8, logged()); |
| LogRangeToStream(stream(), ints.begin(), ints.end(), |
| LogShortUpToN(max_elements)); |
| EXPECT_EQ(expected9, logged()); |
| } |
| |
| TEST_F(ContainerLoggingTest, LogContainer) { |
| std::set<int> ints = {1, 2, 3}; |
| stream() << LogContainer(ints, LogMultiline()); |
| EXPECT_EQ("[\n1\n2\n3\n]", logged()); |
| |
| stream() << LogContainer(ints); |
| EXPECT_EQ("[1, 2, 3]", logged()); |
| |
| stream() << LogContainer(std::vector<int>(ints.begin(), ints.end()), |
| LogLegacyUpTo100()); |
| EXPECT_EQ("1 2 3", logged()); |
| } |
| |
| TEST_F(ContainerLoggingTest, LogMutableSpan) { |
| std::vector<int> ints = {1, 2, 3}; |
| absl::Span<int> int_span(ints); |
| stream() << LogContainer(int_span); |
| EXPECT_EQ("[1, 2, 3]", logged()); |
| } |
| |
| TEST_F(ContainerLoggingTest, LogRange) { |
| std::set<int> ints = {1, 2, 3}; |
| stream() << LogRange(ints.begin(), ints.end(), LogMultiline()); |
| EXPECT_EQ("[\n1\n2\n3\n]", logged()); |
| |
| stream() << LogRange(ints.begin(), ints.end()); |
| EXPECT_EQ("[1, 2, 3]", logged()); |
| } |
| |
| // Some class with a custom Stringify |
| class C { |
| public: |
| explicit C(int x) : x_(x) {} |
| |
| private: |
| // This is intentionally made private for the purposes of the test; |
| //` AbslStringify` isn't meant to be called directly, and instead invoked |
| // via `StrCat` and friends. |
| template <typename Sink> |
| friend void AbslStringify(Sink& sink, const C& p) { |
| absl::Format(&sink, "C(%d)", p.x_); |
| } |
| |
| int x_; |
| }; |
| |
| TEST_F(ContainerLoggingTest, LogContainerWithCustomStringify) { |
| std::vector<C> c = {C(1), C(2), C(3)}; |
| stream() << LogContainer(c); |
| EXPECT_EQ("[C(1), C(2), C(3)]", logged()); |
| } |
| |
| class LogEnumTest : public ContainerLoggingTest { |
| protected: |
| enum Unscoped { kUnscoped0, kUnscoped1, kUnscoped2 }; |
| |
| enum StreamableUnscoped { |
| kStreamableUnscoped0, |
| kStreamableUnscoped1, |
| kStreamableUnscoped2 |
| }; |
| |
| enum class Scoped { k0, k1, k2 }; |
| |
| enum class StreamableScoped { k0, k1, k2 }; |
| |
| friend std::ostream& operator<<(std::ostream& os, StreamableUnscoped v) { |
| return os << LogEnum(v); |
| } |
| |
| friend std::ostream& operator<<(std::ostream& os, StreamableScoped v) { |
| return os << LogEnum(v); |
| } |
| }; |
| |
| TEST_F(LogEnumTest, Unscoped) { |
| stream() << LogEnum(kUnscoped0) << "," << LogEnum(kUnscoped1) << "," |
| << LogEnum(kUnscoped2); |
| EXPECT_EQ("0,1,2", logged()); |
| } |
| |
| TEST_F(LogEnumTest, StreamableUnscoped) { |
| stream() << kStreamableUnscoped0 << "," << kStreamableUnscoped1 << "," |
| << kStreamableUnscoped2; |
| EXPECT_EQ("0,1,2", logged()); |
| } |
| |
| TEST_F(LogEnumTest, Scoped) { |
| stream() << LogEnum(Scoped::k0) << "," << LogEnum(Scoped::k1) << "," |
| << LogEnum(Scoped::k2); |
| EXPECT_EQ("0,1,2", logged()); |
| } |
| |
| TEST_F(LogEnumTest, StreamableScoped) { |
| // Test using LogEnum to implement an operator<<. |
| stream() << StreamableScoped::k0 << "," << StreamableScoped::k1 << "," |
| << StreamableScoped::k2; |
| EXPECT_EQ("0,1,2", logged()); |
| } |
| |
| } // namespace |
| } // namespace log_internal |
| ABSL_NAMESPACE_END |
| } // namespace absl |