parser_test.cc (14373B)
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 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 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: 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 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 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 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 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 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 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 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 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; 346 explicit SummarizeConsumer(std::string* out) : out(out) {} 347 348 bool Append(string_view s) { 349 *out += "[" + std::string(s) + "]"; 350 return true; 351 } 352 353 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 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 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 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 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