1 #include "absl/strings/internal/str_format/parser.h"
2
3 #include <string.h>
4 #include "gtest/gtest.h"
5 #include "absl/base/macros.h"
6
7 namespace absl {
8 namespace str_format_internal {
9
10 namespace {
11
TEST(LengthModTest,Names)12 TEST(LengthModTest, Names) {
13 struct Expectation {
14 int line;
15 LengthMod::Id id;
16 const char *name;
17 };
18 const Expectation kExpect[] = {
19 {__LINE__, LengthMod::none, "" },
20 {__LINE__, LengthMod::h, "h" },
21 {__LINE__, LengthMod::hh, "hh"},
22 {__LINE__, LengthMod::l, "l" },
23 {__LINE__, LengthMod::ll, "ll"},
24 {__LINE__, LengthMod::L, "L" },
25 {__LINE__, LengthMod::j, "j" },
26 {__LINE__, LengthMod::z, "z" },
27 {__LINE__, LengthMod::t, "t" },
28 {__LINE__, LengthMod::q, "q" },
29 };
30 EXPECT_EQ(ABSL_ARRAYSIZE(kExpect), LengthMod::kNumValues);
31 for (auto e : kExpect) {
32 SCOPED_TRACE(e.line);
33 LengthMod mod = LengthMod::FromId(e.id);
34 EXPECT_EQ(e.id, mod.id());
35 EXPECT_EQ(e.name, mod.name());
36 }
37 }
38
TEST(ConversionCharTest,Names)39 TEST(ConversionCharTest, Names) {
40 struct Expectation {
41 ConversionChar::Id id;
42 char name;
43 };
44 // clang-format off
45 const Expectation kExpect[] = {
46 #define X(c) {ConversionChar::c, #c[0]}
47 X(c), X(C), X(s), X(S), // text
48 X(d), X(i), X(o), X(u), X(x), X(X), // int
49 X(f), X(F), X(e), X(E), X(g), X(G), X(a), X(A), // float
50 X(n), X(p), // misc
51 #undef X
52 {ConversionChar::none, '\0'},
53 };
54 // clang-format on
55 EXPECT_EQ(ABSL_ARRAYSIZE(kExpect), ConversionChar::kNumValues);
56 for (auto e : kExpect) {
57 SCOPED_TRACE(e.name);
58 ConversionChar v = ConversionChar::FromId(e.id);
59 EXPECT_EQ(e.id, v.id());
60 EXPECT_EQ(e.name, v.Char());
61 }
62 }
63
64 class ConsumeUnboundConversionTest : public ::testing::Test {
65 public:
66 typedef UnboundConversion Props;
Consume(string_view * src)67 string_view Consume(string_view* src) {
68 int next = 0;
69 const char* prev_begin = src->begin();
70 o = UnboundConversion(); // refresh
71 ConsumeUnboundConversion(src, &o, &next);
72 return {prev_begin, static_cast<size_t>(src->begin() - prev_begin)};
73 }
74
Run(const char * fmt,bool force_positional=false)75 bool Run(const char *fmt, bool force_positional = false) {
76 string_view src = fmt;
77 int next = force_positional ? -1 : 0;
78 o = UnboundConversion(); // refresh
79 return ConsumeUnboundConversion(&src, &o, &next) && src.empty();
80 }
81 UnboundConversion o;
82 };
83
TEST_F(ConsumeUnboundConversionTest,ConsumeSpecification)84 TEST_F(ConsumeUnboundConversionTest, ConsumeSpecification) {
85 struct Expectation {
86 int line;
87 const char *src;
88 const char *out;
89 const char *src_post;
90 };
91 const Expectation kExpect[] = {
92 {__LINE__, "", "", "" },
93 {__LINE__, "b", "", "b" }, // 'b' is invalid
94 {__LINE__, "ba", "", "ba"}, // 'b' is invalid
95 {__LINE__, "l", "", "l" }, // just length mod isn't okay
96 {__LINE__, "d", "d", "" }, // basic
97 {__LINE__, "d ", "d", " " }, // leave suffix
98 {__LINE__, "dd", "d", "d" }, // don't be greedy
99 {__LINE__, "d9", "d", "9" }, // leave non-space suffix
100 {__LINE__, "dzz", "d", "zz"}, // length mod as suffix
101 {__LINE__, "1$*2$d", "1$*2$d", "" }, // arg indexing and * allowed.
102 {__LINE__, "0-14.3hhd", "0-14.3hhd", ""}, // precision, width
103 {__LINE__, " 0-+#14.3hhd", " 0-+#14.3hhd", ""}, // flags
104 };
105 for (const auto& e : kExpect) {
106 SCOPED_TRACE(e.line);
107 string_view src = e.src;
108 EXPECT_EQ(e.src, src);
109 string_view out = Consume(&src);
110 EXPECT_EQ(e.out, out);
111 EXPECT_EQ(e.src_post, src);
112 }
113 }
114
TEST_F(ConsumeUnboundConversionTest,BasicConversion)115 TEST_F(ConsumeUnboundConversionTest, BasicConversion) {
116 EXPECT_FALSE(Run(""));
117 EXPECT_FALSE(Run("z"));
118
119 EXPECT_FALSE(Run("dd")); // no excess allowed
120
121 EXPECT_TRUE(Run("d"));
122 EXPECT_EQ('d', o.conv.Char());
123 EXPECT_FALSE(o.width.is_from_arg());
124 EXPECT_LT(o.width.value(), 0);
125 EXPECT_FALSE(o.precision.is_from_arg());
126 EXPECT_LT(o.precision.value(), 0);
127 EXPECT_EQ(1, o.arg_position);
128 EXPECT_EQ(LengthMod::none, o.length_mod.id());
129 }
130
TEST_F(ConsumeUnboundConversionTest,ArgPosition)131 TEST_F(ConsumeUnboundConversionTest, ArgPosition) {
132 EXPECT_TRUE(Run("d"));
133 EXPECT_EQ(1, o.arg_position);
134 EXPECT_TRUE(Run("3$d"));
135 EXPECT_EQ(3, o.arg_position);
136 EXPECT_TRUE(Run("1$d"));
137 EXPECT_EQ(1, o.arg_position);
138 EXPECT_TRUE(Run("1$d", true));
139 EXPECT_EQ(1, o.arg_position);
140 EXPECT_TRUE(Run("123$d"));
141 EXPECT_EQ(123, o.arg_position);
142 EXPECT_TRUE(Run("123$d", true));
143 EXPECT_EQ(123, o.arg_position);
144 EXPECT_TRUE(Run("10$d"));
145 EXPECT_EQ(10, o.arg_position);
146 EXPECT_TRUE(Run("10$d", true));
147 EXPECT_EQ(10, o.arg_position);
148
149 // Position can't be zero.
150 EXPECT_FALSE(Run("0$d"));
151 EXPECT_FALSE(Run("0$d", true));
152 EXPECT_FALSE(Run("1$*0$d"));
153 EXPECT_FALSE(Run("1$.*0$d"));
154
155 // Position can't start with a zero digit at all. That is not a 'decimal'.
156 EXPECT_FALSE(Run("01$p"));
157 EXPECT_FALSE(Run("01$p", true));
158 EXPECT_FALSE(Run("1$*01$p"));
159 EXPECT_FALSE(Run("1$.*01$p"));
160 }
161
TEST_F(ConsumeUnboundConversionTest,WidthAndPrecision)162 TEST_F(ConsumeUnboundConversionTest, WidthAndPrecision) {
163 EXPECT_TRUE(Run("14d"));
164 EXPECT_EQ('d', o.conv.Char());
165 EXPECT_FALSE(o.width.is_from_arg());
166 EXPECT_EQ(14, o.width.value());
167 EXPECT_FALSE(o.precision.is_from_arg());
168 EXPECT_LT(o.precision.value(), 0);
169
170 EXPECT_TRUE(Run("14.d"));
171 EXPECT_FALSE(o.width.is_from_arg());
172 EXPECT_FALSE(o.precision.is_from_arg());
173 EXPECT_EQ(14, o.width.value());
174 EXPECT_EQ(0, o.precision.value());
175
176 EXPECT_TRUE(Run(".d"));
177 EXPECT_FALSE(o.width.is_from_arg());
178 EXPECT_LT(o.width.value(), 0);
179 EXPECT_FALSE(o.precision.is_from_arg());
180 EXPECT_EQ(0, o.precision.value());
181
182 EXPECT_TRUE(Run(".5d"));
183 EXPECT_FALSE(o.width.is_from_arg());
184 EXPECT_LT(o.width.value(), 0);
185 EXPECT_FALSE(o.precision.is_from_arg());
186 EXPECT_EQ(5, o.precision.value());
187
188 EXPECT_TRUE(Run(".0d"));
189 EXPECT_FALSE(o.width.is_from_arg());
190 EXPECT_LT(o.width.value(), 0);
191 EXPECT_FALSE(o.precision.is_from_arg());
192 EXPECT_EQ(0, o.precision.value());
193
194 EXPECT_TRUE(Run("14.5d"));
195 EXPECT_FALSE(o.width.is_from_arg());
196 EXPECT_FALSE(o.precision.is_from_arg());
197 EXPECT_EQ(14, o.width.value());
198 EXPECT_EQ(5, o.precision.value());
199
200 EXPECT_TRUE(Run("*.*d"));
201 EXPECT_TRUE(o.width.is_from_arg());
202 EXPECT_EQ(1, o.width.get_from_arg());
203 EXPECT_TRUE(o.precision.is_from_arg());
204 EXPECT_EQ(2, o.precision.get_from_arg());
205 EXPECT_EQ(3, o.arg_position);
206
207 EXPECT_TRUE(Run("*d"));
208 EXPECT_TRUE(o.width.is_from_arg());
209 EXPECT_EQ(1, o.width.get_from_arg());
210 EXPECT_FALSE(o.precision.is_from_arg());
211 EXPECT_LT(o.precision.value(), 0);
212 EXPECT_EQ(2, o.arg_position);
213
214 EXPECT_TRUE(Run(".*d"));
215 EXPECT_FALSE(o.width.is_from_arg());
216 EXPECT_LT(o.width.value(), 0);
217 EXPECT_TRUE(o.precision.is_from_arg());
218 EXPECT_EQ(1, o.precision.get_from_arg());
219 EXPECT_EQ(2, o.arg_position);
220
221 // mixed implicit and explicit: didn't specify arg position.
222 EXPECT_FALSE(Run("*23$.*34$d"));
223
224 EXPECT_TRUE(Run("12$*23$.*34$d"));
225 EXPECT_EQ(12, o.arg_position);
226 EXPECT_TRUE(o.width.is_from_arg());
227 EXPECT_EQ(23, o.width.get_from_arg());
228 EXPECT_TRUE(o.precision.is_from_arg());
229 EXPECT_EQ(34, o.precision.get_from_arg());
230
231 EXPECT_TRUE(Run("2$*5$.*9$d"));
232 EXPECT_EQ(2, o.arg_position);
233 EXPECT_TRUE(o.width.is_from_arg());
234 EXPECT_EQ(5, o.width.get_from_arg());
235 EXPECT_TRUE(o.precision.is_from_arg());
236 EXPECT_EQ(9, o.precision.get_from_arg());
237
238 EXPECT_FALSE(Run(".*0$d")) << "no arg 0";
239 }
240
TEST_F(ConsumeUnboundConversionTest,Flags)241 TEST_F(ConsumeUnboundConversionTest, Flags) {
242 static const char kAllFlags[] = "-+ #0";
243 static const int kNumFlags = ABSL_ARRAYSIZE(kAllFlags) - 1;
244 for (int rev = 0; rev < 2; ++rev) {
245 for (int i = 0; i < 1 << kNumFlags; ++i) {
246 std::string fmt;
247 for (int k = 0; k < kNumFlags; ++k)
248 if ((i >> k) & 1) fmt += kAllFlags[k];
249 // flag order shouldn't matter
250 if (rev == 1) { std::reverse(fmt.begin(), fmt.end()); }
251 fmt += 'd';
252 SCOPED_TRACE(fmt);
253 EXPECT_TRUE(Run(fmt.c_str()));
254 EXPECT_EQ(fmt.find('-') == std::string::npos, !o.flags.left);
255 EXPECT_EQ(fmt.find('+') == std::string::npos, !o.flags.show_pos);
256 EXPECT_EQ(fmt.find(' ') == std::string::npos, !o.flags.sign_col);
257 EXPECT_EQ(fmt.find('#') == std::string::npos, !o.flags.alt);
258 EXPECT_EQ(fmt.find('0') == std::string::npos, !o.flags.zero);
259 }
260 }
261 }
262
TEST_F(ConsumeUnboundConversionTest,BasicFlag)263 TEST_F(ConsumeUnboundConversionTest, BasicFlag) {
264 // Flag is on
265 for (const char* fmt : {"d", "llx", "G", "1$X"}) {
266 SCOPED_TRACE(fmt);
267 EXPECT_TRUE(Run(fmt));
268 EXPECT_TRUE(o.flags.basic);
269 }
270
271 // Flag is off
272 for (const char* fmt : {"3d", ".llx", "-G", "1$#X"}) {
273 SCOPED_TRACE(fmt);
274 EXPECT_TRUE(Run(fmt));
275 EXPECT_FALSE(o.flags.basic);
276 }
277 }
278
279 struct SummarizeConsumer {
280 std::string* out;
SummarizeConsumerabsl::str_format_internal::__anonac9289e80111::SummarizeConsumer281 explicit SummarizeConsumer(std::string* out) : out(out) {}
282
Appendabsl::str_format_internal::__anonac9289e80111::SummarizeConsumer283 bool Append(string_view s) {
284 *out += "[" + std::string(s) + "]";
285 return true;
286 }
287
ConvertOneabsl::str_format_internal::__anonac9289e80111::SummarizeConsumer288 bool ConvertOne(const UnboundConversion& conv, string_view s) {
289 *out += "{";
290 *out += std::string(s);
291 *out += ":";
292 *out += std::to_string(conv.arg_position) + "$";
293 if (conv.width.is_from_arg()) {
294 *out += std::to_string(conv.width.get_from_arg()) + "$*";
295 }
296 if (conv.precision.is_from_arg()) {
297 *out += "." + std::to_string(conv.precision.get_from_arg()) + "$*";
298 }
299 *out += conv.conv.Char();
300 *out += "}";
301 return true;
302 }
303 };
304
SummarizeParsedFormat(const ParsedFormatBase & pc)305 std::string SummarizeParsedFormat(const ParsedFormatBase& pc) {
306 std::string out;
307 if (!pc.ProcessFormat(SummarizeConsumer(&out))) out += "!";
308 return out;
309 }
310
311 class ParsedFormatTest : public testing::Test {};
312
TEST_F(ParsedFormatTest,ValueSemantics)313 TEST_F(ParsedFormatTest, ValueSemantics) {
314 ParsedFormatBase p1({}, true, {}); // empty format
315 EXPECT_EQ("", SummarizeParsedFormat(p1));
316
317 ParsedFormatBase p2 = p1; // copy construct (empty)
318 EXPECT_EQ(SummarizeParsedFormat(p1), SummarizeParsedFormat(p2));
319
320 p1 = ParsedFormatBase("hello%s", true, {Conv::s}); // move assign
321 EXPECT_EQ("[hello]{s:1$s}", SummarizeParsedFormat(p1));
322
323 ParsedFormatBase p3 = p1; // copy construct (nonempty)
324 EXPECT_EQ(SummarizeParsedFormat(p1), SummarizeParsedFormat(p3));
325
326 using std::swap;
327 swap(p1, p2);
328 EXPECT_EQ("", SummarizeParsedFormat(p1));
329 EXPECT_EQ("[hello]{s:1$s}", SummarizeParsedFormat(p2));
330 swap(p1, p2); // undo
331
332 p2 = p1; // copy assign
333 EXPECT_EQ(SummarizeParsedFormat(p1), SummarizeParsedFormat(p2));
334 }
335
336 struct ExpectParse {
337 const char* in;
338 std::initializer_list<Conv> conv_set;
339 const char* out;
340 };
341
TEST_F(ParsedFormatTest,Parsing)342 TEST_F(ParsedFormatTest, Parsing) {
343 // Parse should be equivalent to that obtained by ConversionParseIterator.
344 // No need to retest the parsing edge cases here.
345 const ExpectParse kExpect[] = {
346 {"", {}, ""},
347 {"ab", {}, "[ab]"},
348 {"a%d", {Conv::d}, "[a]{d:1$d}"},
349 {"a%+d", {Conv::d}, "[a]{+d:1$d}"},
350 {"a% d", {Conv::d}, "[a]{ d:1$d}"},
351 {"a%b %d", {}, "[a]!"}, // stop after error
352 };
353 for (const auto& e : kExpect) {
354 SCOPED_TRACE(e.in);
355 EXPECT_EQ(e.out,
356 SummarizeParsedFormat(ParsedFormatBase(e.in, false, e.conv_set)));
357 }
358 }
359
TEST_F(ParsedFormatTest,ParsingFlagOrder)360 TEST_F(ParsedFormatTest, ParsingFlagOrder) {
361 const ExpectParse kExpect[] = {
362 {"a%+ 0d", {Conv::d}, "[a]{+ 0d:1$d}"},
363 {"a%+0 d", {Conv::d}, "[a]{+0 d:1$d}"},
364 {"a%0+ d", {Conv::d}, "[a]{0+ d:1$d}"},
365 {"a% +0d", {Conv::d}, "[a]{ +0d:1$d}"},
366 {"a%0 +d", {Conv::d}, "[a]{0 +d:1$d}"},
367 {"a% 0+d", {Conv::d}, "[a]{ 0+d:1$d}"},
368 {"a%+ 0+d", {Conv::d}, "[a]{+ 0+d:1$d}"},
369 };
370 for (const auto& e : kExpect) {
371 SCOPED_TRACE(e.in);
372 EXPECT_EQ(e.out,
373 SummarizeParsedFormat(ParsedFormatBase(e.in, false, e.conv_set)));
374 }
375 }
376
377 } // namespace
378 } // namespace str_format_internal
379 } // namespace absl
380