diff options
author | arkady-e1ppa <arkady-e1ppa@yandex-team.com> | 2023-11-07 17:37:10 +0300 |
---|---|---|
committer | arkady-e1ppa <arkady-e1ppa@yandex-team.com> | 2023-11-07 18:03:42 +0300 |
commit | 5132a4c0b62c856f50d0ff67c379eea66b36dde5 (patch) | |
tree | 5fa439116d82f8f668b517ee5b21921cf724ec39 /yt | |
parent | 7260302093d21007fa4327c53b5c4c4623054daa (diff) | |
download | ydb-5132a4c0b62c856f50d0ff67c379eea66b36dde5.tar.gz |
YT-20165: New overloads of ```operator <<``` and ```Wrap``` for ```TError``` and unit tests
Что тут появилось:
1) Теперь есть перегрузки ```operator <<=``` с сигнатурой ```(TError&, TSmth&&)```, где ```TSmth``` -- это один из 7 типов, которые были раньше в ```operator <<```.
2) Операторы ```<<``` выражены через ```<<=```. Для них есть шаблонизированные перегрузки по ```const TError&``` и ```TError&&```.
3) Функция-член ```TError::Wrap``` теперь имеет перегрузку по ```const &``` и ```&&```, а еще обзавелась проверкой концепта в сигнатуре.
4) Макрос ```THROW_ERROR_EXCEPTION_IF_FAILED``` теперь использует перегрузку по ```&&```, то есть поглощает ```TError```, но только в случае, если в нем лежит ошибка.
5) Макрос ```THROW_ERROR``` использует специальный адаптер, который позволяет писать права от него все, что угодно, от чего можно сконструировать ```TError```.
6) Все операторы ```<<``` и ```<<=``` стали функциями-членами -- больше нельзя делать с ними неявные конверсии, кроме специального адаптора в ```THROW_ERROR```.
Diffstat (limited to 'yt')
-rw-r--r-- | yt/yt/core/misc/error-inl.h | 54 | ||||
-rw-r--r-- | yt/yt/core/misc/error.cpp | 53 | ||||
-rw-r--r-- | yt/yt/core/misc/error.h | 111 | ||||
-rw-r--r-- | yt/yt/core/misc/unittests/error_ut.cpp | 370 | ||||
-rw-r--r-- | yt/yt/core/net/listener.cpp | 2 |
5 files changed, 527 insertions, 63 deletions
diff --git a/yt/yt/core/misc/error-inl.h b/yt/yt/core/misc/error-inl.h index 0020b9e0d6..0750527aff 100644 --- a/yt/yt/core/misc/error-inl.h +++ b/yt/yt/core/misc/error-inl.h @@ -70,11 +70,31 @@ TError::TErrorOr(TErrorCode code, const char (&messageOrFormat)[Length], TArgs&& { } template <class... TArgs> -TError TError::Wrap(TArgs&&... args) const + requires std::constructible_from<TError, TArgs...> +TError TError::Wrap(TArgs&&... args) const & { return TError(std::forward<TArgs>(args)...) << *this; } +template <class... TArgs> + requires std::constructible_from<TError, TArgs...> +TError TError::Wrap(TArgs&&... args) && +{ + return TError(std::forward<TArgs>(args)...) << std::move(*this); +} + +template <CErrorNestable TValue> +TError&& TError::operator << (TValue&& operand) && +{ + return std::move(*this <<= std::forward<TValue>(operand)); +} + +template <CErrorNestable TValue> +TError TError::operator << (TValue&& operand) const & +{ + return TError(*this) << std::forward<TValue>(operand); +} + //////////////////////////////////////////////////////////////////////////////// template <class T> @@ -261,6 +281,7 @@ TString ToString(const TErrorOr<T>& valueOrError) //////////////////////////////////////////////////////////////////////////////// template <class TException> + requires std::derived_from<std::remove_cvref_t<TException>, TErrorException> TException&& operator <<= (TException&& ex, const TError& error) { YT_VERIFY(!error.IsOK()); @@ -269,6 +290,7 @@ TException&& operator <<= (TException&& ex, const TError& error) } template <class TException> + requires std::derived_from<std::remove_cvref_t<TException>, TErrorException> TException&& operator <<= (TException&& ex, TError&& error) { YT_VERIFY(!error.IsOK()); @@ -278,4 +300,34 @@ TException&& operator <<= (TException&& ex, TError&& error) //////////////////////////////////////////////////////////////////////////////// +namespace NDetail { + +template <class TArg> + requires std::constructible_from<TError, TArg> +TError TErrorAdaptor::operator << (TArg&& rightOperand) const +{ + return TError(std::forward<TArg>(rightOperand)); +} + +template <class TArg> + requires std::constructible_from<TError, TArg> && + std::derived_from<std::remove_cvref_t<TArg>, TError> +TArg&& TErrorAdaptor::operator << (TArg&& rightOperand) const +{ + return std::forward<TArg>(rightOperand); +} + +template <class TLikeError, class... TArgs> + requires + std::is_base_of_v<TError, std::remove_cvref_t<TLikeError>> && + std::constructible_from<TError, TArgs...> +void ThrowErrorExceptionIfFailed(TLikeError&& error, TArgs&&... args) +{ + if (!error.IsOK()) { + THROW_ERROR std::move(error).Wrap(std::forward<TArgs>(args)...); + } +} + +} // namespace NDetail + } // namespace NYT diff --git a/yt/yt/core/misc/error.cpp b/yt/yt/core/misc/error.cpp index 2a6f7dcb14..33f5372356 100644 --- a/yt/yt/core/misc/error.cpp +++ b/yt/yt/core/misc/error.cpp @@ -711,11 +711,16 @@ void TError::ThrowOnError() const } } -TError TError::Wrap() const +TError TError::Wrap() const & { return *this; } +TError TError::Wrap() && +{ + return std::move(*this); +} + Y_WEAK TString GetErrorSkeleton(const TError& /*error*/) { // Proper implementation resides in yt/yt/library/error_skeleton/skeleton.cpp. @@ -1213,54 +1218,54 @@ void Deserialize(TError& error, NYson::TYsonPullParserCursor* cursor) //////////////////////////////////////////////////////////////////////////////// -TError operator << (TError error, const TErrorAttribute& attribute) +TError& TError::operator <<= (const TErrorAttribute& attribute) & { - error.MutableAttributes()->SetYson(attribute.Key, attribute.Value); - return error; + MutableAttributes()->SetYson(attribute.Key, attribute.Value); + return *this; } -TError operator << (TError error, const std::vector<TErrorAttribute>& attributes) +TError& TError::operator <<= (const std::vector<TErrorAttribute>& attributes) & { for (const auto& attribute : attributes) { - error.MutableAttributes()->SetYson(attribute.Key, attribute.Value); + MutableAttributes()->SetYson(attribute.Key, attribute.Value); } - return error; + return *this; } -TError operator << (TError error, const TError& innerError) +TError& TError::operator <<= (const TError& innerError) & { - error.MutableInnerErrors()->push_back(innerError); - return error; + MutableInnerErrors()->push_back(innerError); + return *this; } -TError operator << (TError error, TError&& innerError) +TError& TError::operator <<= (TError&& innerError) & { - error.MutableInnerErrors()->push_back(std::move(innerError)); - return error; + MutableInnerErrors()->push_back(std::move(innerError)); + return *this; } -TError operator << (TError error, const std::vector<TError>& innerErrors) +TError& TError::operator <<= (const std::vector<TError>& innerErrors) & { - error.MutableInnerErrors()->insert( - error.MutableInnerErrors()->end(), + MutableInnerErrors()->insert( + MutableInnerErrors()->end(), innerErrors.begin(), innerErrors.end()); - return error; + return *this; } -TError operator << (TError error, std::vector<TError>&& innerErrors) +TError& TError::operator <<= (std::vector<TError>&& innerErrors) & { - error.MutableInnerErrors()->insert( - error.MutableInnerErrors()->end(), + MutableInnerErrors()->insert( + MutableInnerErrors()->end(), std::make_move_iterator(innerErrors.begin()), std::make_move_iterator(innerErrors.end())); - return error; + return *this; } -TError operator << (TError error, const NYTree::IAttributeDictionary& attributes) +TError& TError::operator <<= (const NYTree::IAttributeDictionary& attributes) & { - error.MutableAttributes()->MergeFrom(attributes); - return error; + MutableAttributes()->MergeFrom(attributes); + return *this; } //////////////////////////////////////////////////////////////////////////////// diff --git a/yt/yt/core/misc/error.h b/yt/yt/core/misc/error.h index c79e604102..11e02ba748 100644 --- a/yt/yt/core/misc/error.h +++ b/yt/yt/core/misc/error.h @@ -79,6 +79,31 @@ private: //////////////////////////////////////////////////////////////////////////////// +struct TErrorAttribute +{ + template <class T> + TErrorAttribute(const TString& key, const T& value) + : Key(key) + , Value(NYson::ConvertToYsonString(value)) + { } + + TErrorAttribute(const TString& key, const NYson::TYsonString& value) + : Key(key) + , Value(value) + { } + + TString Key; + NYson::TYsonString Value; +}; + +//////////////////////////////////////////////////////////////////////////////// + +template <class TValue> +concept CErrorNestable = requires (TError& error, TValue&& operand) +{ + { error <<= std::forward<TValue>(operand) } -> std::same_as<TError&>; +}; + template <> class [[nodiscard]] TErrorOr<void> { @@ -157,8 +182,14 @@ public: std::optional<TError> FindMatching(TErrorCode code) const; template <class... TArgs> - TError Wrap(TArgs&&... args) const; - TError Wrap() const; + requires std::constructible_from<TError, TArgs...> + TError Wrap(TArgs&&... args) const &; + TError Wrap() const &; + + template <class... TArgs> + requires std::constructible_from<TError, TArgs...> + TError Wrap(TArgs&&... args) &&; + TError Wrap() &&; //! Perform recursive aggregation of error codes and messages over the error tree. //! Result of this aggregation is suitable for error clustering in groups of @@ -175,6 +206,20 @@ public: void Save(TStreamSaveContext& context) const; void Load(TStreamLoadContext& context); + TError& operator <<= (const TErrorAttribute& attribute) &; + TError& operator <<= (const std::vector<TErrorAttribute>& attributes) &; + TError& operator <<= (const TError& innerError) &; + TError& operator <<= (TError&& innerError) &; + TError& operator <<= (const std::vector<TError>& innerErrors) &; + TError& operator <<= (std::vector<TError>&& innerErrors) &; + TError& operator <<= (const NYTree::IAttributeDictionary& attributes) &; + + template <CErrorNestable TValue> + TError&& operator << (TValue&& operand) &&; + + template <CErrorNestable TValue> + TError operator << (TValue&& operand) const &; + private: class TImpl; std::unique_ptr<TImpl> Impl_; @@ -243,33 +288,6 @@ struct TErrorTraits<TErrorOr<T>> //////////////////////////////////////////////////////////////////////////////// -struct TErrorAttribute -{ - template <class T> - TErrorAttribute(const TString& key, const T& value) - : Key(key) - , Value(NYson::ConvertToYsonString(value)) - { } - - TErrorAttribute(const TString& key, const NYson::TYsonString& value) - : Key(key) - , Value(value) - { } - - TString Key; - NYson::TYsonString Value; -}; - -TError operator << (TError error, const TErrorAttribute& attribute); -TError operator << (TError error, const std::vector<TErrorAttribute>& attributes); -TError operator << (TError error, const TError& innerError); -TError operator << (TError error, TError&& innerError); -TError operator << (TError error, const std::vector<TError>& innerErrors); -TError operator << (TError error, std::vector<TError>&& innerErrors); -TError operator << (TError error, const NYTree::IAttributeDictionary& attributes); - -//////////////////////////////////////////////////////////////////////////////// - class TErrorException : public std::exception { @@ -285,28 +303,51 @@ public: private: mutable TString CachedWhat_; - }; // Make these templates to avoid type erasure during throw. template <class TException> + requires std::derived_from<std::remove_cvref_t<TException>, TErrorException> TException&& operator <<= (TException&& ex, const TError& error); template <class TException> + requires std::derived_from<std::remove_cvref_t<TException>, TErrorException> TException&& operator <<= (TException&& ex, TError&& error); //////////////////////////////////////////////////////////////////////////////// +namespace NDetail { + +struct TErrorAdaptor +{ + template <class TArg> + requires std::constructible_from<TError, TArg> + TError operator << (TArg&& rightOperand) const; + + template <class TArg> + requires std::constructible_from<TError, TArg> && + std::derived_from<std::remove_cvref_t<TArg>, TError> + TArg&& operator << (TArg&& rightOperand) const; +}; + +// Make these to correctly forward TError to Wrap call. +template <class TLikeError, class... TArgs> + requires + std::is_base_of_v<TError, std::remove_cvref_t<TLikeError>> && + std::constructible_from<TError, TArgs...> +void ThrowErrorExceptionIfFailed(TLikeError&& error, TArgs&&... args); + +} // namespace NDetail + +//////////////////////////////////////////////////////////////////////////////// + #define THROW_ERROR \ - throw ::NYT::TErrorException() <<= + throw ::NYT::TErrorException() <<= ::NYT::NDetail::TErrorAdaptor() << #define THROW_ERROR_EXCEPTION(head, ...) \ THROW_ERROR ::NYT::TError(head __VA_OPT__(,) __VA_ARGS__) #define THROW_ERROR_EXCEPTION_IF_FAILED(error, ...) \ - if (const auto& error__ = (error); error__ .IsOK()) { \ - } else { \ - THROW_ERROR error__.Wrap(__VA_ARGS__); \ - } + ::NYT::NDetail::ThrowErrorExceptionIfFailed((error) __VA_OPT__(,) __VA_ARGS__); \ #define THROW_ERROR_EXCEPTION_UNLESS(condition, head, ...) \ if ((condition)) {\ diff --git a/yt/yt/core/misc/unittests/error_ut.cpp b/yt/yt/core/misc/unittests/error_ut.cpp index 2ec50a10c5..d2db8ff56c 100644 --- a/yt/yt/core/misc/unittests/error_ut.cpp +++ b/yt/yt/core/misc/unittests/error_ut.cpp @@ -1,3 +1,4 @@ +#include "library/cpp/testing/gtest_extensions/assertions.h" #include <yt/yt/core/test_framework/framework.h> #include <yt/yt/core/misc/error.h> @@ -18,6 +19,371 @@ using namespace NYTree; //////////////////////////////////////////////////////////////////////////////// +class TAdlException + : public std::exception +{ +public: + static int ResetCallCount() + { + return std::exchange(OverloadCallCount, 0); + } + + const char* what() const noexcept override + { + return "Adl exception"; + } + + // Simulate overload from TAdlException::operator << + template <class TLikeThis, class TArg> + requires std::is_base_of_v<TAdlException, std::decay_t<TLikeThis>> + friend TLikeThis&& operator << (TLikeThis&& ex, const TArg& /*other*/) + { + ++OverloadCallCount; + return std::forward<TLikeThis>(ex); + } + +private: + static inline int OverloadCallCount = 0; +}; + +class TAdlArgument +{ +public: + static int ResetCallCount() + { + return std::exchange(OverloadCallCount, 0); + } + + // Simulate overload TAdlArgument::operator << + friend TError operator << (TError&& error, const TAdlArgument& /*other*/) + { + static const TErrorAttribute Attr("attr", "attr_value"); + ++OverloadCallCount; + return std::move(error) << Attr; + } + + friend TError operator << (const TError& error, const TAdlArgument& /*other*/) + { + static const TErrorAttribute Attr("attr", "attr_value"); + ++OverloadCallCount; + return error << Attr; + } + +private: + static inline int OverloadCallCount = 0; +}; + +class TWidget +{ +public: + TWidget() + { + DefaultConstructorCalls++; + }; + + TWidget(const TWidget&) + { + CopyConstructorCalls++; + } + TWidget& operator = (const TWidget&) = delete; + + TWidget(TWidget&&) + { + MoveConstructorCalls++; + } + TWidget& operator = (TWidget&&) = delete; + + static int ResetDefaultCount() + { + return std::exchange(DefaultConstructorCalls, 0); + } + + static int ResetCopyCount() + { + return std::exchange(CopyConstructorCalls, 0); + } + + static int ResetMoveCount() + { + return std::exchange(MoveConstructorCalls, 0); + } + +private: + static inline int DefaultConstructorCalls = 0; + static inline int CopyConstructorCalls = 0; + static inline int MoveConstructorCalls = 0; +}; + +//////////////////////////////////////////////////////////////////////////////// + +template <class TOverloadTest, bool LeftOperandHasUserDefinedOverload = false> +void IterateTestOverEveryRightOperand(TOverloadTest& tester) +{ + { + TErrorAttribute attribute("attr", "attr_value"); + const auto& attributeRef = attribute; + tester(attributeRef); + } + + { + std::vector<TErrorAttribute> attributeVector{{"attr1", "attr_value"}, {"attr2", "attr_value"}}; + const auto& attributeVectorRef = attributeVector; + tester(attributeVectorRef); + } + + { + TError error("Error"); + + const auto& errorRef = error; + tester(errorRef); + + auto errorCopy = error; + tester(std::move(errorCopy)); + + if constexpr (!LeftOperandHasUserDefinedOverload) { + EXPECT_TRUE(errorCopy.IsOK()); + } + } + + { + std::vector<TError> vectorError{TError("Error"), TError("Error")}; + + const auto& vectorErrorRef = vectorError; + tester(vectorErrorRef); + + auto vectorErrorCopy = vectorError; + tester(std::move(vectorErrorCopy)); + + if constexpr (!LeftOperandHasUserDefinedOverload) { + for (const auto& errorCopy : vectorErrorCopy) { + EXPECT_TRUE(errorCopy.IsOK()); + } + } + } + + { + TError error("Error"); + + const auto& attributeDictionaryRef = error.Attributes(); + tester(attributeDictionaryRef); + } + + { + try { + THROW_ERROR TError("Test error"); + } catch(const NYT::TErrorException& ex) { + const auto& exRef = ex; + tester(exRef); + + auto exCopy = ex; + tester(std::move(exCopy)); + } + } + + { + TErrorOr<int> err(std::exception{}); + + const auto& errRef = err; + tester(errRef); + + auto errCopy = err; + tester(std::move(errCopy)); + + if constexpr (!LeftOperandHasUserDefinedOverload) { + EXPECT_TRUE(errCopy.IsOK()); + } + } + + { + TAdlArgument adlArg; + + const TAdlArgument& adlArgRef = adlArg; + tester(adlArgRef); + + if constexpr (!LeftOperandHasUserDefinedOverload) { + EXPECT_EQ(TAdlArgument::ResetCallCount(), 1); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// + +TEST(TErrorTest, BitshiftOverloadsExplicitLeftOperand) +{ + // TError&& overload. + auto moveTester = [] (auto&& arg) { + TError error = TError("Test error"); + TError moved = std::move(error) << std::forward<decltype(arg)>(arg); + EXPECT_TRUE(error.IsOK()); + EXPECT_EQ(moved.GetMessage(), "Test error"); + }; + IterateTestOverEveryRightOperand(moveTester); + + // const TError& overloads. + auto copyTester = [] (auto&& arg) { + TError error = TError("Test error"); + TError copy = error << std::forward<decltype(arg)>(arg); + EXPECT_EQ(error.GetMessage(), copy.GetMessage()); + }; + IterateTestOverEveryRightOperand(copyTester); + + // Test that TError pr value binds correctly and the call itself is unambiguous. + auto prvalueTester = [] (auto&& arg) { + TError error = TError("Test error") << std::forward<decltype(arg)>(arg); + EXPECT_EQ(error.GetMessage(), "Test error"); + }; + IterateTestOverEveryRightOperand(prvalueTester); +} + +TEST(TErrorTest, BitshiftOverloadsImplicitLeftOperand) +{ + // We want to be able to write THROW_ERROR ex + auto throwErrorTester1 = [] (auto&& arg) { + try { + try { + THROW_ERROR TError("Test error"); + } catch(const NYT::TErrorException& ex) { + THROW_ERROR ex << std::forward<decltype(arg)>(arg); + } + } catch(const NYT::TErrorException& ex) { + TError error = ex; + EXPECT_EQ(error.GetMessage(), "Test error"); + } + }; + IterateTestOverEveryRightOperand(throwErrorTester1); + + // We also want to be able to write THROW_ERROR TError(smth) without compiler errors + auto throwErrorTester2 = [] (auto&& arg) { + try { + try { + THROW_ERROR TError("Test error"); + } catch(const NYT::TErrorException& ex) { + THROW_ERROR TError(ex) << std::forward<decltype(arg)>(arg); + } + } catch(const NYT::TErrorException& ex) { + TError error = ex; + EXPECT_EQ(error.GetMessage(), "Test error"); + } + }; + IterateTestOverEveryRightOperand(throwErrorTester2); + + // Left operand ADL finds the user-defined overload over NYT one. + // In this case AdlException should find templated function + // specialization with perfect match for args over conversions. + auto adlResolutionTester = [] (auto&& arg) { + TAdlException ex; + auto result = ex << std::forward<decltype(arg)>(arg); + static_assert(std::same_as<TAdlException, std::decay_t<decltype(result)>>); + EXPECT_EQ(TAdlException::ResetCallCount(), 1); + }; + IterateTestOverEveryRightOperand< + decltype(adlResolutionTester), + /*LeftOperandHasUserDefinedOverload=*/ true>(adlResolutionTester); + + // Make sure no ambiguous calls. + auto genericErrorOrTester = [] (auto&& arg) { + TErrorOr<int> err(std::exception{}); + TError error = err << std::forward<decltype(arg)>(arg); + EXPECT_EQ(error.GetCode(), NYT::EErrorCode::Generic); + }; + IterateTestOverEveryRightOperand(genericErrorOrTester); +} + +TEST(TErrorTest, Wrap) +{ + TError error("Error"); + + auto wrapped = error.Wrap("Wrapped error"); + EXPECT_EQ(wrapped.GetCode(), NYT::EErrorCode::Generic); + EXPECT_EQ(wrapped.GetMessage(), "Wrapped error"); + EXPECT_EQ(wrapped.InnerErrors().size(), 1u); + EXPECT_EQ(wrapped.InnerErrors()[0], error); + + auto triviallyWrapped = error.Wrap(); + EXPECT_EQ(triviallyWrapped, error); +} + +TEST(TErrorTest, WrapRValue) +{ + TError error("Error"); + + TError errorCopy = error; + auto wrapped = std::move(errorCopy).Wrap("Wrapped error"); + EXPECT_TRUE(errorCopy.IsOK()); + EXPECT_EQ(wrapped.GetCode(), NYT::EErrorCode::Generic); + EXPECT_EQ(wrapped.GetMessage(), "Wrapped error"); + EXPECT_EQ(wrapped.InnerErrors().size(), 1u); + EXPECT_EQ(wrapped.InnerErrors()[0], error); + + TError anotherErrorCopy = error; + auto trviallyWrapped = std::move(anotherErrorCopy).Wrap(); + EXPECT_TRUE(anotherErrorCopy.IsOK()); + EXPECT_EQ(trviallyWrapped, error); +} + +TEST(TErrorTest, ThrowErrorExceptionIfFailedMacroJustWorks) +{ + TError error; + + EXPECT_NO_THROW(THROW_ERROR_EXCEPTION_IF_FAILED(error, "Outer error")); + + error = TError("Real error"); + + TError errorCopy = error; + + try { + THROW_ERROR_EXCEPTION_IF_FAILED(errorCopy, "Outer error"); + } catch (const std::exception& ex) { + TError outerError(ex); + + EXPECT_TRUE(errorCopy.IsOK()); + EXPECT_EQ(outerError.GetMessage(), "Outer error"); + EXPECT_EQ(outerError.InnerErrors().size(), 1u); + EXPECT_EQ(outerError.InnerErrors()[0], error); + } +} + +TEST(TErrorTest, ThrowErrorExceptionIfFailedMacroExpression) +{ + try { + THROW_ERROR_EXCEPTION_IF_FAILED( + TError("Inner error") + << TErrorAttribute("attr", "attr_value"), + "Outer error"); + } catch (const std::exception& ex) { + TError outerError(ex); + + EXPECT_EQ(outerError.GetMessage(), "Outer error"); + EXPECT_EQ(outerError.InnerErrors().size(), 1u); + EXPECT_EQ(outerError.InnerErrors()[0].GetMessage(), "Inner error"); + EXPECT_EQ(outerError.InnerErrors()[0].Attributes().Get<TString>("attr"), "attr_value"); + } +} + +TEST(TErrorTest, ThrowErrorExceptionIfFailedMacroDontStealValue) +{ + TErrorOr<TWidget> widget = TWidget(); + EXPECT_TRUE(widget.IsOK()); + EXPECT_EQ(TWidget::ResetDefaultCount(), 1); + EXPECT_EQ(TWidget::ResetCopyCount(), 0); + EXPECT_EQ(TWidget::ResetMoveCount(), 1); + + EXPECT_NO_THROW(THROW_ERROR_EXCEPTION_IF_FAILED(widget)); + EXPECT_TRUE(widget.IsOK()); + EXPECT_NO_THROW(widget.ValueOrThrow()); + EXPECT_EQ(TWidget::ResetDefaultCount(), 0); + EXPECT_EQ(TWidget::ResetCopyCount(), 0); + EXPECT_EQ(TWidget::ResetMoveCount(), 0); +} + +TEST(TErrorTest, ThrowErrorExceptionIfFailedMacroDontDupeCalls) +{ + EXPECT_NO_THROW(THROW_ERROR_EXCEPTION_IF_FAILED(TErrorOr<TWidget>(TWidget()))); + EXPECT_EQ(TWidget::ResetDefaultCount(), 1); + EXPECT_EQ(TWidget::ResetCopyCount(), 0); + EXPECT_EQ(TWidget::ResetMoveCount(), 1); +} + TEST(TErrorTest, SerializationDepthLimit) { constexpr int Depth = 1000; @@ -111,7 +477,7 @@ TEST(TErrorTest, TruncateSimpleRValue) << TError("Inner error"); auto errorCopy = error; auto truncatedError = std::move(errorCopy).Truncate(); - EXPECT_EQ(NYT::EErrorCode::OK, errorCopy.GetCode()); + EXPECT_TRUE(errorCopy.IsOK()); EXPECT_EQ(error.GetCode(), truncatedError.GetCode()); EXPECT_EQ(error.GetMessage(), truncatedError.GetMessage()); @@ -135,7 +501,7 @@ TEST(TErrorTest, TruncateLargeRValue) auto errorCopy = error; auto truncatedError = std::move(errorCopy).Truncate(/*maxInnerErrorCount*/ 3, /*stringLimit*/ 10); - EXPECT_EQ(NYT::EErrorCode::OK, errorCopy.GetCode()); + EXPECT_TRUE(errorCopy.IsOK()); EXPECT_EQ(error.GetCode(), truncatedError.GetCode()); EXPECT_EQ("Some long ...<message truncated>", truncatedError.GetMessage()); diff --git a/yt/yt/core/net/listener.cpp b/yt/yt/core/net/listener.cpp index 3b9b6b0615..bb658fd68d 100644 --- a/yt/yt/core/net/listener.cpp +++ b/yt/yt/core/net/listener.cpp @@ -73,7 +73,7 @@ public: } } } catch (const TErrorException& ex) { - auto error = ex << TErrorAttribute("listener", Name_); + auto error = TError(ex) << TErrorAttribute("listener", Name_); Abort(error); YT_LOG_FATAL(error, "Listener crashed with fatal error"); } |