error_ut.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779
  1. #include <yt/yt/core/test_framework/framework.h>
  2. #include <library/cpp/yt/error/error.h>
  3. #include <library/cpp/yt/error/error_helpers.h>
  4. #include <util/stream/str.h>
  5. #include <util/string/join.h>
  6. #include <util/string/split.h>
  7. namespace NYT {
  8. namespace {
  9. using namespace NYson;
  10. ////////////////////////////////////////////////////////////////////////////////
  11. class TAdlException
  12. : public std::exception
  13. {
  14. public:
  15. static int ResetCallCount()
  16. {
  17. return std::exchange(OverloadCallCount, 0);
  18. }
  19. const char* what() const noexcept override
  20. {
  21. return "Adl exception";
  22. }
  23. // Simulate overload from TAdlException::operator <<
  24. template <class TLikeThis, class TArg>
  25. requires std::derived_from<std::decay_t<TLikeThis>, TAdlException>
  26. friend TLikeThis&& operator << (TLikeThis&& ex, const TArg& /*other*/)
  27. {
  28. ++OverloadCallCount;
  29. return std::forward<TLikeThis>(ex);
  30. }
  31. private:
  32. static inline int OverloadCallCount = 0;
  33. };
  34. class TAdlArgument
  35. {
  36. public:
  37. static int ResetCallCount()
  38. {
  39. return std::exchange(OverloadCallCount, 0);
  40. }
  41. // Simulate overload TAdlArgument::operator <<
  42. friend TError operator << (TError&& error, const TAdlArgument& /*other*/)
  43. {
  44. static const TErrorAttribute Attr("attr", "attr_value");
  45. ++OverloadCallCount;
  46. return std::move(error) << Attr;
  47. }
  48. friend TError operator << (const TError& error, const TAdlArgument& /*other*/)
  49. {
  50. static const TErrorAttribute Attr("attr", "attr_value");
  51. ++OverloadCallCount;
  52. return error << Attr;
  53. }
  54. private:
  55. static inline int OverloadCallCount = 0;
  56. };
  57. class TWidget
  58. {
  59. public:
  60. TWidget()
  61. {
  62. DefaultConstructorCalls++;
  63. };
  64. TWidget(const TWidget&)
  65. {
  66. CopyConstructorCalls++;
  67. }
  68. TWidget& operator = (const TWidget&) = delete;
  69. TWidget(TWidget&&)
  70. {
  71. MoveConstructorCalls++;
  72. }
  73. TWidget& operator = (TWidget&&) = delete;
  74. static int ResetDefaultCount()
  75. {
  76. return std::exchange(DefaultConstructorCalls, 0);
  77. }
  78. static int ResetCopyCount()
  79. {
  80. return std::exchange(CopyConstructorCalls, 0);
  81. }
  82. static int ResetMoveCount()
  83. {
  84. return std::exchange(MoveConstructorCalls, 0);
  85. }
  86. private:
  87. static inline int DefaultConstructorCalls = 0;
  88. static inline int CopyConstructorCalls = 0;
  89. static inline int MoveConstructorCalls = 0;
  90. };
  91. ////////////////////////////////////////////////////////////////////////////////
  92. template <class TOverloadTest, bool LeftOperandHasUserDefinedOverload = false>
  93. void IterateTestOverEveryRightOperand(TOverloadTest& tester)
  94. {
  95. {
  96. TErrorAttribute attribute("attr", "attr_value");
  97. const auto& attributeRef = attribute;
  98. tester(attributeRef);
  99. }
  100. {
  101. std::vector<TErrorAttribute> attributeVector{{"attr1", "attr_value"}, {"attr2", "attr_value"}};
  102. const auto& attributeVectorRef = attributeVector;
  103. tester(attributeVectorRef);
  104. }
  105. {
  106. TError error("Error");
  107. const auto& errorRef = error;
  108. tester(errorRef);
  109. auto errorCopy = error;
  110. tester(std::move(errorCopy));
  111. if constexpr (!LeftOperandHasUserDefinedOverload) {
  112. EXPECT_TRUE(errorCopy.IsOK());
  113. }
  114. }
  115. {
  116. std::vector<TError> vectorError{TError("Error"), TError("Error")};
  117. const auto& vectorErrorRef = vectorError;
  118. tester(vectorErrorRef);
  119. auto vectorErrorCopy = vectorError;
  120. tester(std::move(vectorErrorCopy));
  121. if constexpr (!LeftOperandHasUserDefinedOverload) {
  122. for (const auto& errorCopy : vectorErrorCopy) {
  123. EXPECT_TRUE(errorCopy.IsOK());
  124. }
  125. }
  126. }
  127. {
  128. TError error("Error");
  129. const auto& attributeDictionaryRef = error.Attributes();
  130. tester(attributeDictionaryRef);
  131. }
  132. {
  133. try {
  134. THROW_ERROR TError("Test error");
  135. } catch(const NYT::TErrorException& ex) {
  136. const auto& exRef = ex;
  137. tester(exRef);
  138. auto exCopy = ex;
  139. tester(std::move(exCopy));
  140. }
  141. }
  142. {
  143. TErrorOr<int> err(std::exception{});
  144. const auto& errRef = err;
  145. tester(errRef);
  146. auto errCopy = err;
  147. tester(std::move(errCopy));
  148. if constexpr (!LeftOperandHasUserDefinedOverload) {
  149. EXPECT_TRUE(errCopy.IsOK());
  150. }
  151. }
  152. {
  153. TAdlArgument adlArg;
  154. const TAdlArgument& adlArgRef = adlArg;
  155. tester(adlArgRef);
  156. if constexpr (!LeftOperandHasUserDefinedOverload) {
  157. EXPECT_EQ(TAdlArgument::ResetCallCount(), 1);
  158. }
  159. }
  160. }
  161. template <class T>
  162. void SetErrorAttribute(TError* error, TString key, const T& value)
  163. {
  164. *error <<= TErrorAttribute(key, value);
  165. }
  166. ////////////////////////////////////////////////////////////////////////////////
  167. TEST(TErrorTest, BitshiftOverloadsExplicitLeftOperand)
  168. {
  169. // TError&& overload.
  170. auto moveTester = [] (auto&& arg) {
  171. TError error = TError("Test error");
  172. TError moved = std::move(error) << std::forward<decltype(arg)>(arg);
  173. EXPECT_TRUE(error.IsOK());
  174. EXPECT_EQ(moved.GetMessage(), "Test error");
  175. };
  176. IterateTestOverEveryRightOperand(moveTester);
  177. // const TError& overloads.
  178. auto copyTester = [] (auto&& arg) {
  179. TError error = TError("Test error");
  180. TError copy = error << std::forward<decltype(arg)>(arg);
  181. EXPECT_EQ(error.GetMessage(), copy.GetMessage());
  182. };
  183. IterateTestOverEveryRightOperand(copyTester);
  184. // Test that TError pr value binds correctly and the call itself is unambiguous.
  185. auto prvalueTester = [] (auto&& arg) {
  186. TError error = TError("Test error") << std::forward<decltype(arg)>(arg);
  187. EXPECT_EQ(error.GetMessage(), "Test error");
  188. };
  189. IterateTestOverEveryRightOperand(prvalueTester);
  190. }
  191. TEST(TErrorTest, BitshiftOverloadsImplicitLeftOperand)
  192. {
  193. // We want to be able to write THROW_ERROR ex
  194. auto throwErrorTester1 = [] (auto&& arg) {
  195. try {
  196. try {
  197. THROW_ERROR TError("Test error");
  198. } catch(const NYT::TErrorException& ex) {
  199. THROW_ERROR ex << std::forward<decltype(arg)>(arg);
  200. }
  201. } catch(const NYT::TErrorException& ex) {
  202. TError error = ex;
  203. EXPECT_EQ(error.GetMessage(), "Test error");
  204. }
  205. };
  206. IterateTestOverEveryRightOperand(throwErrorTester1);
  207. // We also want to be able to write THROW_ERROR TError(smth) without compiler errors
  208. auto throwErrorTester2 = [] (auto&& arg) {
  209. try {
  210. try {
  211. THROW_ERROR TError("Test error");
  212. } catch(const NYT::TErrorException& ex) {
  213. THROW_ERROR TError(ex) << std::forward<decltype(arg)>(arg);
  214. }
  215. } catch(const NYT::TErrorException& ex) {
  216. TError error = ex;
  217. EXPECT_EQ(error.GetMessage(), "Test error");
  218. }
  219. };
  220. IterateTestOverEveryRightOperand(throwErrorTester2);
  221. // Left operand ADL finds the user-defined overload over NYT one.
  222. // In this case AdlException should find templated function
  223. // specialization with perfect match for args over conversions.
  224. auto adlResolutionTester = [] (auto&& arg) {
  225. TAdlException ex;
  226. auto result = ex << std::forward<decltype(arg)>(arg);
  227. static_assert(std::same_as<TAdlException, std::decay_t<decltype(result)>>);
  228. EXPECT_EQ(TAdlException::ResetCallCount(), 1);
  229. };
  230. IterateTestOverEveryRightOperand<
  231. decltype(adlResolutionTester),
  232. /*LeftOperandHasUserDefinedOverload*/ true>(adlResolutionTester);
  233. // Make sure no ambiguous calls.
  234. auto genericErrorOrTester = [] (auto&& arg) {
  235. TErrorOr<int> err(std::exception{});
  236. TError error = err << std::forward<decltype(arg)>(arg);
  237. EXPECT_EQ(error.GetCode(), NYT::EErrorCode::Generic);
  238. };
  239. IterateTestOverEveryRightOperand(genericErrorOrTester);
  240. }
  241. TEST(TErrorTest, Wrap)
  242. {
  243. TError error("Error");
  244. auto wrapped = error.Wrap("Wrapped error");
  245. EXPECT_EQ(wrapped.GetCode(), NYT::EErrorCode::Generic);
  246. EXPECT_EQ(wrapped.GetMessage(), "Wrapped error");
  247. EXPECT_EQ(wrapped.InnerErrors().size(), 1u);
  248. EXPECT_EQ(wrapped.InnerErrors()[0], error);
  249. auto triviallyWrapped = error.Wrap();
  250. EXPECT_EQ(triviallyWrapped, error);
  251. }
  252. TEST(TErrorTest, WrapRValue)
  253. {
  254. TError error("Error");
  255. TError errorCopy = error;
  256. auto wrapped = std::move(errorCopy).Wrap("Wrapped error");
  257. EXPECT_TRUE(errorCopy.IsOK());
  258. EXPECT_EQ(wrapped.GetCode(), NYT::EErrorCode::Generic);
  259. EXPECT_EQ(wrapped.GetMessage(), "Wrapped error");
  260. EXPECT_EQ(wrapped.InnerErrors().size(), 1u);
  261. EXPECT_EQ(wrapped.InnerErrors()[0], error);
  262. TError anotherErrorCopy = error;
  263. auto trviallyWrapped = std::move(anotherErrorCopy).Wrap();
  264. EXPECT_TRUE(anotherErrorCopy.IsOK());
  265. EXPECT_EQ(trviallyWrapped, error);
  266. }
  267. TEST(TErrorTest, ThrowErrorExceptionIfFailedMacroJustWorks)
  268. {
  269. TError error;
  270. EXPECT_NO_THROW(THROW_ERROR_EXCEPTION_IF_FAILED(error, "Outer error"));
  271. error = TError("Real error");
  272. TError errorCopy = error;
  273. try {
  274. THROW_ERROR_EXCEPTION_IF_FAILED(errorCopy, "Outer error");
  275. } catch (const std::exception& ex) {
  276. TError outerError(ex);
  277. EXPECT_TRUE(errorCopy.IsOK());
  278. EXPECT_EQ(outerError.GetMessage(), "Outer error");
  279. EXPECT_EQ(outerError.InnerErrors().size(), 1u);
  280. EXPECT_EQ(outerError.InnerErrors()[0], error);
  281. }
  282. }
  283. TEST(TErrorTest, ThrowErrorExceptionIfFailedMacroExpression)
  284. {
  285. try {
  286. THROW_ERROR_EXCEPTION_IF_FAILED(
  287. TError("Inner error")
  288. << TErrorAttribute("attr", "attr_value"),
  289. "Outer error");
  290. } catch (const std::exception& ex) {
  291. TError outerError(ex);
  292. EXPECT_EQ(outerError.GetMessage(), "Outer error");
  293. EXPECT_EQ(outerError.InnerErrors().size(), 1u);
  294. EXPECT_EQ(outerError.InnerErrors()[0].GetMessage(), "Inner error");
  295. EXPECT_EQ(outerError.InnerErrors()[0].Attributes().Get<TString>("attr"), "attr_value");
  296. }
  297. }
  298. TEST(TErrorTest, ThrowErrorExceptionIfFailedMacroDontStealValue)
  299. {
  300. TErrorOr<TWidget> widget = TWidget();
  301. EXPECT_TRUE(widget.IsOK());
  302. EXPECT_EQ(TWidget::ResetDefaultCount(), 1);
  303. EXPECT_EQ(TWidget::ResetCopyCount(), 0);
  304. EXPECT_EQ(TWidget::ResetMoveCount(), 1);
  305. EXPECT_NO_THROW(THROW_ERROR_EXCEPTION_IF_FAILED(widget));
  306. EXPECT_TRUE(widget.IsOK());
  307. EXPECT_NO_THROW(widget.ValueOrThrow());
  308. EXPECT_EQ(TWidget::ResetDefaultCount(), 0);
  309. EXPECT_EQ(TWidget::ResetCopyCount(), 0);
  310. EXPECT_EQ(TWidget::ResetMoveCount(), 0);
  311. }
  312. TEST(TErrorTest, ThrowErrorExceptionIfFailedMacroDontDupeCalls)
  313. {
  314. EXPECT_NO_THROW(THROW_ERROR_EXCEPTION_IF_FAILED(TErrorOr<TWidget>(TWidget())));
  315. EXPECT_EQ(TWidget::ResetDefaultCount(), 1);
  316. EXPECT_EQ(TWidget::ResetCopyCount(), 0);
  317. EXPECT_EQ(TWidget::ResetMoveCount(), 1);
  318. }
  319. TEST(TErrorTest, ErrorSkeletonStubImplementation)
  320. {
  321. TError error("foo");
  322. EXPECT_THROW(error.GetSkeleton(), std::exception);
  323. }
  324. TEST(TErrorTest, FormatCtor)
  325. {
  326. // EXPECT_EQ("Some error %v", TError("Some error %v").GetMessage()); // No longer compiles due to static analysis.
  327. EXPECT_EQ("Some error hello", TError("Some error %v", "hello").GetMessage());
  328. }
  329. TEST(TErrorTest, FindRecursive)
  330. {
  331. auto inner = TError("Inner")
  332. << TErrorAttribute("inner_attr", 42);
  333. auto error = TError("Error")
  334. << inner
  335. << TErrorAttribute("attr", 8);
  336. auto attr = FindAttribute<int>(error, "attr");
  337. EXPECT_TRUE(attr);
  338. EXPECT_EQ(*attr, 8);
  339. EXPECT_FALSE(FindAttribute<int>(error, "inner_attr"));
  340. auto innerAttr = FindAttributeRecursive<int>(error, "inner_attr");
  341. EXPECT_TRUE(innerAttr);
  342. EXPECT_EQ(*innerAttr, 42);
  343. }
  344. TEST(TErrorTest, TruncateSimple)
  345. {
  346. auto error = TError("Some error")
  347. << TErrorAttribute("my_attr", "Attr value")
  348. << TError("Inner error");
  349. auto truncatedError = error.Truncate();
  350. EXPECT_EQ(error.GetCode(), truncatedError.GetCode());
  351. EXPECT_EQ(error.GetMessage(), truncatedError.GetMessage());
  352. EXPECT_EQ(error.GetPid(), truncatedError.GetPid());
  353. EXPECT_EQ(error.GetTid(), truncatedError.GetTid());
  354. EXPECT_EQ(error.GetDatetime(), truncatedError.GetDatetime());
  355. EXPECT_EQ(error.Attributes().Get<TString>("my_attr"), truncatedError.Attributes().Get<TString>("my_attr"));
  356. EXPECT_EQ(error.InnerErrors().size(), truncatedError.InnerErrors().size());
  357. EXPECT_EQ(error.InnerErrors()[0].GetMessage(), truncatedError.InnerErrors()[0].GetMessage());
  358. }
  359. TEST(TErrorTest, TruncateLarge)
  360. {
  361. auto error = TError("Some long long error")
  362. << TError("First inner error")
  363. << TError("Second inner error")
  364. << TError("Third inner error")
  365. << TError("Fourth inner error");
  366. SetErrorAttribute(&error, "my_attr", "Some long long attr");
  367. auto truncatedError = error.Truncate(/*maxInnerErrorCount*/ 3, /*stringLimit*/ 10);
  368. EXPECT_EQ(error.GetCode(), truncatedError.GetCode());
  369. EXPECT_EQ("Some long ...<message truncated>", truncatedError.GetMessage());
  370. EXPECT_EQ("...<attribute truncated>...", truncatedError.Attributes().Get<TString>("my_attr"));
  371. EXPECT_EQ(truncatedError.InnerErrors().size(), 3u);
  372. EXPECT_EQ("First inne...<message truncated>", truncatedError.InnerErrors()[0].GetMessage());
  373. EXPECT_EQ("Second inn...<message truncated>", truncatedError.InnerErrors()[1].GetMessage());
  374. EXPECT_EQ("Fourth inn...<message truncated>", truncatedError.InnerErrors()[2].GetMessage());
  375. }
  376. TEST(TErrorTest, TruncateSimpleRValue)
  377. {
  378. auto error = TError("Some error")
  379. << TErrorAttribute("my_attr", "Attr value")
  380. << TError("Inner error");
  381. auto errorCopy = error;
  382. auto truncatedError = std::move(errorCopy).Truncate();
  383. EXPECT_TRUE(errorCopy.IsOK());
  384. EXPECT_EQ(error.GetCode(), truncatedError.GetCode());
  385. EXPECT_EQ(error.GetMessage(), truncatedError.GetMessage());
  386. EXPECT_EQ(error.GetPid(), truncatedError.GetPid());
  387. EXPECT_EQ(error.GetTid(), truncatedError.GetTid());
  388. EXPECT_EQ(error.GetDatetime(), truncatedError.GetDatetime());
  389. EXPECT_EQ(error.Attributes().Get<TString>("my_attr"), truncatedError.Attributes().Get<TString>("my_attr"));
  390. EXPECT_EQ(error.InnerErrors().size(), truncatedError.InnerErrors().size());
  391. EXPECT_EQ(error.InnerErrors()[0].GetMessage(), truncatedError.InnerErrors()[0].GetMessage());
  392. }
  393. TEST(TErrorTest, TruncateLargeRValue)
  394. {
  395. auto error = TError("Some long long error")
  396. << TError("First inner error")
  397. << TError("Second inner error")
  398. << TError("Third inner error")
  399. << TError("Fourth inner error");
  400. SetErrorAttribute(&error, "my_attr", "Some long long attr");
  401. auto errorCopy = error;
  402. auto truncatedError = std::move(errorCopy).Truncate(/*maxInnerErrorCount*/ 3, /*stringLimit*/ 10);
  403. EXPECT_TRUE(errorCopy.IsOK());
  404. EXPECT_EQ(error.GetCode(), truncatedError.GetCode());
  405. EXPECT_EQ("Some long ...<message truncated>", truncatedError.GetMessage());
  406. EXPECT_EQ("...<attribute truncated>...", truncatedError.Attributes().Get<TString>("my_attr"));
  407. EXPECT_EQ(truncatedError.InnerErrors().size(), 3u);
  408. EXPECT_EQ("First inne...<message truncated>", truncatedError.InnerErrors()[0].GetMessage());
  409. EXPECT_EQ("Second inn...<message truncated>", truncatedError.InnerErrors()[1].GetMessage());
  410. EXPECT_EQ("Fourth inn...<message truncated>", truncatedError.InnerErrors()[2].GetMessage());
  411. }
  412. TEST(TErrorTest, TruncateConsistentOverloads)
  413. {
  414. auto error = TError("Some long long error")
  415. << TError("First inner error")
  416. << TError("Second inner error")
  417. << TError("Third inner error")
  418. << TError("Fourth inner error");
  419. SetErrorAttribute(&error, "my_attr", "Some long long attr");
  420. auto errorCopy = error;
  421. auto truncatedRValueError = std::move(errorCopy).Truncate(/*maxInnerErrorCount*/ 3, /*stringLimit*/ 10);
  422. auto trunactedLValueError = error.Truncate(/*maxInnerErrorCount*/ 3, /*stringLimit*/ 10);
  423. EXPECT_EQ(truncatedRValueError, trunactedLValueError);
  424. }
  425. TEST(TErrorTest, TruncateWhitelist)
  426. {
  427. auto error = TError("Some error");
  428. SetErrorAttribute(&error, "attr1", "Some long long attr");
  429. SetErrorAttribute(&error, "attr2", "Some long long attr");
  430. THashSet<TStringBuf> myWhitelist = {"attr2"};
  431. auto truncatedError = error.Truncate(2, 10, myWhitelist);
  432. EXPECT_EQ(error.GetCode(), truncatedError.GetCode());
  433. EXPECT_EQ(error.GetMessage(), truncatedError.GetMessage());
  434. EXPECT_EQ("...<attribute truncated>...", truncatedError.Attributes().Get<TString>("attr1"));
  435. EXPECT_EQ("Some long long attr", truncatedError.Attributes().Get<TString>("attr2"));
  436. }
  437. TEST(TErrorTest, TruncateWhitelistRValue)
  438. {
  439. auto error = TError("Some error");
  440. SetErrorAttribute(&error, "attr1", "Some long long attr");
  441. SetErrorAttribute(&error, "attr2", "Some long long attr");
  442. THashSet<TStringBuf> myWhitelist = {"attr2"};
  443. auto errorCopy = error;
  444. auto truncatedError = std::move(errorCopy).Truncate(2, 10, myWhitelist);
  445. EXPECT_TRUE(errorCopy.IsOK());
  446. EXPECT_EQ(error.GetCode(), truncatedError.GetCode());
  447. EXPECT_EQ(error.GetMessage(), truncatedError.GetMessage());
  448. EXPECT_EQ("...<attribute truncated>...", truncatedError.Attributes().Get<TString>("attr1"));
  449. EXPECT_EQ("Some long long attr", truncatedError.Attributes().Get<TString>("attr2"));
  450. }
  451. TEST(TErrorTest, TruncateWhitelistInnerErrors)
  452. {
  453. auto innerError = TError("Inner error");
  454. SetErrorAttribute(&innerError, "attr1", "Some long long attr");
  455. SetErrorAttribute(&innerError, "attr2", "Some long long attr");
  456. auto error = TError("Error") << innerError;
  457. THashSet<TStringBuf> myWhitelist = {"attr2"};
  458. auto truncatedError = error.Truncate(2, 15, myWhitelist);
  459. EXPECT_EQ(truncatedError.InnerErrors().size(), 1u);
  460. auto truncatedInnerError = truncatedError.InnerErrors()[0];
  461. EXPECT_EQ(truncatedInnerError.GetCode(), innerError.GetCode());
  462. EXPECT_EQ(truncatedInnerError.GetMessage(), innerError.GetMessage());
  463. EXPECT_EQ("...<attribute truncated>...", truncatedInnerError.Attributes().Get<TString>("attr1"));
  464. EXPECT_EQ("Some long long attr", truncatedInnerError.Attributes().Get<TString>("attr2"));
  465. }
  466. TEST(TErrorTest, TruncateWhitelistInnerErrorsRValue)
  467. {
  468. auto innerError = TError("Inner error");
  469. SetErrorAttribute(&innerError, "attr1", "Some long long attr");
  470. SetErrorAttribute(&innerError, "attr2", "Some long long attr");
  471. auto error = TError("Error") << innerError;
  472. THashSet<TStringBuf> myWhitelist = {"attr2"};
  473. auto errorCopy = error;
  474. auto truncatedError = std::move(errorCopy).Truncate(2, 15, myWhitelist);
  475. EXPECT_TRUE(errorCopy.IsOK());
  476. EXPECT_EQ(truncatedError.InnerErrors().size(), 1u);
  477. auto truncatedInnerError = truncatedError.InnerErrors()[0];
  478. EXPECT_EQ(truncatedInnerError.GetCode(), innerError.GetCode());
  479. EXPECT_EQ(truncatedInnerError.GetMessage(), innerError.GetMessage());
  480. EXPECT_EQ("...<attribute truncated>...", truncatedInnerError.Attributes().Get<TString>("attr1"));
  481. EXPECT_EQ("Some long long attr", truncatedInnerError.Attributes().Get<TString>("attr2"));
  482. }
  483. TEST(TErrorTest, TruncateWhitelistSaveInnerError)
  484. {
  485. auto genericInner = TError("GenericInner");
  486. auto whitelistedInner = TError("Inner")
  487. << TErrorAttribute("whitelisted_key", 42);
  488. auto error = TError("Error")
  489. << (genericInner << TErrorAttribute("foo", "bar"))
  490. << whitelistedInner
  491. << genericInner;
  492. error = std::move(error).Truncate(1, 20, {
  493. "whitelisted_key"
  494. });
  495. EXPECT_TRUE(!error.IsOK());
  496. EXPECT_EQ(error.InnerErrors().size(), 2u);
  497. EXPECT_EQ(error.InnerErrors()[0], whitelistedInner);
  498. EXPECT_EQ(error.InnerErrors()[1], genericInner);
  499. // TODO: error_helpers???
  500. EXPECT_TRUE(FindAttributeRecursive<int>(error, "whitelisted_key"));
  501. EXPECT_FALSE(FindAttributeRecursive<int>(error, "foo"));
  502. }
  503. TEST(TErrorTest, YTExceptionToError)
  504. {
  505. try {
  506. throw TSimpleException("message");
  507. } catch (const std::exception& ex) {
  508. TError error(ex);
  509. EXPECT_EQ(NYT::EErrorCode::Generic, error.GetCode());
  510. EXPECT_EQ("message", error.GetMessage());
  511. }
  512. }
  513. TEST(TErrorTest, CompositeYTExceptionToError)
  514. {
  515. try {
  516. try {
  517. throw TSimpleException("inner message");
  518. } catch (const std::exception& ex) {
  519. throw TSimpleException(ex, "outer message");
  520. }
  521. } catch (const std::exception& ex) {
  522. TError outerError(ex);
  523. EXPECT_EQ(NYT::EErrorCode::Generic, outerError.GetCode());
  524. EXPECT_EQ("outer message", outerError.GetMessage());
  525. EXPECT_EQ(1, std::ssize(outerError.InnerErrors()));
  526. const auto& innerError = outerError.InnerErrors()[0];
  527. EXPECT_EQ(NYT::EErrorCode::Generic, innerError.GetCode());
  528. EXPECT_EQ("inner message", innerError.GetMessage());
  529. }
  530. }
  531. TEST(TErrorTest, YTExceptionWithAttributesToError)
  532. {
  533. try {
  534. throw TSimpleException("message")
  535. << TExceptionAttribute{"Int64 value", static_cast<i64>(42)}
  536. << TExceptionAttribute{"double value", 7.77}
  537. << TExceptionAttribute{"bool value", false}
  538. << TExceptionAttribute{"String value", "FooBar"};
  539. } catch (const std::exception& ex) {
  540. TError error(ex);
  541. EXPECT_EQ(NYT::EErrorCode::Generic, error.GetCode());
  542. EXPECT_EQ("message", error.GetMessage());
  543. auto i64value = error.Attributes().Find<i64>("Int64 value");
  544. EXPECT_TRUE(i64value);
  545. EXPECT_EQ(*i64value, static_cast<i64>(42));
  546. auto doubleValue = error.Attributes().Find<double>("double value");
  547. EXPECT_TRUE(doubleValue);
  548. EXPECT_EQ(*doubleValue, 7.77);
  549. auto boolValue = error.Attributes().Find<bool>("bool value");
  550. EXPECT_TRUE(boolValue);
  551. EXPECT_EQ(*boolValue, false);
  552. auto stringValue = error.Attributes().Find<TString>("String value");
  553. EXPECT_TRUE(stringValue);
  554. EXPECT_EQ(*stringValue, "FooBar");
  555. }
  556. }
  557. TEST(TErrorTest, AttributeSerialization)
  558. {
  559. auto getWeededText = [] (const TError& err) {
  560. std::vector<TString> lines;
  561. for (const auto& line : StringSplitter(ToString(err)).Split('\n')) {
  562. if (!line.Contains("origin") && !line.Contains("datetime")) {
  563. lines.push_back(TString{line});
  564. }
  565. }
  566. return JoinSeq("\n", lines);
  567. };
  568. EXPECT_EQ(getWeededText(TError("E1") << TErrorAttribute("A1", "V1")), TString(
  569. "E1\n"
  570. " A1 V1\n"));
  571. EXPECT_EQ(getWeededText(TError("E1") << TErrorAttribute("A1", "L1\nL2\nL3")), TString(
  572. "E1\n"
  573. " A1\n"
  574. " L1\n"
  575. " L2\n"
  576. " L3\n"));
  577. }
  578. TEST(TErrorTest, MacroStaticAnalysis)
  579. {
  580. auto swallow = [] (auto expr) {
  581. try {
  582. expr();
  583. } catch (...) {
  584. }
  585. };
  586. swallow([] {
  587. THROW_ERROR_EXCEPTION("Foo");
  588. });
  589. swallow([] {
  590. THROW_ERROR_EXCEPTION("Hello, %v", "World");
  591. });
  592. swallow([] {
  593. THROW_ERROR_EXCEPTION(NYT::EErrorCode::Generic, "Foo");
  594. });
  595. swallow([] {
  596. THROW_ERROR_EXCEPTION(NYT::EErrorCode::Generic, "Foo%v", "Bar");
  597. });
  598. swallow([] {
  599. THROW_ERROR_EXCEPTION(NYT::EErrorCode::Generic, "Foo%v%v", "Bar", "Baz");
  600. });
  601. swallow([] {
  602. THROW_ERROR_EXCEPTION_IF_FAILED(TError{}, "Foo");
  603. });
  604. swallow([] {
  605. THROW_ERROR_EXCEPTION_IF_FAILED(TError{}, "Foo%v", "Bar");
  606. });
  607. swallow([] {
  608. THROW_ERROR_EXCEPTION_IF_FAILED(TError{}, "Foo%v%v", "Bar", "Baz");
  609. });
  610. swallow([] {
  611. THROW_ERROR_EXCEPTION_IF_FAILED(TError{}, NYT::EErrorCode::Generic, "Foo%v", "Bar");
  612. });
  613. swallow([] {
  614. THROW_ERROR_EXCEPTION_IF_FAILED(TError{}, NYT::EErrorCode::Generic, "Foo%v%v", "Bar", "Baz");
  615. });
  616. }
  617. TEST(TErrorTest, WrapStaticAnalysis)
  618. {
  619. TError error;
  620. Y_UNUSED(error.Wrap());
  621. Y_UNUSED(error.Wrap(std::exception{}));
  622. Y_UNUSED(error.Wrap("Hello"));
  623. Y_UNUSED(error.Wrap("Hello, %v", "World"));
  624. Y_UNUSED(error.Wrap(TRuntimeFormat{"Hello, %v"}));
  625. }
  626. // NB(arkady-e1ppa): Uncomment these occasionally to see
  627. // that static analysis is still working.
  628. TEST(TErrorTest, MacroStaticAnalysisBrokenFormat)
  629. {
  630. // auto swallow = [] (auto expr) {
  631. // try {
  632. // expr();
  633. // } catch (...) {
  634. // }
  635. // };
  636. // swallow([] {
  637. // THROW_ERROR_EXCEPTION("Hello, %v");
  638. // });
  639. // swallow([] {
  640. // THROW_ERROR_EXCEPTION(TErrorCode{}, "Foo%v");
  641. // });
  642. // swallow([] {
  643. // THROW_ERROR_EXCEPTION_IF_FAILED(TError{}, "Foo%v");
  644. // });
  645. // swallow([] {
  646. // THROW_ERROR_EXCEPTION_IF_FAILED(TError{}, TErrorCode{}, "Foo%v");
  647. // });
  648. }
  649. ////////////////////////////////////////////////////////////////////////////////
  650. } // namespace
  651. } // namespace NYT