yexception_ut.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. #include "yexception.h"
  2. static inline void Throw1DontMove() {
  3. ythrow yexception() << "blabla"; // don't move this line
  4. }
  5. static inline void Throw2DontMove() {
  6. ythrow yexception() << 1 << " qw " << 12.1; // don't move this line
  7. }
  8. #include <library/cpp/testing/unittest/registar.h>
  9. #include <util/generic/algorithm.h>
  10. #include <util/memory/tempbuf.h>
  11. #include <util/random/mersenne.h>
  12. #include <util/stream/output.h>
  13. #include <util/string/subst.h>
  14. #include <util/string/split.h>
  15. #include "yexception_ut.h"
  16. #include "bt_exception.h"
  17. #if defined(_MSC_VER)
  18. #pragma warning(disable : 4702) /*unreachable code*/
  19. #endif
  20. static void CallbackFun(int i) {
  21. throw i;
  22. }
  23. static IOutputStream* OUTS = nullptr;
  24. namespace NOuter::NInner {
  25. void Compare10And20() {
  26. Y_ENSURE(10 > 20);
  27. }
  28. }
  29. class TExceptionTest: public TTestBase {
  30. UNIT_TEST_SUITE(TExceptionTest);
  31. UNIT_TEST_EXCEPTION(TestException, yexception)
  32. UNIT_TEST_EXCEPTION(TestLineInfo, yexception)
  33. UNIT_TEST(TestCurrentExceptionMessageWhenThereisNoException)
  34. UNIT_TEST(TestFormat1)
  35. UNIT_TEST(TestRaise1)
  36. UNIT_TEST(TestVirtuality)
  37. UNIT_TEST(TestVirtualInheritance)
  38. UNIT_TEST(TestMixedCode)
  39. UNIT_TEST(TestBackTrace)
  40. UNIT_TEST(TestEnsureWithBackTrace1)
  41. UNIT_TEST(TestEnsureWithBackTrace2)
  42. #ifdef _YNDX_LIBUNWIND_ENABLE_EXCEPTION_BACKTRACE
  43. UNIT_TEST(TestFormatCurrentException)
  44. #endif
  45. UNIT_TEST(TestFormatCurrentExceptionWithNoException)
  46. #ifdef _YNDX_LIBUNWIND_ENABLE_EXCEPTION_BACKTRACE
  47. UNIT_TEST(TestFormatCurrentExceptionWithInvalidBacktraceFormatter)
  48. #endif
  49. UNIT_TEST(TestRethrowAppend)
  50. UNIT_TEST(TestMacroOverload)
  51. UNIT_TEST(TestMessageCrop)
  52. UNIT_TEST(TestTIoSystemErrorSpecialMethods)
  53. UNIT_TEST(TestCurrentExceptionTypeNameMethod)
  54. UNIT_TEST_SUITE_END();
  55. private:
  56. inline void TestRethrowAppend() {
  57. try {
  58. try {
  59. ythrow yexception() << "it";
  60. } catch (yexception& e) {
  61. e << "happens";
  62. throw;
  63. }
  64. } catch (...) {
  65. UNIT_ASSERT(CurrentExceptionMessage().Contains("ithappens"));
  66. }
  67. }
  68. inline void TestCurrentExceptionMessageWhenThereisNoException() {
  69. UNIT_ASSERT(CurrentExceptionMessage() == "(NO EXCEPTION)");
  70. }
  71. inline void TestBackTrace() {
  72. try {
  73. ythrow TWithBackTrace<TIoSystemError>() << "test";
  74. } catch (...) {
  75. UNIT_ASSERT(CurrentExceptionMessage().find('\n') != TString::npos);
  76. return;
  77. }
  78. UNIT_ASSERT(false);
  79. }
  80. template <typename TException>
  81. static void EnsureCurrentExceptionHasBackTrace() {
  82. auto exceptionPtr = std::current_exception();
  83. UNIT_ASSERT_C(exceptionPtr != nullptr, "No exception");
  84. try {
  85. std::rethrow_exception(exceptionPtr);
  86. } catch (const TException& e) {
  87. const TBackTrace* bt = e.BackTrace();
  88. UNIT_ASSERT(bt != nullptr);
  89. } catch (...) {
  90. UNIT_ASSERT_C(false, "Unexpected exception type");
  91. }
  92. }
  93. inline void TestEnsureWithBackTrace1() {
  94. try {
  95. Y_ENSURE_BT(4 > 6);
  96. } catch (...) {
  97. const TString msg = CurrentExceptionMessage();
  98. UNIT_ASSERT(msg.Contains("4 > 6"));
  99. UNIT_ASSERT(msg.Contains("\n"));
  100. EnsureCurrentExceptionHasBackTrace<yexception>();
  101. return;
  102. }
  103. UNIT_ASSERT(false);
  104. }
  105. inline void TestEnsureWithBackTrace2() {
  106. try {
  107. Y_ENSURE_BT(4 > 6, "custom "
  108. << "message");
  109. } catch (...) {
  110. const TString msg = CurrentExceptionMessage();
  111. UNIT_ASSERT(!msg.Contains("4 > 6"));
  112. UNIT_ASSERT(msg.Contains("custom message"));
  113. UNIT_ASSERT(msg.Contains("\n"));
  114. EnsureCurrentExceptionHasBackTrace<yexception>();
  115. return;
  116. }
  117. UNIT_ASSERT(false);
  118. }
  119. // TODO(svkrasnov): the output should be canonized after https://st.yandex-team.ru/YMAKE-103
  120. #ifdef _YNDX_LIBUNWIND_ENABLE_EXCEPTION_BACKTRACE
  121. void TestFormatCurrentException() {
  122. try {
  123. throw std::logic_error("some exception"); // is instance of std::exception
  124. UNIT_ASSERT(false);
  125. } catch (...) {
  126. TString exceptionMessage = FormatCurrentException();
  127. UNIT_ASSERT(exceptionMessage.Contains("(std::logic_error) some exception"));
  128. TVector<TString> backtraceStrs = StringSplitter(exceptionMessage).Split('\n');
  129. UNIT_ASSERT(backtraceStrs.size() > 1);
  130. }
  131. }
  132. #endif
  133. void TestFormatCurrentExceptionWithNoException() {
  134. UNIT_ASSERT_VALUES_EQUAL(FormatCurrentException(), "(NO EXCEPTION)\n");
  135. }
  136. #ifdef _YNDX_LIBUNWIND_ENABLE_EXCEPTION_BACKTRACE
  137. void TestFormatCurrentExceptionWithInvalidBacktraceFormatter() {
  138. auto invalidFormatter = [](IOutputStream*, void* const*, size_t) {
  139. Throw2DontMove();
  140. };
  141. SetFormatBackTraceFn(invalidFormatter);
  142. try {
  143. Throw1DontMove();
  144. UNIT_ASSERT(false);
  145. } catch (...) {
  146. TString expected = "Caught:\n"
  147. "(yexception) util/generic/yexception_ut.cpp:4: blabla\n"
  148. "Failed to print backtrace: "
  149. "(yexception) util/generic/yexception_ut.cpp:8: 1 qw 12.1";
  150. UNIT_ASSERT_EQUAL(FormatCurrentException(), expected);
  151. }
  152. try {
  153. throw std::logic_error("std exception");
  154. UNIT_ASSERT(false);
  155. } catch (...) {
  156. TString expected = "Caught:\n"
  157. "(std::logic_error) std exception\n"
  158. "Failed to print backtrace: "
  159. "(yexception) util/generic/yexception_ut.cpp:8: 1 qw 12.1";
  160. UNIT_ASSERT_EQUAL(FormatCurrentException(), expected);
  161. }
  162. }
  163. #endif
  164. inline void TestVirtualInheritance() {
  165. TStringStream ss;
  166. OUTS = &ss;
  167. class TA {
  168. public:
  169. inline TA() {
  170. *OUTS << "A";
  171. }
  172. };
  173. class TB {
  174. public:
  175. inline TB() {
  176. *OUTS << "B";
  177. }
  178. };
  179. class TC: public virtual TB, public virtual TA {
  180. public:
  181. inline TC() {
  182. *OUTS << "C";
  183. }
  184. };
  185. class TD: public virtual TA {
  186. public:
  187. inline TD() {
  188. *OUTS << "D";
  189. }
  190. };
  191. class TE: public TC, public TD {
  192. public:
  193. inline TE() {
  194. *OUTS << "E";
  195. }
  196. };
  197. TE e;
  198. UNIT_ASSERT_EQUAL(ss.Str(), "BACDE");
  199. }
  200. inline void TestVirtuality() {
  201. try {
  202. ythrow TFileError() << "1";
  203. UNIT_ASSERT(false);
  204. } catch (const TIoException&) {
  205. } catch (...) {
  206. UNIT_ASSERT(false);
  207. }
  208. try {
  209. ythrow TFileError() << 1;
  210. UNIT_ASSERT(false);
  211. } catch (const TSystemError&) {
  212. } catch (...) {
  213. UNIT_ASSERT(false);
  214. }
  215. try {
  216. ythrow TFileError() << '1';
  217. UNIT_ASSERT(false);
  218. } catch (const yexception&) {
  219. } catch (...) {
  220. UNIT_ASSERT(false);
  221. }
  222. try {
  223. ythrow TFileError() << 1.0;
  224. UNIT_ASSERT(false);
  225. } catch (const TFileError&) {
  226. } catch (...) {
  227. UNIT_ASSERT(false);
  228. }
  229. }
  230. inline void TestFormat1() {
  231. try {
  232. throw yexception() << 1 << " qw " << 12.1;
  233. UNIT_ASSERT(false);
  234. } catch (...) {
  235. const TString err = CurrentExceptionMessage();
  236. UNIT_ASSERT(err.Contains("1 qw 12.1"));
  237. }
  238. }
  239. static inline void CheckCurrentExceptionContains(const char* message) {
  240. TString err = CurrentExceptionMessage();
  241. SubstGlobal(err, '\\', '/'); // remove backslashes from path in message
  242. UNIT_ASSERT(err.Contains(message));
  243. }
  244. inline void TestRaise1() {
  245. try {
  246. Throw2DontMove();
  247. UNIT_ASSERT(false);
  248. } catch (...) {
  249. CheckCurrentExceptionContains("util/generic/yexception_ut.cpp:8: 1 qw 12.1");
  250. }
  251. }
  252. inline void TestException() {
  253. ythrow yexception() << "blablabla";
  254. }
  255. inline void TestLineInfo() {
  256. try {
  257. Throw1DontMove();
  258. UNIT_ASSERT(false);
  259. } catch (...) {
  260. CheckCurrentExceptionContains("util/generic/yexception_ut.cpp:4: blabla");
  261. throw;
  262. }
  263. }
  264. //! tests propagation of an exception through C code
  265. //! @note on some platforms, for example GCC on 32-bit Linux without -fexceptions option,
  266. //! throwing an exception from a C++ callback through C code aborts program
  267. inline void TestMixedCode() {
  268. const int N = 26082009;
  269. try {
  270. TestCallback(&CallbackFun, N);
  271. UNIT_ASSERT(false);
  272. } catch (int i) {
  273. UNIT_ASSERT_VALUES_EQUAL(i, N);
  274. }
  275. }
  276. void TestMacroOverload() {
  277. try {
  278. Y_ENSURE(10 > 20);
  279. } catch (const yexception& e) {
  280. UNIT_ASSERT(e.AsStrBuf().Contains("10 > 20"));
  281. }
  282. try {
  283. Y_ENSURE(10 > 20, "exception message to search for");
  284. } catch (const yexception& e) {
  285. UNIT_ASSERT(e.AsStrBuf().Contains("exception message to search for"));
  286. }
  287. try {
  288. NOuter::NInner::Compare10And20();
  289. } catch (const yexception& e) {
  290. UNIT_ASSERT(e.AsStrBuf().Contains("10 > 20"));
  291. }
  292. }
  293. void TestMessageCrop() {
  294. TTempBuf tmp;
  295. size_t size = tmp.Size() * 1.5;
  296. TString s;
  297. s.reserve(size);
  298. TMersenne<ui64> generator(42);
  299. for (int j = 0; j < 50; ++j) {
  300. for (size_t i = 0; i < size; ++i) {
  301. s += static_cast<char>('a' + generator() % 26);
  302. }
  303. yexception e;
  304. e << s;
  305. UNIT_ASSERT_EQUAL(e.AsStrBuf(), s.substr(0, tmp.Size() - 1));
  306. }
  307. }
  308. void TestTIoSystemErrorSpecialMethods() {
  309. TString testStr{"systemError"};
  310. TIoSystemError err;
  311. err << testStr;
  312. UNIT_ASSERT(err.AsStrBuf().Contains(testStr));
  313. TIoSystemError errCopy{err};
  314. UNIT_ASSERT(err.AsStrBuf().Contains(testStr));
  315. UNIT_ASSERT(errCopy.AsStrBuf().Contains(testStr));
  316. TIoSystemError errAssign;
  317. errAssign = err;
  318. UNIT_ASSERT(err.AsStrBuf().Contains(testStr));
  319. UNIT_ASSERT(errAssign.AsStrBuf().Contains(testStr));
  320. TIoSystemError errMove{std::move(errCopy)};
  321. UNIT_ASSERT(errMove.AsStrBuf().Contains(testStr));
  322. TIoSystemError errMoveAssign;
  323. errMoveAssign = std::move(errMove);
  324. UNIT_ASSERT(errMoveAssign.AsStrBuf().Contains(testStr));
  325. }
  326. inline void TestCurrentExceptionTypeNameMethod() {
  327. //Basic test of getting the correct exception type name.
  328. try {
  329. throw std::runtime_error("Test Runtime Error Exception");
  330. } catch (...) {
  331. UNIT_ASSERT_STRING_CONTAINS(CurrentExceptionTypeName(), "std::runtime_error");
  332. }
  333. //Test when exception has an unusual type. Under Linux it should return "int" and under other OSs "unknown type".
  334. try {
  335. throw int(1);
  336. } catch (...) {
  337. #if defined(LIBCXX_BUILDING_LIBCXXRT) || defined(LIBCXX_BUILDING_LIBGCC)
  338. UNIT_ASSERT_VALUES_EQUAL(CurrentExceptionTypeName(), "int");
  339. #else
  340. UNIT_ASSERT_VALUES_EQUAL(CurrentExceptionTypeName(), "unknown type");
  341. #endif
  342. }
  343. //Test when the caught exception is rethrown with std::rethrow_exception.
  344. try {
  345. throw std::logic_error("Test Logic Error Exception");
  346. } catch (...) {
  347. try {
  348. std::rethrow_exception(std::current_exception());
  349. } catch (...) {
  350. UNIT_ASSERT_STRING_CONTAINS(CurrentExceptionTypeName(), "std::logic_error");
  351. }
  352. }
  353. //Test when the caught exception is rethrown with throw; .
  354. //This test is different from the previous one because of the interaction with cxxabi specifics.
  355. try {
  356. throw std::bad_alloc();
  357. } catch (...) {
  358. try {
  359. throw;
  360. } catch (...) {
  361. UNIT_ASSERT_STRING_CONTAINS(CurrentExceptionTypeName(), "std::bad_alloc");
  362. }
  363. }
  364. // For exceptions thrown by std::rethrow_exception() a nullptr will be returned by libcxxrt's __cxa_current_exception_type().
  365. // Adding an explicit test for the case.
  366. try {
  367. throw int(1);
  368. } catch (...) {
  369. try {
  370. std::rethrow_exception(std::current_exception());
  371. } catch (...) {
  372. #if defined(LIBCXX_BUILDING_LIBGCC)
  373. UNIT_ASSERT_VALUES_EQUAL(CurrentExceptionTypeName(), "int");
  374. #else
  375. UNIT_ASSERT_VALUES_EQUAL(CurrentExceptionTypeName(), "unknown type");
  376. #endif
  377. }
  378. }
  379. //Test when int is rethrown with throw; .
  380. try {
  381. throw int(1);
  382. } catch (...) {
  383. try {
  384. throw;
  385. } catch (...) {
  386. #if defined(LIBCXX_BUILDING_LIBCXXRT) || defined(LIBCXX_BUILDING_LIBGCC)
  387. UNIT_ASSERT_VALUES_EQUAL(CurrentExceptionTypeName(), "int");
  388. #else
  389. UNIT_ASSERT_VALUES_EQUAL(CurrentExceptionTypeName(), "unknown type");
  390. #endif
  391. }
  392. }
  393. }
  394. };
  395. UNIT_TEST_SUITE_REGISTRATION(TExceptionTest);