diff --git a/src/v/kafka/protocol/tests/BUILD b/src/v/kafka/protocol/tests/BUILD index c367f9f62b5ae..55e42bce5db15 100644 --- a/src/v/kafka/protocol/tests/BUILD +++ b/src/v/kafka/protocol/tests/BUILD @@ -56,6 +56,7 @@ redpanda_cc_btest( "field_parser_test.cc", ], deps = [ + "//src/v/base", "//src/v/kafka/protocol", "//src/v/random:generators", "//src/v/test_utils:container_ostream", @@ -63,6 +64,7 @@ redpanda_cc_btest( "//src/v/test_utils:seastar_boost", "//src/v/utils:base64", "@boost//:iterator", + "@fmt", "@seastar", "@seastar//:testing", ], diff --git a/src/v/kafka/protocol/tests/field_parser_test.cc b/src/v/kafka/protocol/tests/field_parser_test.cc index 091fb575328f4..f0b90aff7ef2f 100644 --- a/src/v/kafka/protocol/tests/field_parser_test.cc +++ b/src/v/kafka/protocol/tests/field_parser_test.cc @@ -9,6 +9,7 @@ * by the Apache License, Version 2.0 */ +#include "base/format_to.h" #include "kafka/protocol/types.h" #include "kafka/protocol/wire.h" #include "random/generators.h" @@ -79,9 +80,8 @@ struct test_struct { return a.field_a == b.field_a && a.field_b == b.field_b; } - friend std::ostream& operator<<(std::ostream& os, const test_struct& ts) { - os << "field_a: " << ts.field_a << " field_b: " << ts.field_b; - return os; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "field_a: {} field_b: {}", field_a, field_b); } }; diff --git a/src/v/pandaproxy/parsing/test/BUILD b/src/v/pandaproxy/parsing/test/BUILD index 06dedeca632ad..860197ceaf9ea 100644 --- a/src/v/pandaproxy/parsing/test/BUILD +++ b/src/v/pandaproxy/parsing/test/BUILD @@ -26,6 +26,7 @@ redpanda_cc_btest( "httpd.cc", ], deps = [ + "//src/v/base", "//src/v/pandaproxy:json", "//src/v/pandaproxy:parsing", "//src/v/reflection:type_traits", @@ -35,6 +36,7 @@ redpanda_cc_btest( "@boost//:test", "@boost//:tuple", "@boost//:utility", + "@fmt", "@seastar", "@seastar//:testing", ], diff --git a/src/v/pandaproxy/parsing/test/httpd.cc b/src/v/pandaproxy/parsing/test/httpd.cc index 0cff888d6ae47..5f6cb4321705c 100644 --- a/src/v/pandaproxy/parsing/test/httpd.cc +++ b/src/v/pandaproxy/parsing/test/httpd.cc @@ -11,6 +11,7 @@ #include "pandaproxy/parsing/httpd.h" +#include "base/format_to.h" #include "pandaproxy/json/types.h" #include "test_utils/container_ostream.h" // IWYU pragma: keep @@ -24,8 +25,8 @@ namespace pp = pandaproxy; namespace ppj = pp::json; namespace pandaproxy::json { -std::ostream& operator<<(std::ostream& os, serialization_format fmt) { - return os << name(fmt); +fmt::iterator format_to(serialization_format fmt, fmt::iterator it) { + return fmt::format_to(it, "{}", name(fmt)); } } // namespace pandaproxy::json diff --git a/src/v/test_utils/BUILD b/src/v/test_utils/BUILD index 49f3a356f0132..682e8570e4dc2 100644 --- a/src/v/test_utils/BUILD +++ b/src/v/test_utils/BUILD @@ -185,6 +185,9 @@ redpanda_test_cc_library( "container_ostream.h", ], visibility = ["//visibility:public"], + deps = [ + "@fmt", + ], ) redpanda_test_cc_library( diff --git a/src/v/test_utils/container_ostream.h b/src/v/test_utils/container_ostream.h index 35c18d25f7acb..6af01e763646c 100644 --- a/src/v/test_utils/container_ostream.h +++ b/src/v/test_utils/container_ostream.h @@ -23,10 +23,15 @@ /// rely on streaming containers, which is why the set is limited to those /// two types rather than every standard container. /// -/// The overloads stream each element via operator<< so they work for any -/// element type that has streaming, regardless of whether it has a fmt -/// formatter. Include this only in test translation units to avoid -/// pulling a std-namespace overload into production code. +/// The overloads delegate to fmt's range formatter, so the element type +/// must be fmt-formattable (has a fmt::formatter specialization or a +/// format_to method/free function). Include this only in test +/// translation units to avoid pulling a std-namespace overload into +/// production code. + +#include +#include +#include #include #include @@ -37,32 +42,16 @@ namespace std { template // NOLINTNEXTLINE(cert-dcl58-cpp): test-only operator<< overload for std types ostream& operator<<(ostream& os, const vector& v) { - os << "{"; - bool first = true; - for (const auto& e : v) { - if (!first) { - os << ", "; - } - first = false; - os << e; - } - return os << "}"; + fmt::print(os, "{}", v); + return os; } template // NOLINTNEXTLINE(cert-dcl58-cpp): test-only operator<< overload for std types ostream& operator<<(ostream& os, const unordered_map& m) { - os << "{"; - bool first = true; - for (const auto& [k, v] : m) { - if (!first) { - os << ", "; - } - first = false; - os << "{" << k << " -> " << v << "}"; - } - return os << "}"; + fmt::print(os, "{}", m); + return os; } } // namespace std diff --git a/src/v/test_utils/tests/container_ostream_test.cc b/src/v/test_utils/tests/container_ostream_test.cc index bbe405969836a..202152dda785d 100644 --- a/src/v/test_utils/tests/container_ostream_test.cc +++ b/src/v/test_utils/tests/container_ostream_test.cc @@ -25,40 +25,22 @@ std::string stream(const T& v) { return std::move(os).str(); } -} // namespace - -// An element type that has operator<< but no fmt formatter, to verify the -// header does not require fmt-formattability. Defined at file scope so the -// streaming operator below can be a non-friend free function reachable via -// ADL from the test below. -struct only_stream { - int x; -}; -inline std::ostream& operator<<(std::ostream& os, const only_stream& v) { - return os << "x=" << v.x; -} - -namespace { - -TEST(ContainerOstream, ElementOnlyHasOperatorStream) { - EXPECT_EQ(stream(std::vector{{1}, {2}}), "{x=1, x=2}"); -} - TEST(ContainerOstream, EmptyVector) { - EXPECT_EQ(stream(std::vector{}), "{}"); + EXPECT_EQ(stream(std::vector{}), "[]"); } TEST(ContainerOstream, VectorOfInts) { - EXPECT_EQ(stream(std::vector{1, 2, 3}), "{1, 2, 3}"); + EXPECT_EQ(stream(std::vector{1, 2, 3}), "[1, 2, 3]"); } TEST(ContainerOstream, VectorOfStrings) { - EXPECT_EQ(stream(std::vector{"a", "b"}), "{a, b}"); + // fmt's range formatter wraps strings in quotes by default. + EXPECT_EQ(stream(std::vector{"a", "b"}), "[\"a\", \"b\"]"); } TEST(ContainerOstream, NestedVector) { EXPECT_EQ( - stream(std::vector>{{1, 2}, {3}}), "{{1, 2}, {3}}"); + stream(std::vector>{{1, 2}, {3}}), "[[1, 2], [3]]"); } TEST(ContainerOstream, EmptyUnorderedMap) { @@ -69,7 +51,7 @@ TEST(ContainerOstream, UnorderedMapSingleEntry) { // Unordered iteration order is implementation-defined; pin the test to // one entry so it stays deterministic. EXPECT_EQ( - stream(std::unordered_map{{1, "one"}}), "{{1 -> one}}"); + stream(std::unordered_map{{1, "one"}}), "{1: \"one\"}"); } TEST(ContainerOstream, GTestStreamingMessage) { @@ -77,7 +59,7 @@ TEST(ContainerOstream, GTestStreamingMessage) { // container into a googletest assertion message. std::vector v{42}; testing::AssertionResult r = testing::AssertionFailure() << v; - EXPECT_EQ(std::string{r.message()}, "{42}"); + EXPECT_EQ(std::string{r.message()}, "[42]"); } } // namespace