aboutsummaryrefslogtreecommitdiffstats
path: root/yt
diff options
context:
space:
mode:
authorarkady-e1ppa <arkady-e1ppa@yandex-team.com>2023-11-07 17:37:10 +0300
committerarkady-e1ppa <arkady-e1ppa@yandex-team.com>2023-11-07 18:03:42 +0300
commit5132a4c0b62c856f50d0ff67c379eea66b36dde5 (patch)
tree5fa439116d82f8f668b517ee5b21921cf724ec39 /yt
parent7260302093d21007fa4327c53b5c4c4623054daa (diff)
downloadydb-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.h54
-rw-r--r--yt/yt/core/misc/error.cpp53
-rw-r--r--yt/yt/core/misc/error.h111
-rw-r--r--yt/yt/core/misc/unittests/error_ut.cpp370
-rw-r--r--yt/yt/core/net/listener.cpp2
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");
}