format_ut.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. #include <library/cpp/testing/gtest/gtest.h>
  2. #include <library/cpp/yt/string/format.h>
  3. #include <library/cpp/yt/compact_containers/compact_vector.h>
  4. #include <util/generic/hash_set.h>
  5. #include <limits>
  6. namespace NYT {
  7. namespace {
  8. ////////////////////////////////////////////////////////////////////////////////
  9. struct TWithCustomFlags
  10. {
  11. [[maybe_unused]]
  12. friend void FormatValue(TStringBuilderBase* builder, const TWithCustomFlags&, TStringBuf spec)
  13. {
  14. if (spec.Contains('R')) {
  15. builder->AppendString("R");
  16. }
  17. if (spec.Contains('N')) {
  18. builder->AppendString("N");
  19. }
  20. builder->AppendString("P");
  21. }
  22. };
  23. ////////////////////////////////////////////////////////////////////////////////
  24. } // namespace
  25. ////////////////////////////////////////////////////////////////////////////////
  26. template <>
  27. struct TFormatArg<TWithCustomFlags>
  28. {
  29. [[maybe_unused]] static constexpr std::array ConversionSpecifiers = {
  30. 'v',
  31. };
  32. [[maybe_unused]] static constexpr std::array FlagSpecifiers = {
  33. 'R', 'N',
  34. };
  35. };
  36. ////////////////////////////////////////////////////////////////////////////////
  37. namespace {
  38. // Some compile-time sanity checks.
  39. static_assert(CFormattable<int>);
  40. static_assert(CFormattable<double>);
  41. static_assert(CFormattable<void*>);
  42. static_assert(CFormattable<const char*>);
  43. static_assert(CFormattable<TStringBuf>);
  44. static_assert(CFormattable<TString>);
  45. static_assert(CFormattable<std::span<int>>);
  46. static_assert(CFormattable<std::vector<int>>);
  47. static_assert(CFormattable<std::array<int, 5>>);
  48. // N.B. TCompactVector<int, 1> is not buildable on Windows
  49. static_assert(CFormattable<TCompactVector<int, 2>>);
  50. static_assert(CFormattable<std::set<int>>);
  51. static_assert(CFormattable<std::map<int, int>>);
  52. static_assert(CFormattable<std::multimap<int, int>>);
  53. static_assert(CFormattable<THashSet<int>>);
  54. static_assert(CFormattable<THashMap<int, int>>);
  55. static_assert(CFormattable<THashMultiSet<int>>);
  56. static_assert(CFormattable<TCompactFlatMap<int, int, 2>>);
  57. static_assert(CFormattable<std::pair<int, int>>);
  58. static_assert(CFormattable<std::optional<int>>);
  59. static_assert(CFormattable<TDuration>);
  60. static_assert(CFormattable<TInstant>);
  61. struct TUnformattable
  62. { };
  63. static_assert(!CFormattable<TUnformattable>);
  64. static_assert(!CFormattable<std::variant<TUnformattable>>);
  65. static_assert(CFormattable<TWithCustomFlags>);
  66. ////////////////////////////////////////////////////////////////////////////////
  67. TEST(TFormatTest, Nothing)
  68. {
  69. EXPECT_EQ("abc", Format("a%nb%nc", 1, 2));
  70. }
  71. TEST(TFormatTest, Verbatim)
  72. {
  73. EXPECT_EQ("", Format(""));
  74. EXPECT_EQ("test", Format("test"));
  75. EXPECT_EQ("%", Format("%%"));
  76. EXPECT_EQ("%hello%world%", Format("%%hello%%world%%"));
  77. }
  78. TEST(TFormatTest, MultipleArgs)
  79. {
  80. EXPECT_EQ("2+2=4", Format("%v+%v=%v", 2, 2, 4));
  81. }
  82. TEST(TFormatTest, Strings)
  83. {
  84. EXPECT_EQ("test", Format("%s", "test"));
  85. EXPECT_EQ("test", Format("%s", TStringBuf("test")));
  86. EXPECT_EQ("test", Format("%s", TString("test")));
  87. EXPECT_EQ(" abc", Format("%6s", TString("abc")));
  88. EXPECT_EQ("abc ", Format("%-6s", TString("abc")));
  89. EXPECT_EQ(" abc", Format("%10v", TString("abc")));
  90. EXPECT_EQ("abc ", Format("%-10v", TString("abc")));
  91. EXPECT_EQ("abc", Format("%2s", TString("abc")));
  92. EXPECT_EQ("abc", Format("%-2s", TString("abc")));
  93. EXPECT_EQ("abc", Format("%0s", TString("abc")));
  94. EXPECT_EQ("abc", Format("%-0s", TString("abc")));
  95. EXPECT_EQ(100, std::ssize(Format("%100v", "abc")));
  96. }
  97. TEST(TFormatTest, DecIntegers)
  98. {
  99. EXPECT_EQ("123", Format("%d", 123));
  100. EXPECT_EQ("123", Format("%v", 123));
  101. EXPECT_EQ("042", Format("%03d", 42));
  102. EXPECT_EQ("42", Format("%01d", 42));
  103. EXPECT_EQ("2147483647", Format("%d", std::numeric_limits<i32>::max()));
  104. EXPECT_EQ("-2147483648", Format("%d", std::numeric_limits<i32>::min()));
  105. EXPECT_EQ("0", Format("%u", 0U));
  106. EXPECT_EQ("0", Format("%v", 0U));
  107. EXPECT_EQ("4294967295", Format("%u", std::numeric_limits<ui32>::max()));
  108. EXPECT_EQ("4294967295", Format("%v", std::numeric_limits<ui32>::max()));
  109. EXPECT_EQ("9223372036854775807", Format("%" PRId64, std::numeric_limits<i64>::max()));
  110. EXPECT_EQ("9223372036854775807", Format("%v", std::numeric_limits<i64>::max()));
  111. EXPECT_EQ("-9223372036854775808", Format("%" PRId64, std::numeric_limits<i64>::min()));
  112. EXPECT_EQ("-9223372036854775808", Format("%v", std::numeric_limits<i64>::min()));
  113. EXPECT_EQ("0", Format("%" PRIu64, 0ULL));
  114. EXPECT_EQ("0", Format("%v", 0ULL));
  115. EXPECT_EQ("18446744073709551615", Format("%" PRIu64, std::numeric_limits<ui64>::max()));
  116. EXPECT_EQ("18446744073709551615", Format("%v", std::numeric_limits<ui64>::max()));
  117. }
  118. TEST(TFormatTest, HexIntegers)
  119. {
  120. EXPECT_EQ("7b", Format("%x", 123));
  121. EXPECT_EQ("7B", Format("%X", 123));
  122. EXPECT_EQ("02a", Format("%03x", 42));
  123. EXPECT_EQ("2a", Format("%01x", 42));
  124. EXPECT_EQ("7fffffff", Format("%x", std::numeric_limits<i32>::max()));
  125. EXPECT_EQ("-80000000", Format("%x", std::numeric_limits<i32>::min()));
  126. EXPECT_EQ("0", Format("%x", 0U));
  127. EXPECT_EQ("0", Format("%X", 0U));
  128. EXPECT_EQ("ffffffff", Format("%x", std::numeric_limits<ui32>::max()));
  129. EXPECT_EQ("7fffffffffffffff", Format("%x", std::numeric_limits<i64>::max()));
  130. EXPECT_EQ("-8000000000000000", Format("%x", std::numeric_limits<i64>::min()));
  131. EXPECT_EQ("0", Format("%x", 0ULL));
  132. EXPECT_EQ("ffffffffffffffff", Format("%x", std::numeric_limits<ui64>::max()));
  133. }
  134. TEST(TFormatTest, Floats)
  135. {
  136. EXPECT_EQ("3.14", Format("%.2f", 3.1415F));
  137. EXPECT_EQ("3.14", Format("%.2v", 3.1415F));
  138. EXPECT_EQ("3.14", Format("%.2lf", 3.1415));
  139. EXPECT_EQ("3.14", Format("%.2v", 3.1415));
  140. EXPECT_EQ(TString(std::to_string(std::numeric_limits<double>::max())),
  141. Format("%lF", std::numeric_limits<double>::max()));
  142. }
  143. TEST(TFormatTest, Bool)
  144. {
  145. EXPECT_EQ("True", Format("%v", true));
  146. EXPECT_EQ("False", Format("%v", false));
  147. EXPECT_EQ("true", Format("%lv", true));
  148. EXPECT_EQ("false", Format("%lv", false));
  149. }
  150. TEST(TFormatTest, Quotes)
  151. {
  152. EXPECT_EQ("\"True\"", Format("%Qv", true));
  153. EXPECT_EQ("'False'", Format("%qv", false));
  154. EXPECT_EQ("'\\\'\"'", Format("%qv", "\'\""));
  155. EXPECT_EQ("\"\\x01\"", Format("%Qv", "\x1"));
  156. EXPECT_EQ("'\\x1b'", Format("%qv", '\x1b'));
  157. EXPECT_EQ("'\\\\'", Format("%qv", '\\'));
  158. EXPECT_EQ("'\\n'", Format("%qv", '\n'));
  159. EXPECT_EQ("'\\t'", Format("%qv", '\t'));
  160. EXPECT_EQ("'\\\''", Format("%qv", '\''));
  161. EXPECT_EQ("\"'\"", Format("%Qv", '\''));
  162. EXPECT_EQ("'\"'", Format("%qv", '\"'));
  163. EXPECT_EQ("\"\\\"\"", Format("%Qv", '\"'));
  164. }
  165. TEST(TFormatTest, Escape)
  166. {
  167. EXPECT_EQ("\'\"", Format("%hv", "\'\""));
  168. EXPECT_EQ("\\x01", Format("%hv", "\x1"));
  169. EXPECT_EQ("\\x1b", Format("%hv", '\x1b'));
  170. EXPECT_EQ("\\\\", Format("%hv", '\\'));
  171. EXPECT_EQ("\\n", Format("%hv", '\n'));
  172. EXPECT_EQ("\\t", Format("%hv", '\t'));
  173. EXPECT_EQ("\'", Format("%hv", '\''));
  174. }
  175. TEST(TFormatTest, Nullable)
  176. {
  177. EXPECT_EQ("1", Format("%v", std::make_optional<int>(1)));
  178. EXPECT_EQ("<null>", Format("%v", std::nullopt));
  179. EXPECT_EQ("<null>", Format("%v", std::optional<int>()));
  180. EXPECT_EQ("3.14", Format("%.2f", std::optional<double>(3.1415)));
  181. }
  182. TEST(TFormatTest, Pointers)
  183. {
  184. {
  185. auto ptr = reinterpret_cast<void*>(0x12345678);
  186. EXPECT_EQ("0x12345678", Format("%p", ptr));
  187. EXPECT_EQ("0x12345678", Format("%v", ptr));
  188. EXPECT_EQ("12345678", Format("%x", ptr));
  189. EXPECT_EQ("12345678", Format("%X", ptr));
  190. }
  191. {
  192. auto ptr = reinterpret_cast<void*>(0x12345678abcdefab);
  193. EXPECT_EQ("0x12345678abcdefab", Format("%p", ptr));
  194. EXPECT_EQ("0x12345678abcdefab", Format("%v", ptr));
  195. EXPECT_EQ("12345678abcdefab", Format("%x", ptr));
  196. EXPECT_EQ("12345678ABCDEFAB", Format("%X", ptr));
  197. }
  198. }
  199. TEST(TFormatTest, Tuples)
  200. {
  201. EXPECT_EQ("{}", Format("%v", std::tuple()));
  202. EXPECT_EQ("{1, 2, 3}", Format("%v", std::tuple(1, 2, 3)));
  203. EXPECT_EQ("{1, 2}", Format("%v", std::pair(1, 2)));
  204. }
  205. TEST(TFormatTest, CompactIntervalView)
  206. {
  207. EXPECT_EQ("[]", Format("%v", MakeCompactIntervalView(std::vector<int>{})));
  208. EXPECT_EQ("[1]", Format("%v", MakeCompactIntervalView(std::vector<int>{1})));
  209. EXPECT_EQ("[0, 2-4, 7]", Format("%v", MakeCompactIntervalView(std::vector<int>{0, 2, 3, 4, 7})));
  210. }
  211. TEST(TFormatTest, LazyMultiValueFormatter)
  212. {
  213. int i = 1;
  214. TString s = "hello";
  215. std::vector<int> range{1, 2, 3};
  216. auto lazyFormatter = MakeLazyMultiValueFormatter(
  217. "int: %v, string: %v, range: %v",
  218. i,
  219. s,
  220. MakeFormattableView(range, TDefaultFormatter{}));
  221. EXPECT_EQ("int: 1, string: hello, range: [1, 2, 3]", Format("%v", lazyFormatter));
  222. }
  223. TEST(TFormatTest, VectorArg)
  224. {
  225. std::vector<TString> params = {"a", "b", "c"};
  226. EXPECT_EQ(FormatVector("a is %v, b is %v, c is %v", params), "a is a, b is b, c is c");
  227. }
  228. TEST(TFormatTest, RuntimeFormat)
  229. {
  230. TString format = "Hello %v";
  231. EXPECT_EQ(Format(TRuntimeFormat(format), "World"), "Hello World");
  232. }
  233. TEST(TFormatTest, CustomFlagsSimple)
  234. {
  235. EXPECT_EQ(Format("%v", TWithCustomFlags{}), TString("P"));
  236. EXPECT_EQ(Format("%Rv", TWithCustomFlags{}), TString("RP"));
  237. EXPECT_EQ(Format("%Nv", TWithCustomFlags{}), TString("NP"));
  238. EXPECT_EQ(Format("%RNv", TWithCustomFlags{}), TString("RNP"));
  239. EXPECT_EQ(Format("%NRv", TWithCustomFlags{}), TString("RNP"));
  240. }
  241. TEST(TFormatTest, CustomFlagsCollection)
  242. {
  243. constexpr int elementCount = 5;
  244. auto toCollection = [] (TString pattern) {
  245. TString ret = "[";
  246. for (int i = 0; i < elementCount - 1; ++i) {
  247. ret += pattern + ", ";
  248. }
  249. ret += pattern + "]";
  250. return ret;
  251. };
  252. std::vector vec(elementCount, TWithCustomFlags{});
  253. EXPECT_EQ(Format("%v", vec), toCollection("P"));
  254. EXPECT_EQ(Format("%Rv", vec), toCollection("RP"));
  255. EXPECT_EQ(Format("%Nv", vec), toCollection("NP"));
  256. EXPECT_EQ(Format("%RNv", vec), toCollection("RNP"));
  257. EXPECT_EQ(Format("%NRv", vec), toCollection("RNP"));
  258. }
  259. TEST(TFormatTest, CustomFlagsCollectionTwoLevels)
  260. {
  261. constexpr int elementCount1 = 5;
  262. constexpr int elementCount2 = 3;
  263. auto toCollection = [] (int count, TString pattern) {
  264. TString ret = "[";
  265. for (int i = 0; i < count - 1; ++i) {
  266. ret += pattern + ", ";
  267. }
  268. ret += pattern + "]";
  269. return ret;
  270. };
  271. auto toCollectionD2 = [&] (TString pattern) {
  272. return toCollection(elementCount2, toCollection(elementCount1, std::move(pattern)));
  273. };
  274. std::vector vec(elementCount1, TWithCustomFlags{});
  275. std::array<decltype(vec), elementCount2> arr;
  276. std::ranges::fill(arr, vec);
  277. EXPECT_EQ(Format("%v", arr), toCollectionD2("P"));
  278. EXPECT_EQ(Format("%Rv", arr), toCollectionD2("RP"));
  279. EXPECT_EQ(Format("%Nv", arr), toCollectionD2("NP"));
  280. EXPECT_EQ(Format("%RNv", arr), toCollectionD2("RNP"));
  281. EXPECT_EQ(Format("%NRv", arr), toCollectionD2("RNP"));
  282. }
  283. TEST(TFormatTest, ManyEscapes)
  284. {
  285. EXPECT_EQ("a%b%c%d%e%f%g", Format("%v%%%v%%%v%%%v%%%v%%%v%%%g", "a", "b", "c", "d", "e", "f", "g"));
  286. }
  287. ////////////////////////////////////////////////////////////////////////////////
  288. } // namespace
  289. } // namespace NYT