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