• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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