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__, "v",    "v",    ""  },  // basic
114     {__LINE__, "d ",   "d",    " " },  // leave suffix
115     {__LINE__, "dd",   "d",    "d" },  // don't be greedy
116     {__LINE__, "d9",   "d",    "9" },  // leave non-space suffix
117     {__LINE__, "dzz",  "d",    "zz"},  // length mod as suffix
118     {__LINE__, "3v",   "",     "3v"},  // 'v' cannot have modifiers
119     {__LINE__, "hv",   "",     "hv"},  // 'v' cannot have modifiers
120     {__LINE__, "1$v",   "1$v",     ""},  // 'v' can have use posix syntax
121     {__LINE__, "1$*2$d", "1$*2$d", ""  },  // arg indexing and * allowed.
122     {__LINE__, "0-14.3hhd", "0-14.3hhd", ""},  // precision, width
123     {__LINE__, " 0-+#14.3hhd", " 0-+#14.3hhd", ""},  // flags
124   };
125   for (const auto& e : kExpect) {
126     SCOPED_TRACE(e.line);
127     EXPECT_THAT(Consume(e.src), Pair(e.out, e.src_post));
128   }
129 }
130 
TEST_F(ConsumeUnboundConversionTest,BasicConversion)131 TEST_F(ConsumeUnboundConversionTest, BasicConversion) {
132   EXPECT_FALSE(Run(""));
133   EXPECT_FALSE(Run("z"));
134 
135   EXPECT_FALSE(Run("dd"));  // no excess allowed
136 
137   EXPECT_TRUE(Run("d"));
138   EXPECT_EQ('d', FormatConversionCharToChar(o.conv));
139   EXPECT_FALSE(o.width.is_from_arg());
140   EXPECT_LT(o.width.value(), 0);
141   EXPECT_FALSE(o.precision.is_from_arg());
142   EXPECT_LT(o.precision.value(), 0);
143   EXPECT_EQ(1, o.arg_position);
144 }
145 
TEST_F(ConsumeUnboundConversionTest,ArgPosition)146 TEST_F(ConsumeUnboundConversionTest, ArgPosition) {
147   EXPECT_TRUE(Run("d"));
148   EXPECT_EQ(1, o.arg_position);
149   EXPECT_TRUE(Run("3$d"));
150   EXPECT_EQ(3, o.arg_position);
151   EXPECT_TRUE(Run("1$d"));
152   EXPECT_EQ(1, o.arg_position);
153   EXPECT_TRUE(Run("1$d", true));
154   EXPECT_EQ(1, o.arg_position);
155   EXPECT_TRUE(Run("123$d"));
156   EXPECT_EQ(123, o.arg_position);
157   EXPECT_TRUE(Run("123$d", true));
158   EXPECT_EQ(123, o.arg_position);
159   EXPECT_TRUE(Run("10$d"));
160   EXPECT_EQ(10, o.arg_position);
161   EXPECT_TRUE(Run("10$d", true));
162   EXPECT_EQ(10, o.arg_position);
163 
164   // Position can't be zero.
165   EXPECT_FALSE(Run("0$d"));
166   EXPECT_FALSE(Run("0$d", true));
167   EXPECT_FALSE(Run("1$*0$d"));
168   EXPECT_FALSE(Run("1$.*0$d"));
169 
170   // Position can't start with a zero digit at all. That is not a 'decimal'.
171   EXPECT_FALSE(Run("01$p"));
172   EXPECT_FALSE(Run("01$p", true));
173   EXPECT_FALSE(Run("1$*01$p"));
174   EXPECT_FALSE(Run("1$.*01$p"));
175 }
176 
TEST_F(ConsumeUnboundConversionTest,WidthAndPrecision)177 TEST_F(ConsumeUnboundConversionTest, WidthAndPrecision) {
178   EXPECT_TRUE(Run("14d"));
179   EXPECT_EQ('d', FormatConversionCharToChar(o.conv));
180   EXPECT_FALSE(o.width.is_from_arg());
181   EXPECT_EQ(14, o.width.value());
182   EXPECT_FALSE(o.precision.is_from_arg());
183   EXPECT_LT(o.precision.value(), 0);
184 
185   EXPECT_TRUE(Run("14.d"));
186   EXPECT_FALSE(o.width.is_from_arg());
187   EXPECT_FALSE(o.precision.is_from_arg());
188   EXPECT_EQ(14, o.width.value());
189   EXPECT_EQ(0, o.precision.value());
190 
191   EXPECT_TRUE(Run(".d"));
192   EXPECT_FALSE(o.width.is_from_arg());
193   EXPECT_LT(o.width.value(), 0);
194   EXPECT_FALSE(o.precision.is_from_arg());
195   EXPECT_EQ(0, o.precision.value());
196 
197   EXPECT_TRUE(Run(".5d"));
198   EXPECT_FALSE(o.width.is_from_arg());
199   EXPECT_LT(o.width.value(), 0);
200   EXPECT_FALSE(o.precision.is_from_arg());
201   EXPECT_EQ(5, o.precision.value());
202 
203   EXPECT_TRUE(Run(".0d"));
204   EXPECT_FALSE(o.width.is_from_arg());
205   EXPECT_LT(o.width.value(), 0);
206   EXPECT_FALSE(o.precision.is_from_arg());
207   EXPECT_EQ(0, o.precision.value());
208 
209   EXPECT_TRUE(Run("14.5d"));
210   EXPECT_FALSE(o.width.is_from_arg());
211   EXPECT_FALSE(o.precision.is_from_arg());
212   EXPECT_EQ(14, o.width.value());
213   EXPECT_EQ(5, o.precision.value());
214 
215   EXPECT_TRUE(Run("*.*d"));
216   EXPECT_TRUE(o.width.is_from_arg());
217   EXPECT_EQ(1, o.width.get_from_arg());
218   EXPECT_TRUE(o.precision.is_from_arg());
219   EXPECT_EQ(2, o.precision.get_from_arg());
220   EXPECT_EQ(3, o.arg_position);
221 
222   EXPECT_TRUE(Run("*d"));
223   EXPECT_TRUE(o.width.is_from_arg());
224   EXPECT_EQ(1, o.width.get_from_arg());
225   EXPECT_FALSE(o.precision.is_from_arg());
226   EXPECT_LT(o.precision.value(), 0);
227   EXPECT_EQ(2, o.arg_position);
228 
229   EXPECT_TRUE(Run(".*d"));
230   EXPECT_FALSE(o.width.is_from_arg());
231   EXPECT_LT(o.width.value(), 0);
232   EXPECT_TRUE(o.precision.is_from_arg());
233   EXPECT_EQ(1, o.precision.get_from_arg());
234   EXPECT_EQ(2, o.arg_position);
235 
236   // mixed implicit and explicit: didn't specify arg position.
237   EXPECT_FALSE(Run("*23$.*34$d"));
238 
239   EXPECT_TRUE(Run("12$*23$.*34$d"));
240   EXPECT_EQ(12, o.arg_position);
241   EXPECT_TRUE(o.width.is_from_arg());
242   EXPECT_EQ(23, o.width.get_from_arg());
243   EXPECT_TRUE(o.precision.is_from_arg());
244   EXPECT_EQ(34, o.precision.get_from_arg());
245 
246   EXPECT_TRUE(Run("2$*5$.*9$d"));
247   EXPECT_EQ(2, o.arg_position);
248   EXPECT_TRUE(o.width.is_from_arg());
249   EXPECT_EQ(5, o.width.get_from_arg());
250   EXPECT_TRUE(o.precision.is_from_arg());
251   EXPECT_EQ(9, o.precision.get_from_arg());
252 
253   EXPECT_FALSE(Run(".*0$d")) << "no arg 0";
254 
255   // Large values
256   EXPECT_TRUE(Run("999999999.999999999d"));
257   EXPECT_FALSE(o.width.is_from_arg());
258   EXPECT_EQ(999999999, o.width.value());
259   EXPECT_FALSE(o.precision.is_from_arg());
260   EXPECT_EQ(999999999, o.precision.value());
261 
262   EXPECT_FALSE(Run("1000000000.999999999d"));
263   EXPECT_FALSE(Run("999999999.1000000000d"));
264   EXPECT_FALSE(Run("9999999999d"));
265   EXPECT_FALSE(Run(".9999999999d"));
266 }
267 
TEST_F(ConsumeUnboundConversionTest,Flags)268 TEST_F(ConsumeUnboundConversionTest, Flags) {
269   static const char kAllFlags[] = "-+ #0";
270   static const int kNumFlags = ABSL_ARRAYSIZE(kAllFlags) - 1;
271   for (int rev = 0; rev < 2; ++rev) {
272     for (int i = 0; i < 1 << kNumFlags; ++i) {
273       std::string fmt;
274       for (int k = 0; k < kNumFlags; ++k)
275         if ((i >> k) & 1) fmt += kAllFlags[k];
276       // flag order shouldn't matter
277       if (rev == 1) {
278         std::reverse(fmt.begin(), fmt.end());
279       }
280       fmt += 'd';
281       SCOPED_TRACE(fmt);
282       EXPECT_TRUE(Run(fmt.c_str()));
283       EXPECT_EQ(fmt.find('-') == std::string::npos,
284                 !FlagsContains(o.flags, Flags::kLeft));
285       EXPECT_EQ(fmt.find('+') == std::string::npos,
286                 !FlagsContains(o.flags, Flags::kShowPos));
287       EXPECT_EQ(fmt.find(' ') == std::string::npos,
288                 !FlagsContains(o.flags, Flags::kSignCol));
289       EXPECT_EQ(fmt.find('#') == std::string::npos,
290                 !FlagsContains(o.flags, Flags::kAlt));
291       EXPECT_EQ(fmt.find('0') == std::string::npos,
292                 !FlagsContains(o.flags, Flags::kZero));
293     }
294   }
295 }
296 
TEST_F(ConsumeUnboundConversionTest,BasicFlag)297 TEST_F(ConsumeUnboundConversionTest, BasicFlag) {
298   // Flag is on
299   for (const char* fmt : {"d", "llx", "G", "1$X"}) {
300     SCOPED_TRACE(fmt);
301     EXPECT_TRUE(Run(fmt));
302     EXPECT_EQ(o.flags, Flags::kBasic);
303   }
304 
305   // Flag is off
306   for (const char* fmt : {"3d", ".llx", "-G", "1$#X"}) {
307     SCOPED_TRACE(fmt);
308     EXPECT_TRUE(Run(fmt));
309     EXPECT_NE(o.flags, Flags::kBasic);
310   }
311 }
312 
TEST_F(ConsumeUnboundConversionTest,LengthMod)313 TEST_F(ConsumeUnboundConversionTest, LengthMod) {
314   EXPECT_TRUE(Run("d"));
315   EXPECT_EQ(LengthMod::none, o.length_mod);
316   EXPECT_TRUE(Run("hd"));
317   EXPECT_EQ(LengthMod::h, o.length_mod);
318   EXPECT_TRUE(Run("hhd"));
319   EXPECT_EQ(LengthMod::hh, o.length_mod);
320   EXPECT_TRUE(Run("ld"));
321   EXPECT_EQ(LengthMod::l, o.length_mod);
322   EXPECT_TRUE(Run("lld"));
323   EXPECT_EQ(LengthMod::ll, o.length_mod);
324   EXPECT_TRUE(Run("Lf"));
325   EXPECT_EQ(LengthMod::L, o.length_mod);
326   EXPECT_TRUE(Run("qf"));
327   EXPECT_EQ(LengthMod::q, o.length_mod);
328   EXPECT_TRUE(Run("jd"));
329   EXPECT_EQ(LengthMod::j, o.length_mod);
330   EXPECT_TRUE(Run("zd"));
331   EXPECT_EQ(LengthMod::z, o.length_mod);
332   EXPECT_TRUE(Run("td"));
333   EXPECT_EQ(LengthMod::t, o.length_mod);
334 }
335 
336 struct SummarizeConsumer {
337   std::string* out;
SummarizeConsumerabsl::str_format_internal::__anon47b269420111::SummarizeConsumer338   explicit SummarizeConsumer(std::string* out) : out(out) {}
339 
Appendabsl::str_format_internal::__anon47b269420111::SummarizeConsumer340   bool Append(string_view s) {
341     *out += "[" + std::string(s) + "]";
342     return true;
343   }
344 
ConvertOneabsl::str_format_internal::__anon47b269420111::SummarizeConsumer345   bool ConvertOne(const UnboundConversion& conv, string_view s) {
346     *out += "{";
347     *out += std::string(s);
348     *out += ":";
349     *out += std::to_string(conv.arg_position) + "$";
350     if (conv.width.is_from_arg()) {
351       *out += std::to_string(conv.width.get_from_arg()) + "$*";
352     }
353     if (conv.precision.is_from_arg()) {
354       *out += "." + std::to_string(conv.precision.get_from_arg()) + "$*";
355     }
356     *out += FormatConversionCharToChar(conv.conv);
357     *out += "}";
358     return true;
359   }
360 };
361 
SummarizeParsedFormat(const ParsedFormatBase & pc)362 std::string SummarizeParsedFormat(const ParsedFormatBase& pc) {
363   std::string out;
364   if (!pc.ProcessFormat(SummarizeConsumer(&out))) out += "!";
365   return out;
366 }
367 
368 class ParsedFormatTest : public testing::Test {};
369 
TEST_F(ParsedFormatTest,ValueSemantics)370 TEST_F(ParsedFormatTest, ValueSemantics) {
371   ParsedFormatBase p1({}, true, {});  // empty format
372   EXPECT_EQ("", SummarizeParsedFormat(p1));
373 
374   ParsedFormatBase p2 = p1;  // copy construct (empty)
375   EXPECT_EQ(SummarizeParsedFormat(p1), SummarizeParsedFormat(p2));
376 
377   p1 = ParsedFormatBase("hello%s", true,
378                         {FormatConversionCharSetInternal::s});  // move assign
379   EXPECT_EQ("[hello]{s:1$s}", SummarizeParsedFormat(p1));
380 
381   ParsedFormatBase p3 = p1;  // copy construct (nonempty)
382   EXPECT_EQ(SummarizeParsedFormat(p1), SummarizeParsedFormat(p3));
383 
384   using std::swap;
385   swap(p1, p2);
386   EXPECT_EQ("", SummarizeParsedFormat(p1));
387   EXPECT_EQ("[hello]{s:1$s}", SummarizeParsedFormat(p2));
388   swap(p1, p2);  // undo
389 
390   p2 = p1;  // copy assign
391   EXPECT_EQ(SummarizeParsedFormat(p1), SummarizeParsedFormat(p2));
392 }
393 
394 struct ExpectParse {
395   const char* in;
396   std::initializer_list<FormatConversionCharSet> conv_set;
397   const char* out;
398 };
399 
TEST_F(ParsedFormatTest,Parsing)400 TEST_F(ParsedFormatTest, Parsing) {
401   // Parse should be equivalent to that obtained by ConversionParseIterator.
402   // No need to retest the parsing edge cases here.
403   const ExpectParse kExpect[] = {
404       {"", {}, ""},
405       {"ab", {}, "[ab]"},
406       {"a%d", {FormatConversionCharSetInternal::d}, "[a]{d:1$d}"},
407       {"a%+d", {FormatConversionCharSetInternal::d}, "[a]{+d:1$d}"},
408       {"a% d", {FormatConversionCharSetInternal::d}, "[a]{ d:1$d}"},
409       {"a%b %d", {}, "[a]!"},  // stop after error
410   };
411   for (const auto& e : kExpect) {
412     SCOPED_TRACE(e.in);
413     EXPECT_EQ(e.out,
414               SummarizeParsedFormat(ParsedFormatBase(e.in, false, e.conv_set)));
415   }
416 }
417 
TEST_F(ParsedFormatTest,ParsingFlagOrder)418 TEST_F(ParsedFormatTest, ParsingFlagOrder) {
419   const ExpectParse kExpect[] = {
420       {"a%+ 0d", {FormatConversionCharSetInternal::d}, "[a]{+ 0d: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       {"a% +0d", {FormatConversionCharSetInternal::d}, "[a]{ +0d:1$d}"},
424       {"a%0 +d", {FormatConversionCharSetInternal::d}, "[a]{0 +d:1$d}"},
425       {"a% 0+d", {FormatConversionCharSetInternal::d}, "[a]{ 0+d:1$d}"},
426       {"a%+   0+d", {FormatConversionCharSetInternal::d}, "[a]{+   0+d:1$d}"},
427   };
428   for (const auto& e : kExpect) {
429     SCOPED_TRACE(e.in);
430     EXPECT_EQ(e.out,
431               SummarizeParsedFormat(ParsedFormatBase(e.in, false, e.conv_set)));
432   }
433 }
434 
435 }  // namespace
436 }  // namespace str_format_internal
437 ABSL_NAMESPACE_END
438 }  // namespace absl
439