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