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