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