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