1 // Formatting library for C++ - dynamic argument store tests
2 //
3 // Copyright (c) 2012 - present, Victor Zverovich
4 // All rights reserved.
5 //
6 // For the license information refer to format.h.
7
8 #include "fmt/args.h"
9
10 #include <memory>
11
12 #include "gtest/gtest.h"
13
TEST(args_test,basic)14 TEST(args_test, basic) {
15 fmt::dynamic_format_arg_store<fmt::format_context> store;
16 store.push_back(42);
17 store.push_back("abc1");
18 store.push_back(1.5f);
19 EXPECT_EQ("42 and abc1 and 1.5", fmt::vformat("{} and {} and {}", store));
20 }
21
TEST(args_test,strings_and_refs)22 TEST(args_test, strings_and_refs) {
23 // Unfortunately the tests are compiled with old ABI so strings use COW.
24 fmt::dynamic_format_arg_store<fmt::format_context> store;
25 char str[] = "1234567890";
26 store.push_back(str);
27 store.push_back(std::cref(str));
28 store.push_back(fmt::string_view{str});
29 str[0] = 'X';
30
31 auto result = fmt::vformat("{} and {} and {}", store);
32 EXPECT_EQ("1234567890 and X234567890 and X234567890", result);
33 }
34
35 struct custom_type {
36 int i = 0;
37 };
38
39 FMT_BEGIN_NAMESPACE
40 template <> struct formatter<custom_type> {
parseformatter41 auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
42 return ctx.begin();
43 }
44
45 template <typename FormatContext>
formatformatter46 auto format(const custom_type& p, FormatContext& ctx) const
47 -> decltype(ctx.out()) {
48 return fmt::format_to(ctx.out(), "cust={}", p.i);
49 }
50 };
51 FMT_END_NAMESPACE
52
TEST(args_test,custom_format)53 TEST(args_test, custom_format) {
54 fmt::dynamic_format_arg_store<fmt::format_context> store;
55 auto c = custom_type();
56 store.push_back(c);
57 ++c.i;
58 store.push_back(c);
59 ++c.i;
60 store.push_back(std::cref(c));
61 ++c.i;
62 auto result = fmt::vformat("{} and {} and {}", store);
63 EXPECT_EQ("cust=0 and cust=1 and cust=3", result);
64 }
65
66 struct to_stringable {
to_string_view(to_stringable)67 friend fmt::string_view to_string_view(to_stringable) { return {}; }
68 };
69
70 FMT_BEGIN_NAMESPACE
71 template <> struct formatter<to_stringable> {
parseformatter72 auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
73 return ctx.begin();
74 }
75
formatformatter76 auto format(to_stringable, format_context& ctx) const -> decltype(ctx.out()) {
77 return ctx.out();
78 }
79 };
80 FMT_END_NAMESPACE
81
TEST(args_test,to_string_and_formatter)82 TEST(args_test, to_string_and_formatter) {
83 fmt::dynamic_format_arg_store<fmt::format_context> store;
84 auto s = to_stringable();
85 store.push_back(s);
86 store.push_back(std::cref(s));
87 fmt::vformat("", store);
88 }
89
TEST(args_test,named_int)90 TEST(args_test, named_int) {
91 fmt::dynamic_format_arg_store<fmt::format_context> store;
92 store.push_back(fmt::arg("a1", 42));
93 EXPECT_EQ("42", fmt::vformat("{a1}", store));
94 }
95
TEST(args_test,named_strings)96 TEST(args_test, named_strings) {
97 fmt::dynamic_format_arg_store<fmt::format_context> store;
98 char str[] = "1234567890";
99 store.push_back(fmt::arg("a1", str));
100 store.push_back(fmt::arg("a2", std::cref(str)));
101 str[0] = 'X';
102 EXPECT_EQ("1234567890 and X234567890", fmt::vformat("{a1} and {a2}", store));
103 }
104
TEST(args_test,named_arg_by_ref)105 TEST(args_test, named_arg_by_ref) {
106 fmt::dynamic_format_arg_store<fmt::format_context> store;
107 char band[] = "Rolling Stones";
108 store.push_back(fmt::arg("band", std::cref(band)));
109 band[9] = 'c'; // Changing band affects the output.
110 EXPECT_EQ(fmt::vformat("{band}", store), "Rolling Scones");
111 }
112
TEST(args_test,named_custom_format)113 TEST(args_test, named_custom_format) {
114 fmt::dynamic_format_arg_store<fmt::format_context> store;
115 auto c = custom_type();
116 store.push_back(fmt::arg("c1", c));
117 ++c.i;
118 store.push_back(fmt::arg("c2", c));
119 ++c.i;
120 store.push_back(fmt::arg("c_ref", std::cref(c)));
121 ++c.i;
122 auto result = fmt::vformat("{c1} and {c2} and {c_ref}", store);
123 EXPECT_EQ("cust=0 and cust=1 and cust=3", result);
124 }
125
TEST(args_test,clear)126 TEST(args_test, clear) {
127 fmt::dynamic_format_arg_store<fmt::format_context> store;
128 store.push_back(42);
129
130 auto result = fmt::vformat("{}", store);
131 EXPECT_EQ("42", result);
132
133 store.push_back(43);
134 result = fmt::vformat("{} and {}", store);
135 EXPECT_EQ("42 and 43", result);
136
137 store.clear();
138 store.push_back(44);
139 result = fmt::vformat("{}", store);
140 EXPECT_EQ("44", result);
141 }
142
TEST(args_test,reserve)143 TEST(args_test, reserve) {
144 fmt::dynamic_format_arg_store<fmt::format_context> store;
145 store.reserve(2, 1);
146 store.push_back(1.5f);
147 store.push_back(fmt::arg("a", 42));
148 auto result = fmt::vformat("{} and {a}", store);
149 EXPECT_EQ("1.5 and 42", result);
150 }
151
152 struct copy_throwable {
copy_throwablecopy_throwable153 copy_throwable() {}
copy_throwablecopy_throwable154 copy_throwable(const copy_throwable&) { throw "deal with it"; }
155 };
156
157 FMT_BEGIN_NAMESPACE
158 template <> struct formatter<copy_throwable> {
parseformatter159 auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
160 return ctx.begin();
161 }
formatformatter162 auto format(copy_throwable, format_context& ctx) const
163 -> decltype(ctx.out()) {
164 return ctx.out();
165 }
166 };
167 FMT_END_NAMESPACE
168
TEST(args_test,throw_on_copy)169 TEST(args_test, throw_on_copy) {
170 fmt::dynamic_format_arg_store<fmt::format_context> store;
171 store.push_back(std::string("foo"));
172 try {
173 store.push_back(copy_throwable());
174 } catch (...) {
175 }
176 EXPECT_EQ(fmt::vformat("{}", store), "foo");
177 }
178
TEST(args_test,move_constructor)179 TEST(args_test, move_constructor) {
180 using store_type = fmt::dynamic_format_arg_store<fmt::format_context>;
181 auto store = std::unique_ptr<store_type>(new store_type());
182 store->push_back(42);
183 store->push_back(std::string("foo"));
184 store->push_back(fmt::arg("a1", "foo"));
185 auto moved_store = std::move(*store);
186 store.reset();
187 EXPECT_EQ(fmt::vformat("{} {} {a1}", moved_store), "42 foo foo");
188 }
189