[CELEBORN-1754][CIP-14] Add exceptions and checking utils to cppClient
### What changes were proposed in this pull request? This PR adds exceptions and checking utils code to CppClient. Besides, the ctest framework is added to CppClient for UTs. ### Why are the changes needed? To provide exception utils and UT frmework to CppClient. ### Does this PR introduce _any_ user-facing change? No. ### How was this patch tested? Compilation and UTs. Closes #2966 from HolyLow/issue/celeborn-1754-add-exceptions-utils-to-cppClient. Authored-by: HolyLow <jiaming.xie7@gmail.com> Signed-off-by: mingji <fengmingxiao.fmx@alibaba-inc.com>
This commit is contained in:
parent
cc04d1315e
commit
b2b9a0ab4b
4
LICENSE
4
LICENSE
@ -266,8 +266,10 @@ Meta Velox
|
||||
./cpp/celeborn/utils/StackTrace.cpp
|
||||
./cpp/celeborn/utils/CelebornException.h
|
||||
./cpp/celeborn/utils/CelebornException.cpp
|
||||
./cpp/celeborn/utils/Exceptions.h
|
||||
./cpp/celeborn/utils/Exceptions.cpp
|
||||
./cpp/celeborn/utils/flags.cpp
|
||||
|
||||
./cpp/celeborn/utils/tests/ExceptionTest.cpp
|
||||
|
||||
------------------------------------------------------------------------------------
|
||||
This product bundles various third-party components under the CC0 license.
|
||||
|
||||
@ -19,6 +19,7 @@ if (NOT DEFINED PACKAGE_VERSION)
|
||||
endif ()
|
||||
|
||||
project("celeborn" VERSION ${PACKAGE_VERSION} LANGUAGES CXX C)
|
||||
enable_testing()
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
@ -140,4 +141,14 @@ include_directories(SYSTEM ${OPENSSL_INCLUDE_DIR})
|
||||
include_directories(SYSTEM celeborn)
|
||||
include_directories(.)
|
||||
|
||||
option(CELEBORN_BUILD_TESTS "CELEBORN_BUILD_TESTS" ON)
|
||||
if(CELEBORN_BUILD_TESTS)
|
||||
### TODO: we use prebuilt gtest prebuilt package here. A better way is
|
||||
### to use source package, but the setting would be more complicated.
|
||||
### Maybe we could change the method later.
|
||||
find_package(GTest CONFIG REQUIRED)
|
||||
# Include after project() but before add_subdirectory().
|
||||
include(CTest)
|
||||
endif()
|
||||
|
||||
add_subdirectory(celeborn)
|
||||
|
||||
@ -14,7 +14,7 @@ docker run \
|
||||
-w /celeborn \
|
||||
-it --rm \
|
||||
--name celeborn-cpp-dev-container \
|
||||
holylow/celeborn-cpp-dev:0.1 \
|
||||
holylow/celeborn-cpp-dev:0.2 \
|
||||
/bin/bash
|
||||
```
|
||||
|
||||
@ -38,13 +38,14 @@ bash setup-ubuntu.sh
|
||||
```
|
||||
Other platforms are not supported yet, and you could use the container above as your dev environment.
|
||||
|
||||
## Compile
|
||||
## Compile and test
|
||||
Currently, the modules are under development.
|
||||
You could compile the code within the dev container by
|
||||
You could compile the code and run the tests within the dev container by
|
||||
```
|
||||
cd celeborn/cpp
|
||||
mkdir -p build && cd build
|
||||
cmake ..
|
||||
make
|
||||
ctest
|
||||
```
|
||||
|
||||
|
||||
@ -12,7 +12,9 @@
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
add_library(utils ProcessBase.cpp StackTrace.cpp CelebornException.cpp flags.cpp)
|
||||
add_library(
|
||||
utils
|
||||
ProcessBase.cpp StackTrace.cpp CelebornException.cpp Exceptions.cpp flags.cpp)
|
||||
|
||||
target_link_libraries(
|
||||
utils
|
||||
@ -23,3 +25,7 @@ target_link_libraries(
|
||||
${GLOG}
|
||||
${GFLAGS_LIBRARIES}
|
||||
)
|
||||
|
||||
if(CELEBORN_BUILD_TESTS)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
26
cpp/celeborn/utils/Exceptions.cpp
Normal file
26
cpp/celeborn/utils/Exceptions.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Based on Exceptions.cpp from Facebook Velox
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "celeborn/utils/Exceptions.h"
|
||||
|
||||
namespace celeborn::detail {
|
||||
|
||||
CELEBORN_DEFINE_CHECK_FAIL_TEMPLATES(::celeborn::CelebornRuntimeError);
|
||||
CELEBORN_DEFINE_CHECK_FAIL_TEMPLATES(::celeborn::CelebornUserError);
|
||||
|
||||
} // namespace celeborn::detail
|
||||
421
cpp/celeborn/utils/Exceptions.h
Normal file
421
cpp/celeborn/utils/Exceptions.h
Normal file
@ -0,0 +1,421 @@
|
||||
/*
|
||||
* Based on Exceptions.h from Facebook Velox
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/ostream.h>
|
||||
#include <glog/logging.h>
|
||||
|
||||
#include <folly/Conv.h>
|
||||
#include <folly/Exception.h>
|
||||
#include <folly/Preprocessor.h>
|
||||
|
||||
#include "celeborn/utils/CelebornException.h"
|
||||
// TODO: maybe remove the file permanently...
|
||||
// #include "celeborn/utils/FmtStdFormatters.h"
|
||||
|
||||
namespace celeborn {
|
||||
namespace detail {
|
||||
|
||||
struct CelebornCheckFailArgs {
|
||||
const char* file;
|
||||
size_t line;
|
||||
const char* function;
|
||||
const char* expression;
|
||||
const char* errorSource;
|
||||
const char* errorCode;
|
||||
bool isRetriable;
|
||||
};
|
||||
|
||||
struct CompileTimeEmptyString {
|
||||
CompileTimeEmptyString() = default;
|
||||
constexpr operator const char*() const {
|
||||
return "";
|
||||
}
|
||||
constexpr operator std::string_view() const {
|
||||
return {};
|
||||
}
|
||||
operator std::string() const {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
// celebornCheckFail is defined as a separate helper function rather than
|
||||
// a macro or inline `throw` expression to allow the compiler *not* to
|
||||
// inline it when it is large. Having an out-of-line error path helps
|
||||
// otherwise-small functions that call error-checking macros stay
|
||||
// small and thus stay eligible for inlining.
|
||||
template <typename Exception, typename StringType>
|
||||
[[noreturn]] void celebornCheckFail(
|
||||
const CelebornCheckFailArgs& args,
|
||||
StringType s) {
|
||||
static_assert(
|
||||
!std::is_same_v<StringType, std::string>,
|
||||
"BUG: we should not pass std::string by value to celebornCheckFail");
|
||||
if constexpr (!std::is_same_v<Exception, CelebornUserError>) {
|
||||
LOG(ERROR) << "Line: " << args.file << ":" << args.line
|
||||
<< ", Function:" << args.function
|
||||
<< ", Expression: " << args.expression << " " << s
|
||||
<< ", Source: " << args.errorSource
|
||||
<< ", ErrorCode: " << args.errorCode;
|
||||
}
|
||||
|
||||
++threadNumCelebornThrow();
|
||||
throw Exception(
|
||||
args.file,
|
||||
args.line,
|
||||
args.function,
|
||||
args.expression,
|
||||
s,
|
||||
args.errorSource,
|
||||
args.errorCode,
|
||||
args.isRetriable);
|
||||
}
|
||||
|
||||
// CelebornCheckFailStringType helps us pass by reference to
|
||||
// celebornCheckFail exactly when the string type is std::string.
|
||||
template <typename T>
|
||||
struct CelebornCheckFailStringType;
|
||||
|
||||
template <>
|
||||
struct CelebornCheckFailStringType<CompileTimeEmptyString> {
|
||||
using type = CompileTimeEmptyString;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct CelebornCheckFailStringType<const char*> {
|
||||
using type = const char*;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct CelebornCheckFailStringType<std::string> {
|
||||
using type = const std::string&;
|
||||
};
|
||||
|
||||
// Declare explicit instantiations of celebornCheckFail for the given
|
||||
// exceptionType. Just like normal function declarations (prototypes),
|
||||
// this allows the compiler to assume that they are defined elsewhere
|
||||
// and simply insert a function call for the linker to fix up, rather
|
||||
// than emitting a definition of these templates into every
|
||||
// translation unit they are used in.
|
||||
#define CELEBORN_DECLARE_CHECK_FAIL_TEMPLATES(exception_type) \
|
||||
namespace detail { \
|
||||
extern template void \
|
||||
celebornCheckFail<exception_type, CompileTimeEmptyString>( \
|
||||
const CelebornCheckFailArgs& args, \
|
||||
CompileTimeEmptyString); \
|
||||
extern template void celebornCheckFail<exception_type, const char*>( \
|
||||
const CelebornCheckFailArgs& args, \
|
||||
const char*); \
|
||||
extern template void celebornCheckFail<exception_type, const std::string&>( \
|
||||
const CelebornCheckFailArgs& args, \
|
||||
const std::string&); \
|
||||
} // namespace detail
|
||||
|
||||
// Definitions corresponding to CELEBORN_DECLARE_CHECK_FAIL_TEMPLATES. Should
|
||||
// only be used in Exceptions.cpp.
|
||||
#define CELEBORN_DEFINE_CHECK_FAIL_TEMPLATES(exception_type) \
|
||||
template void celebornCheckFail<exception_type, CompileTimeEmptyString>( \
|
||||
const CelebornCheckFailArgs& args, CompileTimeEmptyString); \
|
||||
template void celebornCheckFail<exception_type, const char*>( \
|
||||
const CelebornCheckFailArgs& args, const char*); \
|
||||
template void celebornCheckFail<exception_type, const std::string&>( \
|
||||
const CelebornCheckFailArgs& args, const std::string&);
|
||||
|
||||
// When there is no message passed, we can statically detect this case
|
||||
// and avoid passing even a single unnecessary argument pointer,
|
||||
// minimizing size and thus maximizing eligibility for inlining.
|
||||
inline CompileTimeEmptyString errorMessage() {
|
||||
return {};
|
||||
}
|
||||
|
||||
inline const char* errorMessage(const char* s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
inline std::string errorMessage(const std::string& str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
std::string errorMessage(fmt::string_view fmt, const Args&... args) {
|
||||
return fmt::vformat(fmt, fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
#define _CELEBORN_THROW_IMPL( \
|
||||
exception, exprStr, errorSource, errorCode, isRetriable, ...) \
|
||||
{ \
|
||||
/* GCC 9.2.1 doesn't accept this code with constexpr. */ \
|
||||
static const ::celeborn::detail::CelebornCheckFailArgs \
|
||||
celebornCheckFailArgs = { \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
__FUNCTION__, \
|
||||
exprStr, \
|
||||
errorSource, \
|
||||
errorCode, \
|
||||
isRetriable}; \
|
||||
auto message = ::celeborn::detail::errorMessage(__VA_ARGS__); \
|
||||
::celeborn::detail::celebornCheckFail< \
|
||||
exception, \
|
||||
typename ::celeborn::detail::CelebornCheckFailStringType< \
|
||||
decltype(message)>::type>(celebornCheckFailArgs, message); \
|
||||
}
|
||||
|
||||
#define _CELEBORN_CHECK_AND_THROW_IMPL( \
|
||||
expr, exprStr, exception, errorSource, errorCode, isRetriable, ...) \
|
||||
if (UNLIKELY(!(expr))) { \
|
||||
_CELEBORN_THROW_IMPL( \
|
||||
exception, exprStr, errorSource, errorCode, isRetriable, __VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define _CELEBORN_THROW(exception, ...) \
|
||||
_CELEBORN_THROW_IMPL(exception, "", ##__VA_ARGS__)
|
||||
|
||||
CELEBORN_DECLARE_CHECK_FAIL_TEMPLATES(::celeborn::CelebornRuntimeError);
|
||||
|
||||
#define _CELEBORN_CHECK_IMPL(expr, exprStr, ...) \
|
||||
_CELEBORN_CHECK_AND_THROW_IMPL( \
|
||||
expr, \
|
||||
exprStr, \
|
||||
::celeborn::CelebornRuntimeError, \
|
||||
::celeborn::error_source::kErrorSourceRuntime.c_str(), \
|
||||
::celeborn::error_code::kInvalidState.c_str(), \
|
||||
/* isRetriable */ false, \
|
||||
##__VA_ARGS__)
|
||||
|
||||
// If the caller passes a custom message (4 *or more* arguments), we
|
||||
// have to construct a format string from ours ("({} vs. {})") plus
|
||||
// theirs by adding a space and shuffling arguments. If they don't (exactly 3
|
||||
// arguments), we can just pass our own format string and arguments straight
|
||||
// through.
|
||||
|
||||
#define _CELEBORN_CHECK_OP_WITH_USER_FMT_HELPER( \
|
||||
implmacro, expr1, expr2, op, user_fmt, ...) \
|
||||
implmacro( \
|
||||
(expr1)op(expr2), \
|
||||
#expr1 " " #op " " #expr2, \
|
||||
"({} vs. {}) " user_fmt, \
|
||||
expr1, \
|
||||
expr2, \
|
||||
##__VA_ARGS__)
|
||||
|
||||
#define _CELEBORN_CHECK_OP_HELPER(implmacro, expr1, expr2, op, ...) \
|
||||
if constexpr (FOLLY_PP_DETAIL_NARGS(__VA_ARGS__) > 0) { \
|
||||
_CELEBORN_CHECK_OP_WITH_USER_FMT_HELPER( \
|
||||
implmacro, expr1, expr2, op, __VA_ARGS__); \
|
||||
} else { \
|
||||
implmacro( \
|
||||
(expr1)op(expr2), \
|
||||
#expr1 " " #op " " #expr2, \
|
||||
"({} vs. {})", \
|
||||
expr1, \
|
||||
expr2); \
|
||||
}
|
||||
|
||||
#define _CELEBORN_CHECK_OP(expr1, expr2, op, ...) \
|
||||
_CELEBORN_CHECK_OP_HELPER( \
|
||||
_CELEBORN_CHECK_IMPL, expr1, expr2, op, ##__VA_ARGS__)
|
||||
|
||||
#define _CELEBORN_USER_CHECK_IMPL(expr, exprStr, ...) \
|
||||
_CELEBORN_CHECK_AND_THROW_IMPL( \
|
||||
expr, \
|
||||
exprStr, \
|
||||
::celeborn::CelebornUserError, \
|
||||
::celeborn::error_source::kErrorSourceUser.c_str(), \
|
||||
::celeborn::error_code::kInvalidArgument.c_str(), \
|
||||
/* isRetriable */ false, \
|
||||
##__VA_ARGS__)
|
||||
|
||||
#define _CELEBORN_USER_CHECK_OP(expr1, expr2, op, ...) \
|
||||
_CELEBORN_CHECK_OP_HELPER( \
|
||||
_CELEBORN_USER_CHECK_IMPL, expr1, expr2, op, ##__VA_ARGS__)
|
||||
|
||||
// For all below macros, an additional message can be passed using a
|
||||
// format string and arguments, as with `fmt::format`.
|
||||
#define CELEBORN_CHECK(expr, ...) \
|
||||
_CELEBORN_CHECK_IMPL(expr, #expr, ##__VA_ARGS__)
|
||||
#define CELEBORN_CHECK_GT(e1, e2, ...) \
|
||||
_CELEBORN_CHECK_OP(e1, e2, >, ##__VA_ARGS__)
|
||||
#define CELEBORN_CHECK_GE(e1, e2, ...) \
|
||||
_CELEBORN_CHECK_OP(e1, e2, >=, ##__VA_ARGS__)
|
||||
#define CELEBORN_CHECK_LT(e1, e2, ...) \
|
||||
_CELEBORN_CHECK_OP(e1, e2, <, ##__VA_ARGS__)
|
||||
#define CELEBORN_CHECK_LE(e1, e2, ...) \
|
||||
_CELEBORN_CHECK_OP(e1, e2, <=, ##__VA_ARGS__)
|
||||
#define CELEBORN_CHECK_EQ(e1, e2, ...) \
|
||||
_CELEBORN_CHECK_OP(e1, e2, ==, ##__VA_ARGS__)
|
||||
#define CELEBORN_CHECK_NE(e1, e2, ...) \
|
||||
_CELEBORN_CHECK_OP(e1, e2, !=, ##__VA_ARGS__)
|
||||
#define CELEBORN_CHECK_NULL(e, ...) CELEBORN_CHECK(e == nullptr, ##__VA_ARGS__)
|
||||
#define CELEBORN_CHECK_NOT_NULL(e, ...) \
|
||||
CELEBORN_CHECK(e != nullptr, ##__VA_ARGS__)
|
||||
|
||||
#define CELEBORN_CHECK_OK(expr) \
|
||||
do { \
|
||||
::celeborn::Status _s = (expr); \
|
||||
_CELEBORN_CHECK_IMPL(_s.ok(), #expr, _s.toString()); \
|
||||
} while (false)
|
||||
|
||||
#define CELEBORN_UNSUPPORTED(...) \
|
||||
_CELEBORN_THROW( \
|
||||
::celeborn::CelebornUserError, \
|
||||
::celeborn::error_source::kErrorSourceUser.c_str(), \
|
||||
::celeborn::error_code::kUnsupported.c_str(), \
|
||||
/* isRetriable */ false, \
|
||||
##__VA_ARGS__)
|
||||
|
||||
#define CELEBORN_ARITHMETIC_ERROR(...) \
|
||||
_CELEBORN_THROW( \
|
||||
::celeborn::CelebornUserError, \
|
||||
::celeborn::error_source::kErrorSourceUser.c_str(), \
|
||||
::celeborn::error_code::kArithmeticError.c_str(), \
|
||||
/* isRetriable */ false, \
|
||||
##__VA_ARGS__)
|
||||
|
||||
#define CELEBORN_SCHEMA_MISMATCH_ERROR(...) \
|
||||
_CELEBORN_THROW( \
|
||||
::celeborn::CelebornUserError, \
|
||||
::celeborn::error_source::kErrorSourceUser.c_str(), \
|
||||
::celeborn::error_code::kSchemaMismatch.c_str(), \
|
||||
/* isRetriable */ false, \
|
||||
##__VA_ARGS__)
|
||||
|
||||
#define CELEBORN_FILE_NOT_FOUND_ERROR(...) \
|
||||
_CELEBORN_THROW( \
|
||||
::celeborn::CelebornRuntimeError, \
|
||||
::celeborn::error_source::kErrorSourceRuntime.c_str(), \
|
||||
::celeborn::error_code::kFileNotFound.c_str(), \
|
||||
/* isRetriable */ false, \
|
||||
##__VA_ARGS__)
|
||||
|
||||
#define CELEBORN_UNREACHABLE(...) \
|
||||
_CELEBORN_THROW( \
|
||||
::celeborn::CelebornRuntimeError, \
|
||||
::celeborn::error_source::kErrorSourceRuntime.c_str(), \
|
||||
::celeborn::error_code::kUnreachableCode.c_str(), \
|
||||
/* isRetriable */ false, \
|
||||
##__VA_ARGS__)
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define CELEBORN_DCHECK(expr, ...) CELEBORN_CHECK(expr, ##__VA_ARGS__)
|
||||
#define CELEBORN_DCHECK_GT(e1, e2, ...) CELEBORN_CHECK_GT(e1, e2, ##__VA_ARGS__)
|
||||
#define CELEBORN_DCHECK_GE(e1, e2, ...) CELEBORN_CHECK_GE(e1, e2, ##__VA_ARGS__)
|
||||
#define CELEBORN_DCHECK_LT(e1, e2, ...) CELEBORN_CHECK_LT(e1, e2, ##__VA_ARGS__)
|
||||
#define CELEBORN_DCHECK_LE(e1, e2, ...) CELEBORN_CHECK_LE(e1, e2, ##__VA_ARGS__)
|
||||
#define CELEBORN_DCHECK_EQ(e1, e2, ...) CELEBORN_CHECK_EQ(e1, e2, ##__VA_ARGS__)
|
||||
#define CELEBORN_DCHECK_NE(e1, e2, ...) CELEBORN_CHECK_NE(e1, e2, ##__VA_ARGS__)
|
||||
#define CELEBORN_DCHECK_NULL(e, ...) CELEBORN_CHECK_NULL(e, ##__VA_ARGS__)
|
||||
#define CELEBORN_DCHECK_NOT_NULL(e, ...) \
|
||||
CELEBORN_CHECK_NOT_NULL(e, ##__VA_ARGS__)
|
||||
#else
|
||||
#define CELEBORN_DCHECK(expr, ...) CELEBORN_CHECK(true)
|
||||
#define CELEBORN_DCHECK_GT(e1, e2, ...) CELEBORN_CHECK(true)
|
||||
#define CELEBORN_DCHECK_GE(e1, e2, ...) CELEBORN_CHECK(true)
|
||||
#define CELEBORN_DCHECK_LT(e1, e2, ...) CELEBORN_CHECK(true)
|
||||
#define CELEBORN_DCHECK_LE(e1, e2, ...) CELEBORN_CHECK(true)
|
||||
#define CELEBORN_DCHECK_EQ(e1, e2, ...) CELEBORN_CHECK(true)
|
||||
#define CELEBORN_DCHECK_NE(e1, e2, ...) CELEBORN_CHECK(true)
|
||||
#define CELEBORN_DCHECK_NULL(e, ...) CELEBORN_CHECK(true)
|
||||
#define CELEBORN_DCHECK_NOT_NULL(e, ...) CELEBORN_CHECK(true)
|
||||
#endif
|
||||
|
||||
#define CELEBORN_FAIL(...) \
|
||||
_CELEBORN_THROW( \
|
||||
::celeborn::CelebornRuntimeError, \
|
||||
::celeborn::error_source::kErrorSourceRuntime.c_str(), \
|
||||
::celeborn::error_code::kInvalidState.c_str(), \
|
||||
/* isRetriable */ false, \
|
||||
##__VA_ARGS__)
|
||||
|
||||
CELEBORN_DECLARE_CHECK_FAIL_TEMPLATES(::celeborn::CelebornUserError);
|
||||
|
||||
// For all below macros, an additional message can be passed using a
|
||||
// format string and arguments, as with `fmt::format`.
|
||||
#define CELEBORN_USER_CHECK(expr, ...) \
|
||||
_CELEBORN_USER_CHECK_IMPL(expr, #expr, ##__VA_ARGS__)
|
||||
#define CELEBORN_USER_CHECK_GT(e1, e2, ...) \
|
||||
_CELEBORN_USER_CHECK_OP(e1, e2, >, ##__VA_ARGS__)
|
||||
#define CELEBORN_USER_CHECK_GE(e1, e2, ...) \
|
||||
_CELEBORN_USER_CHECK_OP(e1, e2, >=, ##__VA_ARGS__)
|
||||
#define CELEBORN_USER_CHECK_LT(e1, e2, ...) \
|
||||
_CELEBORN_USER_CHECK_OP(e1, e2, <, ##__VA_ARGS__)
|
||||
#define CELEBORN_USER_CHECK_LE(e1, e2, ...) \
|
||||
_CELEBORN_USER_CHECK_OP(e1, e2, <=, ##__VA_ARGS__)
|
||||
#define CELEBORN_USER_CHECK_EQ(e1, e2, ...) \
|
||||
_CELEBORN_USER_CHECK_OP(e1, e2, ==, ##__VA_ARGS__)
|
||||
#define CELEBORN_USER_CHECK_NE(e1, e2, ...) \
|
||||
_CELEBORN_USER_CHECK_OP(e1, e2, !=, ##__VA_ARGS__)
|
||||
#define CELEBORN_USER_CHECK_NULL(e, ...) \
|
||||
CELEBORN_USER_CHECK(e == nullptr, ##__VA_ARGS__)
|
||||
#define CELEBORN_USER_CHECK_NOT_NULL(e, ...) \
|
||||
CELEBORN_USER_CHECK(e != nullptr, ##__VA_ARGS__)
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define CELEBORN_USER_DCHECK(expr, ...) CELEBORN_USER_CHECK(expr, ##__VA_ARGS__)
|
||||
#define CELEBORN_USER_DCHECK_GT(e1, e2, ...) \
|
||||
CELEBORN_USER_CHECK_GT(e1, e2, ##__VA_ARGS__)
|
||||
#define CELEBORN_USER_DCHECK_GE(e1, e2, ...) \
|
||||
CELEBORN_USER_CHECK_GE(e1, e2, ##__VA_ARGS__)
|
||||
#define CELEBORN_USER_DCHECK_LT(e1, e2, ...) \
|
||||
CELEBORN_USER_CHECK_LT(e1, e2, ##__VA_ARGS__)
|
||||
#define CELEBORN_USER_DCHECK_LE(e1, e2, ...) \
|
||||
CELEBORN_USER_CHECK_LE(e1, e2, ##__VA_ARGS__)
|
||||
#define CELEBORN_USER_DCHECK_EQ(e1, e2, ...) \
|
||||
CELEBORN_USER_CHECK_EQ(e1, e2, ##__VA_ARGS__)
|
||||
#define CELEBORN_USER_DCHECK_NE(e1, e2, ...) \
|
||||
CELEBORN_USER_CHECK_NE(e1, e2, ##__VA_ARGS__)
|
||||
#define CELEBORN_USER_DCHECK_NOT_NULL(e, ...) \
|
||||
CELEBORN_USER_CHECK_NOT_NULL(e, ##__VA_ARGS__)
|
||||
#define CELEBORN_USER_DCHECK_NULL(e, ...) \
|
||||
CELEBORN_USER_CHECK_NULL(e, ##__VA_ARGS__)
|
||||
#else
|
||||
#define CELEBORN_USER_DCHECK(expr, ...) CELEBORN_USER_CHECK(true)
|
||||
#define CELEBORN_USER_DCHECK_GT(e1, e2, ...) CELEBORN_USER_CHECK(true)
|
||||
#define CELEBORN_USER_DCHECK_GE(e1, e2, ...) CELEBORN_USER_CHECK(true)
|
||||
#define CELEBORN_USER_DCHECK_LT(e1, e2, ...) CELEBORN_USER_CHECK(true)
|
||||
#define CELEBORN_USER_DCHECK_LE(e1, e2, ...) CELEBORN_USER_CHECK(true)
|
||||
#define CELEBORN_USER_DCHECK_EQ(e1, e2, ...) CELEBORN_USER_CHECK(true)
|
||||
#define CELEBORN_USER_DCHECK_NE(e1, e2, ...) CELEBORN_USER_CHECK(true)
|
||||
#define CELEBORN_USER_DCHECK_NULL(e, ...) CELEBORN_USER_CHECK(true)
|
||||
#define CELEBORN_USER_DCHECK_NOT_NULL(e, ...) CELEBORN_USER_CHECK(true)
|
||||
#endif
|
||||
|
||||
#define CELEBORN_USER_FAIL(...) \
|
||||
_CELEBORN_THROW( \
|
||||
::celeborn::CelebornUserError, \
|
||||
::celeborn::error_source::kErrorSourceUser.c_str(), \
|
||||
::celeborn::error_code::kInvalidArgument.c_str(), \
|
||||
/* isRetriable */ false, \
|
||||
##__VA_ARGS__)
|
||||
|
||||
#define CELEBORN_NYI(...) \
|
||||
_CELEBORN_THROW( \
|
||||
::celeborn::CelebornRuntimeError, \
|
||||
::celeborn::error_source::kErrorSourceRuntime.c_str(), \
|
||||
::celeborn::error_code::kNotImplemented.c_str(), \
|
||||
/* isRetriable */ false, \
|
||||
##__VA_ARGS__)
|
||||
|
||||
} // namespace celeborn
|
||||
32
cpp/celeborn/utils/tests/CMakeLists.txt
Normal file
32
cpp/celeborn/utils/tests/CMakeLists.txt
Normal file
@ -0,0 +1,32 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
add_executable(celeborn_utils_test ExceptionTest.cpp)
|
||||
|
||||
add_test(NAME celeborn_utils_test COMMAND celeborn_utils_test)
|
||||
|
||||
target_link_libraries(
|
||||
celeborn_utils_test
|
||||
PRIVATE
|
||||
utils
|
||||
${WANGLE}
|
||||
${FIZZ}
|
||||
${LIBSODIUM_LIBRARY}
|
||||
${FOLLY_WITH_DEPENDENCIES}
|
||||
${GLOG}
|
||||
${GFLAGS_LIBRARIES}
|
||||
GTest::gtest
|
||||
GTest::gmock
|
||||
GTest::gtest_main)
|
||||
932
cpp/celeborn/utils/tests/ExceptionTest.cpp
Normal file
932
cpp/celeborn/utils/tests/ExceptionTest.cpp
Normal file
@ -0,0 +1,932 @@
|
||||
/*
|
||||
* Based on ExceptionTest.cpp from Facebook Velox
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <folly/Random.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "celeborn/utils/Exceptions.h"
|
||||
|
||||
using namespace celeborn;
|
||||
|
||||
struct Counter {
|
||||
mutable int counter = 0;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Counter& c) {
|
||||
os << c.counter;
|
||||
++c.counter;
|
||||
return os;
|
||||
}
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Counter> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const Counter& c, FormatContext& ctx) const {
|
||||
auto x = c.counter++;
|
||||
return format_to(ctx.out(), "{}", x);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void verifyException(
|
||||
std::function<void()> f,
|
||||
std::function<void(const T&)> exceptionVerifier) {
|
||||
try {
|
||||
f();
|
||||
FAIL() << "Expected exception of type " << typeid(T).name()
|
||||
<< ", but no exception was thrown.";
|
||||
} catch (const T& e) {
|
||||
exceptionVerifier(e);
|
||||
} catch (...) {
|
||||
FAIL() << "Expected exception of type " << typeid(T).name()
|
||||
<< ", but instead got an exception of a different type.";
|
||||
}
|
||||
}
|
||||
|
||||
void verifyCelebornException(
|
||||
std::function<void()> f,
|
||||
const std::string& messagePrefix) {
|
||||
verifyException<CelebornException>(f, [&messagePrefix](const auto& e) {
|
||||
EXPECT_TRUE(folly::StringPiece{e.what()}.startsWith(messagePrefix))
|
||||
<< "\nException message prefix mismatch.\n\nExpected prefix: "
|
||||
<< messagePrefix << "\n\nActual message: " << e.what();
|
||||
});
|
||||
}
|
||||
|
||||
void testExceptionTraceCollectionControl(bool userException, bool enabled) {
|
||||
// Disable rate control in the test.
|
||||
FLAGS_celeborn_exception_user_stacktrace_rate_limit_ms = 0;
|
||||
FLAGS_celeborn_exception_system_stacktrace_rate_limit_ms = 0;
|
||||
|
||||
if (userException) {
|
||||
FLAGS_celeborn_exception_user_stacktrace_enabled = enabled ? true : false;
|
||||
FLAGS_celeborn_exception_system_stacktrace_enabled =
|
||||
folly::Random::oneIn(2);
|
||||
} else {
|
||||
FLAGS_celeborn_exception_system_stacktrace_enabled = enabled ? true : false;
|
||||
FLAGS_celeborn_exception_user_stacktrace_enabled = folly::Random::oneIn(2);
|
||||
}
|
||||
try {
|
||||
if (userException) {
|
||||
throw CelebornUserError(
|
||||
"file_name",
|
||||
1,
|
||||
"function_name()",
|
||||
"operator()",
|
||||
"test message",
|
||||
"",
|
||||
error_code::kArithmeticError,
|
||||
false);
|
||||
} else {
|
||||
throw CelebornRuntimeError(
|
||||
"file_name",
|
||||
1,
|
||||
"function_name()",
|
||||
"operator()",
|
||||
"test message",
|
||||
"",
|
||||
error_code::kArithmeticError,
|
||||
false);
|
||||
}
|
||||
} catch (CelebornException& e) {
|
||||
SCOPED_TRACE(fmt::format(
|
||||
"enabled: {}, user flag: {}, sys flag: {}",
|
||||
enabled,
|
||||
FLAGS_celeborn_exception_user_stacktrace_enabled,
|
||||
FLAGS_celeborn_exception_system_stacktrace_enabled));
|
||||
ASSERT_EQ(
|
||||
userException, e.exceptionType() == CelebornException::Type::kUser);
|
||||
ASSERT_EQ(enabled, e.stackTrace() != nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void testExceptionTraceCollectionRateControl(
|
||||
bool userException,
|
||||
bool hasRateLimit) {
|
||||
// Disable rate control in the test.
|
||||
// Enable trace rate control in the test.
|
||||
FLAGS_celeborn_exception_user_stacktrace_enabled = true;
|
||||
FLAGS_celeborn_exception_system_stacktrace_enabled = true;
|
||||
// Set rate control interval to a large value to avoid time related test
|
||||
// flakiness.
|
||||
const int kRateLimitIntervalMs = 4000;
|
||||
if (hasRateLimit) {
|
||||
// Wait a bit to ensure that the last stack trace collection time has
|
||||
// passed sufficient long.
|
||||
/* sleep override */
|
||||
std::this_thread::sleep_for(
|
||||
std::chrono::milliseconds(kRateLimitIntervalMs)); // NOLINT
|
||||
}
|
||||
if (userException) {
|
||||
FLAGS_celeborn_exception_user_stacktrace_rate_limit_ms =
|
||||
hasRateLimit ? kRateLimitIntervalMs : 0;
|
||||
FLAGS_celeborn_exception_system_stacktrace_rate_limit_ms =
|
||||
folly::Random::rand32();
|
||||
} else {
|
||||
// Set rate control to a large interval to avoid time related test
|
||||
// flakiness.
|
||||
FLAGS_celeborn_exception_system_stacktrace_rate_limit_ms =
|
||||
hasRateLimit ? kRateLimitIntervalMs : 0;
|
||||
FLAGS_celeborn_exception_user_stacktrace_rate_limit_ms =
|
||||
folly::Random::rand32();
|
||||
}
|
||||
for (int iter = 0; iter < 3; ++iter) {
|
||||
try {
|
||||
if (userException) {
|
||||
throw CelebornUserError(
|
||||
"file_name",
|
||||
1,
|
||||
"function_name()",
|
||||
"operator()",
|
||||
"test message",
|
||||
"",
|
||||
error_code::kArithmeticError,
|
||||
false);
|
||||
} else {
|
||||
throw CelebornRuntimeError(
|
||||
"file_name",
|
||||
1,
|
||||
"function_name()",
|
||||
"operator()",
|
||||
"test message",
|
||||
"",
|
||||
error_code::kArithmeticError,
|
||||
false);
|
||||
}
|
||||
} catch (CelebornException& e) {
|
||||
SCOPED_TRACE(fmt::format(
|
||||
"userException: {}, hasRateLimit: {}, user limit: {}ms, sys limit: {}ms",
|
||||
userException,
|
||||
hasRateLimit,
|
||||
FLAGS_celeborn_exception_user_stacktrace_rate_limit_ms,
|
||||
FLAGS_celeborn_exception_system_stacktrace_rate_limit_ms));
|
||||
ASSERT_EQ(
|
||||
userException, e.exceptionType() == CelebornException::Type::kUser);
|
||||
ASSERT_EQ(!hasRateLimit || ((iter % 2) == 0), e.stackTrace() != nullptr);
|
||||
// NOTE: with rate limit control, we want to verify if we can collect
|
||||
// stack trace after waiting for a while.
|
||||
if (hasRateLimit && (iter % 2 != 0)) {
|
||||
/* sleep override */
|
||||
std::this_thread::sleep_for(
|
||||
std::chrono::milliseconds(kRateLimitIntervalMs)); // NOLINT
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensures that expressions on the stream are not evaluated unless the condition
|
||||
// is met.
|
||||
TEST(ExceptionTest, lazyStreamEvaluation) {
|
||||
Counter c;
|
||||
|
||||
EXPECT_EQ(0, c.counter);
|
||||
CELEBORN_CHECK(true, "{}", c);
|
||||
EXPECT_EQ(0, c.counter);
|
||||
|
||||
EXPECT_THROW(
|
||||
([&]() { CELEBORN_CHECK(false, "{}", c); })(), CelebornRuntimeError);
|
||||
EXPECT_EQ(1, c.counter);
|
||||
|
||||
CELEBORN_CHECK(true, "{}", c);
|
||||
EXPECT_EQ(1, c.counter);
|
||||
|
||||
EXPECT_THROW(
|
||||
([&]() { CELEBORN_USER_CHECK(false, "{}", c); })(), CelebornUserError);
|
||||
EXPECT_EQ(2, c.counter);
|
||||
|
||||
EXPECT_THROW(
|
||||
([&]() { CELEBORN_CHECK(false, "{}", c); })(), CelebornRuntimeError);
|
||||
EXPECT_EQ(3, c.counter);
|
||||
|
||||
// Simple types.
|
||||
size_t i = 0;
|
||||
CELEBORN_CHECK(true, "{}", i++);
|
||||
EXPECT_EQ(0, i);
|
||||
CELEBORN_CHECK(true, "{}", ++i);
|
||||
EXPECT_EQ(0, i);
|
||||
|
||||
EXPECT_THROW(
|
||||
([&]() { CELEBORN_CHECK(false, "{}", i++); })(), CelebornRuntimeError);
|
||||
EXPECT_EQ(1, i);
|
||||
EXPECT_THROW(
|
||||
([&]() { CELEBORN_CHECK(false, "{}", ++i); })(), CelebornRuntimeError);
|
||||
EXPECT_EQ(2, i);
|
||||
}
|
||||
|
||||
TEST(ExceptionTest, messageCheck) {
|
||||
verifyCelebornException(
|
||||
[]() { CELEBORN_CHECK(4 > 5, "Test message 1"); },
|
||||
"Exception: CelebornRuntimeError\nError Source: RUNTIME\n"
|
||||
"Error Code: INVALID_STATE\nReason: Test message 1\n"
|
||||
"Retriable: False\nExpression: 4 > 5\nFunction: operator()\nFile: ");
|
||||
}
|
||||
|
||||
TEST(ExceptionTest, messageUnreachable) {
|
||||
verifyCelebornException(
|
||||
[]() { CELEBORN_UNREACHABLE("Test message 3"); },
|
||||
"Exception: CelebornRuntimeError\nError Source: RUNTIME\n"
|
||||
"Error Code: UNREACHABLE_CODE\nReason: Test message 3\n"
|
||||
"Retriable: False\nFunction: operator()\nFile: ");
|
||||
}
|
||||
|
||||
#define RUN_TEST(test) \
|
||||
TEST_##test( \
|
||||
CELEBORN_CHECK_##test, \
|
||||
"RUNTIME", \
|
||||
"INVALID_STATE", \
|
||||
"CelebornRuntimeError"); \
|
||||
TEST_##test( \
|
||||
CELEBORN_USER_CHECK_##test, \
|
||||
"USER", \
|
||||
"INVALID_ARGUMENT", \
|
||||
"CelebornUserError");
|
||||
|
||||
#define TEST_GT(macro, system, code, prefix) \
|
||||
verifyCelebornException( \
|
||||
[]() { macro(4, 5); }, \
|
||||
"Exception: " prefix "\nError Source: " system "\nError Code: " code \
|
||||
"\nReason: (4 vs. 5)" \
|
||||
"\nRetriable: False" \
|
||||
"\nExpression: 4 > 5" \
|
||||
"\nFunction: operator()" \
|
||||
"\nFile: "); \
|
||||
\
|
||||
verifyCelebornException( \
|
||||
[]() { macro(3, 3); }, \
|
||||
"Exception: " prefix "\nError Source: " system "\nError Code: " code \
|
||||
"\nReason: (3 vs. 3)" \
|
||||
"\nRetriable: False" \
|
||||
"\nExpression: 3 > 3" \
|
||||
"\nFunction: operator()" \
|
||||
"\nFile: "); \
|
||||
\
|
||||
verifyCelebornException( \
|
||||
[]() { macro(-1, 1, "Message 1"); }, \
|
||||
"Exception: " prefix "\nError Source: " system "\nError Code: " code \
|
||||
"\nReason: (-1 vs. 1) Message 1" \
|
||||
"\nRetriable: False" \
|
||||
"\nExpression: -1 > 1" \
|
||||
"\nFunction: operator()" \
|
||||
"\nFile: "); \
|
||||
\
|
||||
macro(3, 2); \
|
||||
macro(1, -1, "Message 2");
|
||||
|
||||
TEST(ExceptionTest, greaterThan) {
|
||||
RUN_TEST(GT);
|
||||
}
|
||||
|
||||
#define TEST_GE(macro, system, code, prefix) \
|
||||
verifyCelebornException( \
|
||||
[]() { macro(4, 5); }, \
|
||||
"Exception: " prefix "\nError Source: " system "\nError Code: " code \
|
||||
"\nReason: (4 vs. 5)" \
|
||||
"\nRetriable: False" \
|
||||
"\nExpression: 4 >= 5" \
|
||||
"\nFunction: operator()" \
|
||||
"\nFile: "); \
|
||||
\
|
||||
verifyCelebornException( \
|
||||
[]() { macro(-1, 1, "Message 1"); }, \
|
||||
"Exception: " prefix "\nError Source: " system "\nError Code: " code \
|
||||
"\nReason: (-1 vs. 1) Message 1" \
|
||||
"\nRetriable: False" \
|
||||
"\nExpression: -1 >= 1" \
|
||||
"\nFunction: operator()" \
|
||||
"\nFile: "); \
|
||||
\
|
||||
macro(3, 2); \
|
||||
macro(3, 3); \
|
||||
macro(1, -1, "Message 2");
|
||||
|
||||
TEST(ExceptionTest, greaterEqual) {
|
||||
RUN_TEST(GE);
|
||||
}
|
||||
|
||||
#define TEST_LT(macro, system, code, prefix) \
|
||||
verifyCelebornException( \
|
||||
[]() { macro(5, 4); }, \
|
||||
"Exception: " prefix "\nError Source: " system "\nError Code: " code \
|
||||
"\nReason: (5 vs. 4)" \
|
||||
"\nRetriable: False" \
|
||||
"\nExpression: 5 < 4" \
|
||||
"\nFunction: operator()" \
|
||||
"\nFile: "); \
|
||||
\
|
||||
verifyCelebornException( \
|
||||
[]() { macro(2, 2); }, \
|
||||
"Exception: " prefix "\nError Source: " system "\nError Code: " code \
|
||||
"\nReason: (2 vs. 2)" \
|
||||
"\nRetriable: False" \
|
||||
"\nExpression: 2 < 2" \
|
||||
"\nFunction: operator()" \
|
||||
"\nFile: "); \
|
||||
\
|
||||
verifyCelebornException( \
|
||||
[]() { macro(1, -1, "Message 1"); }, \
|
||||
"Exception: " prefix "\nError Source: " system "\nError Code: " code \
|
||||
"\nReason: (1 vs. -1) Message 1" \
|
||||
"\nRetriable: False" \
|
||||
"\nExpression: 1 < -1" \
|
||||
"\nFunction: operator()" \
|
||||
"\nFile: "); \
|
||||
\
|
||||
macro(2, 3); \
|
||||
macro(-1, 1, "Message 2");
|
||||
|
||||
TEST(ExceptionTest, lessThan) {
|
||||
RUN_TEST(LT);
|
||||
}
|
||||
|
||||
#define TEST_LE(macro, system, code, prefix) \
|
||||
verifyCelebornException( \
|
||||
[]() { macro(6, 2); }, \
|
||||
"Exception: " prefix "\nError Source: " system "\nError Code: " code \
|
||||
"\nReason: (6 vs. 2)" \
|
||||
"\nRetriable: False" \
|
||||
"\nExpression: 6 <= 2" \
|
||||
"\nFunction: operator()" \
|
||||
"\nFile: "); \
|
||||
\
|
||||
verifyCelebornException( \
|
||||
[]() { macro(3, -3, "Message 1"); }, \
|
||||
"Exception: " prefix "\nError Source: " system "\nError Code: " code \
|
||||
"\nReason: (3 vs. -3) Message 1" \
|
||||
"\nRetriable: False" \
|
||||
"\nExpression: 3 <= -3" \
|
||||
"\nFunction: operator()" \
|
||||
"\nFile: "); \
|
||||
\
|
||||
macro(5, 54); \
|
||||
macro(1, 1); \
|
||||
macro(-3, 3, "Message 2");
|
||||
|
||||
TEST(ExceptionTest, lessEqual) {
|
||||
RUN_TEST(LE);
|
||||
}
|
||||
|
||||
#define TEST_EQ(macro, system, code, prefix) \
|
||||
{ \
|
||||
verifyCelebornException( \
|
||||
[]() { macro(1, 2); }, \
|
||||
"Exception: " prefix "\nError Source: " system "\nError Code: " code \
|
||||
"\nReason: (1 vs. 2)" \
|
||||
"\nRetriable: False" \
|
||||
"\nExpression: 1 == 2" \
|
||||
"\nFunction: operator()" \
|
||||
"\nFile: "); \
|
||||
\
|
||||
verifyCelebornException( \
|
||||
[]() { macro(2, 1, "Message 1"); }, \
|
||||
"Exception: " prefix "\nError Source: " system "\nError Code: " code \
|
||||
"\nReason: (2 vs. 1) Message 1" \
|
||||
"\nRetriable: False" \
|
||||
"\nExpression: 2 == 1" \
|
||||
"\nFunction: operator()" \
|
||||
"\nFile: "); \
|
||||
\
|
||||
auto t = true; \
|
||||
auto f = false; \
|
||||
macro(521, 521); \
|
||||
macro(1.1, 1.1); \
|
||||
macro(true, t, "Message 2"); \
|
||||
macro(f, false, "Message 3"); \
|
||||
}
|
||||
|
||||
TEST(ExceptionTest, equal) {
|
||||
RUN_TEST(EQ);
|
||||
}
|
||||
|
||||
#define TEST_NE(macro, system, code, prefix) \
|
||||
{ \
|
||||
verifyCelebornException( \
|
||||
[]() { macro(1, 1); }, \
|
||||
"Exception: " prefix "\nError Source: " system "\nError Code: " code \
|
||||
"\nReason: (1 vs. 1)" \
|
||||
"\nRetriable: False" \
|
||||
"\nExpression: 1 != 1" \
|
||||
"\nFunction: operator()" \
|
||||
"\nFile: "); \
|
||||
\
|
||||
verifyCelebornException( \
|
||||
[]() { macro(2.2, 2.2, "Message 1"); }, \
|
||||
"Exception: " prefix "\nError Source: " system "\nError Code: " code \
|
||||
"\nReason: (2.2 vs. 2.2) Message 1" \
|
||||
"\nRetriable: False" \
|
||||
"\nExpression: 2.2 != 2.2" \
|
||||
"\nFunction: operator()" \
|
||||
"\nFile: "); \
|
||||
\
|
||||
auto t = true; \
|
||||
auto f = false; \
|
||||
macro(521, 522); \
|
||||
macro(1.2, 1.1); \
|
||||
macro(true, f, "Message 2"); \
|
||||
macro(t, false, "Message 3"); \
|
||||
}
|
||||
|
||||
TEST(ExceptionTest, notEqual) {
|
||||
RUN_TEST(NE);
|
||||
}
|
||||
|
||||
#define TEST_NOT_NULL(macro, system, code, prefix) \
|
||||
{ \
|
||||
verifyCelebornException( \
|
||||
[]() { macro(nullptr); }, \
|
||||
"Exception: " prefix "\nError Source: " system "\nError Code: " code \
|
||||
"\nRetriable: False" \
|
||||
"\nExpression: nullptr != nullptr" \
|
||||
"\nFunction: operator()" \
|
||||
"\nFile: "); \
|
||||
verifyCelebornException( \
|
||||
[]() { \
|
||||
std::shared_ptr<int> a; \
|
||||
macro(a, "Message 1"); \
|
||||
}, \
|
||||
"Exception: " prefix "\nError Source: " system "\nError Code: " code \
|
||||
"\nReason: Message 1" \
|
||||
"\nRetriable: False" \
|
||||
"\nExpression: a != nullptr" \
|
||||
"\nFunction: operator()" \
|
||||
"\nFile: "); \
|
||||
auto b = std::make_shared<int>(5); \
|
||||
macro(b); \
|
||||
}
|
||||
|
||||
TEST(ExceptionTest, notNull) {
|
||||
RUN_TEST(NOT_NULL);
|
||||
}
|
||||
|
||||
TEST(ExceptionTest, expressionString) {
|
||||
size_t i = 1;
|
||||
size_t j = 100;
|
||||
constexpr auto msgTemplate =
|
||||
"Exception: CelebornRuntimeError"
|
||||
"\nError Source: RUNTIME"
|
||||
"\nError Code: INVALID_STATE"
|
||||
"\nReason: ({1})"
|
||||
"\nRetriable: False"
|
||||
"\nExpression: {0}"
|
||||
"\nFunction: operator()"
|
||||
"\nFile: ";
|
||||
|
||||
verifyCelebornException(
|
||||
[&]() { CELEBORN_CHECK_EQ(i, j); },
|
||||
fmt::format(msgTemplate, "i == j", "1 vs. 100"));
|
||||
|
||||
verifyCelebornException(
|
||||
[&]() { CELEBORN_CHECK_NE(i, 1); },
|
||||
fmt::format(msgTemplate, "i != 1", "1 vs. 1"));
|
||||
|
||||
verifyCelebornException(
|
||||
[&]() { CELEBORN_CHECK_LT(i + j, j); },
|
||||
fmt::format(msgTemplate, "i + j < j", "101 vs. 100"));
|
||||
|
||||
verifyCelebornException(
|
||||
[&]() { CELEBORN_CHECK_GE(i + j * 2, 1000); },
|
||||
fmt::format(msgTemplate, "i + j * 2 >= 1000", "201 vs. 1000"));
|
||||
}
|
||||
|
||||
TEST(ExceptionTest, notImplemented) {
|
||||
verifyCelebornException(
|
||||
[]() { CELEBORN_NYI(); },
|
||||
"Exception: CelebornRuntimeError\nError Source: RUNTIME\n"
|
||||
"Error Code: NOT_IMPLEMENTED\n"
|
||||
"Retriable: False\nFunction: operator()\nFile: ");
|
||||
|
||||
verifyCelebornException(
|
||||
[]() { CELEBORN_NYI("Message 1"); },
|
||||
"Exception: CelebornRuntimeError\nError Source: RUNTIME\n"
|
||||
"Error Code: NOT_IMPLEMENTED\nReason: Message 1\nRetriable: False\n"
|
||||
"Function: operator()\nFile: ");
|
||||
}
|
||||
|
||||
TEST(ExceptionTest, errorCode) {
|
||||
std::string msgTemplate =
|
||||
"Exception: {}"
|
||||
"\nError Source: {}"
|
||||
"\nError Code: {}"
|
||||
"\nRetriable: {}"
|
||||
"\nExpression: {}"
|
||||
"\nFunction: {}"
|
||||
"\nFile: ";
|
||||
|
||||
verifyCelebornException(
|
||||
[&]() { CELEBORN_FAIL(); },
|
||||
fmt::format(
|
||||
"Exception: {}"
|
||||
"\nError Source: {}"
|
||||
"\nError Code: {}"
|
||||
"\nRetriable: {}"
|
||||
"\nFunction: {}"
|
||||
"\nFile: ",
|
||||
"CelebornRuntimeError",
|
||||
"RUNTIME",
|
||||
"INVALID_STATE",
|
||||
"False",
|
||||
"operator()"));
|
||||
|
||||
verifyCelebornException(
|
||||
[&]() { CELEBORN_USER_FAIL(); },
|
||||
fmt::format(
|
||||
"Exception: {}"
|
||||
"\nError Source: {}"
|
||||
"\nError Code: {}"
|
||||
"\nRetriable: {}"
|
||||
"\nFunction: {}"
|
||||
"\nFile: ",
|
||||
"CelebornUserError",
|
||||
"USER",
|
||||
"INVALID_ARGUMENT",
|
||||
"False",
|
||||
"operator()"));
|
||||
}
|
||||
|
||||
TEST(ExceptionTest, context) {
|
||||
// No context.
|
||||
verifyCelebornException(
|
||||
[&]() { CELEBORN_CHECK_EQ(1, 3); },
|
||||
"Exception: CelebornRuntimeError"
|
||||
"\nError Source: RUNTIME"
|
||||
"\nError Code: INVALID_STATE"
|
||||
"\nReason: (1 vs. 3)"
|
||||
"\nRetriable: False"
|
||||
"\nExpression: 1 == 3"
|
||||
"\nFunction: operator()"
|
||||
"\nFile: ");
|
||||
|
||||
// With context.
|
||||
int callCount = 0;
|
||||
|
||||
struct MessageFunctionArg {
|
||||
std::string message;
|
||||
int* callCount;
|
||||
};
|
||||
|
||||
auto messageFunction = [](celeborn::CelebornException::Type exceptionType,
|
||||
void* untypedArg) {
|
||||
auto arg = static_cast<MessageFunctionArg*>(untypedArg);
|
||||
++(*arg->callCount);
|
||||
switch (exceptionType) {
|
||||
case celeborn::CelebornException::Type::kUser:
|
||||
return fmt::format("User error: {}", arg->message);
|
||||
case celeborn::CelebornException::Type::kSystem:
|
||||
return fmt::format("System error: {}", arg->message);
|
||||
default:
|
||||
return fmt::format("Unexpected error type: {}", arg->message);
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
// Create multi-layer contexts with top level marked as essential.
|
||||
MessageFunctionArg topLevelTroubleshootingAid{
|
||||
"Top-level troubleshooting aid.", &callCount};
|
||||
celeborn::ExceptionContextSetter additionalContext(
|
||||
{.messageFunc = messageFunction, .arg = &topLevelTroubleshootingAid});
|
||||
|
||||
MessageFunctionArg midLevelTroubleshootingAid{
|
||||
"Mid-level troubleshooting aid.", &callCount};
|
||||
celeborn::ExceptionContextSetter midLevelContext(
|
||||
{messageFunction, &midLevelTroubleshootingAid});
|
||||
|
||||
MessageFunctionArg innerLevelTroubleshootingAid{
|
||||
"Inner-level troubleshooting aid.", &callCount};
|
||||
celeborn::ExceptionContextSetter innerLevelContext(
|
||||
{messageFunction, &innerLevelTroubleshootingAid});
|
||||
|
||||
verifyCelebornException(
|
||||
[&]() { CELEBORN_CHECK_EQ(1, 3); },
|
||||
"Exception: CelebornRuntimeError"
|
||||
"\nError Source: RUNTIME"
|
||||
"\nError Code: INVALID_STATE"
|
||||
"\nReason: (1 vs. 3)"
|
||||
"\nRetriable: False"
|
||||
"\nExpression: 1 == 3"
|
||||
"\nContext: System error: Inner-level troubleshooting aid."
|
||||
"\nTop-Level Context: System error: Top-level troubleshooting aid."
|
||||
"\nFunction: operator()"
|
||||
"\nFile: ");
|
||||
|
||||
EXPECT_EQ(2, callCount);
|
||||
|
||||
verifyCelebornException(
|
||||
[&]() { CELEBORN_USER_CHECK_EQ(1, 3); },
|
||||
"Exception: CelebornUserError"
|
||||
"\nError Source: USER"
|
||||
"\nError Code: INVALID_ARGUMENT"
|
||||
"\nReason: (1 vs. 3)"
|
||||
"\nRetriable: False"
|
||||
"\nExpression: 1 == 3"
|
||||
"\nContext: User error: Inner-level troubleshooting aid."
|
||||
"\nTop-Level Context: User error: Top-level troubleshooting aid."
|
||||
"\nFunction: operator()"
|
||||
"\nFile: ");
|
||||
|
||||
EXPECT_EQ(4, callCount);
|
||||
}
|
||||
|
||||
{
|
||||
callCount = 0;
|
||||
// Create multi-layer contexts with none marked as essential.
|
||||
MessageFunctionArg topLevelTroubleshootingAid{
|
||||
"Top-level troubleshooting aid.", &callCount};
|
||||
celeborn::ExceptionContextSetter additionalContext(
|
||||
{.messageFunc = messageFunction, .arg = &topLevelTroubleshootingAid});
|
||||
|
||||
MessageFunctionArg midLevelTroubleshootingAid{
|
||||
"Mid-level troubleshooting aid.", &callCount};
|
||||
celeborn::ExceptionContextSetter midLevelContext(
|
||||
{.messageFunc = messageFunction, .arg = &midLevelTroubleshootingAid});
|
||||
|
||||
MessageFunctionArg innerLevelTroubleshootingAid{
|
||||
"Inner-level troubleshooting aid.", &callCount};
|
||||
celeborn::ExceptionContextSetter innerLevelContext(
|
||||
{messageFunction, &innerLevelTroubleshootingAid});
|
||||
|
||||
verifyCelebornException(
|
||||
[&]() { CELEBORN_CHECK_EQ(1, 3); },
|
||||
"Exception: CelebornRuntimeError"
|
||||
"\nError Source: RUNTIME"
|
||||
"\nError Code: INVALID_STATE"
|
||||
"\nReason: (1 vs. 3)"
|
||||
"\nRetriable: False"
|
||||
"\nExpression: 1 == 3"
|
||||
"\nContext: System error: Inner-level troubleshooting aid."
|
||||
"\nTop-Level Context: System error: Top-level troubleshooting aid."
|
||||
"\nFunction: operator()"
|
||||
"\nFile: ");
|
||||
|
||||
EXPECT_EQ(2, callCount);
|
||||
|
||||
verifyCelebornException(
|
||||
[&]() { CELEBORN_USER_CHECK_EQ(1, 3); },
|
||||
"Exception: CelebornUserError"
|
||||
"\nError Source: USER"
|
||||
"\nError Code: INVALID_ARGUMENT"
|
||||
"\nReason: (1 vs. 3)"
|
||||
"\nRetriable: False"
|
||||
"\nExpression: 1 == 3"
|
||||
"\nContext: User error: Inner-level troubleshooting aid."
|
||||
"\nTop-Level Context: User error: Top-level troubleshooting aid."
|
||||
"\nFunction: operator()"
|
||||
"\nFile: ");
|
||||
|
||||
EXPECT_EQ(4, callCount);
|
||||
}
|
||||
|
||||
// Different context.
|
||||
{
|
||||
callCount = 0;
|
||||
|
||||
// Create a single layer of context. Context and top-level context are
|
||||
// expected to be the same.
|
||||
MessageFunctionArg debuggingInfo{"Debugging info.", &callCount};
|
||||
celeborn::ExceptionContextSetter context({messageFunction, &debuggingInfo});
|
||||
|
||||
verifyCelebornException(
|
||||
[&]() { CELEBORN_CHECK_EQ(1, 3); },
|
||||
"Exception: CelebornRuntimeError"
|
||||
"\nError Source: RUNTIME"
|
||||
"\nError Code: INVALID_STATE"
|
||||
"\nReason: (1 vs. 3)"
|
||||
"\nRetriable: False"
|
||||
"\nExpression: 1 == 3"
|
||||
"\nContext: System error: Debugging info."
|
||||
"\nTop-Level Context: Same as context."
|
||||
"\nFunction: operator()"
|
||||
"\nFile: ");
|
||||
|
||||
EXPECT_EQ(1, callCount);
|
||||
|
||||
verifyCelebornException(
|
||||
[&]() { CELEBORN_USER_CHECK_EQ(1, 3); },
|
||||
"Exception: CelebornUserError"
|
||||
"\nError Source: USER"
|
||||
"\nError Code: INVALID_ARGUMENT"
|
||||
"\nReason: (1 vs. 3)"
|
||||
"\nRetriable: False"
|
||||
"\nExpression: 1 == 3"
|
||||
"\nContext: User error: Debugging info."
|
||||
"\nTop-Level Context: Same as context."
|
||||
"\nFunction: operator()"
|
||||
"\nFile: ");
|
||||
|
||||
EXPECT_EQ(2, callCount);
|
||||
}
|
||||
|
||||
callCount = 0;
|
||||
|
||||
// No context.
|
||||
verifyCelebornException(
|
||||
[&]() { CELEBORN_CHECK_EQ(1, 3); },
|
||||
"Exception: CelebornRuntimeError"
|
||||
"\nError Source: RUNTIME"
|
||||
"\nError Code: INVALID_STATE"
|
||||
"\nReason: (1 vs. 3)"
|
||||
"\nRetriable: False"
|
||||
"\nExpression: 1 == 3"
|
||||
"\nFunction: operator()"
|
||||
"\nFile: ");
|
||||
|
||||
EXPECT_EQ(0, callCount);
|
||||
|
||||
// With message function throwing an exception.
|
||||
auto throwingMessageFunction =
|
||||
[](celeborn::CelebornException::Type /*exceptionType*/,
|
||||
void* untypedArg) -> std::string {
|
||||
auto arg = static_cast<MessageFunctionArg*>(untypedArg);
|
||||
++(*arg->callCount);
|
||||
CELEBORN_FAIL("Test failure.");
|
||||
};
|
||||
{
|
||||
MessageFunctionArg debuggingInfo{"Debugging info.", &callCount};
|
||||
celeborn::ExceptionContextSetter context(
|
||||
{throwingMessageFunction, &debuggingInfo});
|
||||
|
||||
verifyCelebornException(
|
||||
[&]() { CELEBORN_CHECK_EQ(1, 3); },
|
||||
"Exception: CelebornRuntimeError"
|
||||
"\nError Source: RUNTIME"
|
||||
"\nError Code: INVALID_STATE"
|
||||
"\nReason: (1 vs. 3)"
|
||||
"\nRetriable: False"
|
||||
"\nExpression: 1 == 3"
|
||||
"\nContext: Failed to produce additional context."
|
||||
"\nTop-Level Context: Same as context."
|
||||
"\nFunction: operator()"
|
||||
"\nFile: ");
|
||||
|
||||
EXPECT_EQ(1, callCount);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ExceptionTest, traceCollectionEnabling) {
|
||||
// Switch on/off tests.
|
||||
for (const bool enabled : {false, true}) {
|
||||
for (const bool userException : {false, true}) {
|
||||
testExceptionTraceCollectionControl(userException, enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ExceptionTest, traceCollectionRateControl) {
|
||||
// Rate limit tests.
|
||||
for (const bool withLimit : {false, true}) {
|
||||
for (const bool userException : {false, true}) {
|
||||
testExceptionTraceCollectionRateControl(userException, withLimit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ExceptionTest, wrappedException) {
|
||||
try {
|
||||
throw std::invalid_argument("This is a test.");
|
||||
} catch (const std::exception& e) {
|
||||
CelebornUserError ve(std::current_exception(), e.what(), false);
|
||||
ASSERT_EQ(ve.message(), "This is a test.");
|
||||
ASSERT_TRUE(ve.isUserError());
|
||||
ASSERT_EQ(ve.context(), "");
|
||||
ASSERT_EQ(ve.topLevelContext(), "");
|
||||
ASSERT_THROW(
|
||||
std::rethrow_exception(ve.wrappedException()), std::invalid_argument);
|
||||
}
|
||||
|
||||
try {
|
||||
throw std::invalid_argument("This is a test.");
|
||||
} catch (const std::exception& e) {
|
||||
CelebornRuntimeError ve(std::current_exception(), e.what(), false);
|
||||
ASSERT_EQ(ve.message(), "This is a test.");
|
||||
ASSERT_FALSE(ve.isUserError());
|
||||
ASSERT_EQ(ve.context(), "");
|
||||
ASSERT_EQ(ve.topLevelContext(), "");
|
||||
ASSERT_THROW(
|
||||
std::rethrow_exception(ve.wrappedException()), std::invalid_argument);
|
||||
}
|
||||
|
||||
try {
|
||||
CELEBORN_FAIL("This is a test.");
|
||||
} catch (const CelebornException& e) {
|
||||
ASSERT_EQ(e.message(), "This is a test.");
|
||||
ASSERT_TRUE(e.wrappedException() == nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ExceptionTest, wrappedExceptionWithContext) {
|
||||
auto messageFunction = [](celeborn::CelebornException::Type exceptionType,
|
||||
void* untypedArg) {
|
||||
auto data = static_cast<char*>(untypedArg);
|
||||
switch (exceptionType) {
|
||||
case celeborn::CelebornException::Type::kUser:
|
||||
return fmt::format("User error: {}", data);
|
||||
case celeborn::CelebornException::Type::kSystem:
|
||||
return fmt::format("System error: {}", data);
|
||||
default:
|
||||
return fmt::format("Unexpected error type: {}", data);
|
||||
}
|
||||
};
|
||||
|
||||
std::string data = "lakes";
|
||||
celeborn::ExceptionContextSetter context({messageFunction, data.data()});
|
||||
|
||||
try {
|
||||
throw std::invalid_argument("This is a test.");
|
||||
} catch (const std::exception& e) {
|
||||
CelebornUserError ve(std::current_exception(), e.what(), false);
|
||||
ASSERT_EQ(ve.message(), "This is a test.");
|
||||
ASSERT_TRUE(ve.isUserError());
|
||||
ASSERT_EQ(ve.context(), "User error: lakes");
|
||||
ASSERT_EQ(ve.topLevelContext(), "Same as context.");
|
||||
ASSERT_THROW(
|
||||
std::rethrow_exception(ve.wrappedException()), std::invalid_argument);
|
||||
}
|
||||
|
||||
try {
|
||||
throw std::invalid_argument("This is a test.");
|
||||
} catch (const std::exception& e) {
|
||||
CelebornRuntimeError ve(std::current_exception(), e.what(), false);
|
||||
ASSERT_EQ(ve.message(), "This is a test.");
|
||||
ASSERT_FALSE(ve.isUserError());
|
||||
ASSERT_EQ(ve.context(), "System error: lakes");
|
||||
ASSERT_EQ(ve.topLevelContext(), "Same as context.");
|
||||
ASSERT_THROW(
|
||||
std::rethrow_exception(ve.wrappedException()), std::invalid_argument);
|
||||
}
|
||||
|
||||
std::string innerData = "mountains";
|
||||
celeborn::ExceptionContextSetter innerContext(
|
||||
{messageFunction, innerData.data()});
|
||||
|
||||
try {
|
||||
throw std::invalid_argument("This is a test.");
|
||||
} catch (const std::exception& e) {
|
||||
CelebornUserError ve(std::current_exception(), e.what(), false);
|
||||
ASSERT_EQ(ve.message(), "This is a test.");
|
||||
ASSERT_TRUE(ve.isUserError());
|
||||
ASSERT_EQ(ve.context(), "User error: mountains");
|
||||
ASSERT_EQ(ve.topLevelContext(), "User error: lakes");
|
||||
ASSERT_THROW(
|
||||
std::rethrow_exception(ve.wrappedException()), std::invalid_argument);
|
||||
}
|
||||
|
||||
try {
|
||||
throw std::invalid_argument("This is a test.");
|
||||
} catch (const std::exception& e) {
|
||||
CelebornRuntimeError ve(std::current_exception(), e.what(), false);
|
||||
ASSERT_EQ(ve.message(), "This is a test.");
|
||||
ASSERT_FALSE(ve.isUserError());
|
||||
ASSERT_EQ(ve.context(), "System error: mountains");
|
||||
ASSERT_EQ(ve.topLevelContext(), "System error: lakes");
|
||||
ASSERT_THROW(
|
||||
std::rethrow_exception(ve.wrappedException()), std::invalid_argument);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ExceptionTest, exceptionMacroInlining) {
|
||||
// Verify that the right formatting method is inlined when using
|
||||
// _CELEBORN_THROW macro. This test can be removed if fmt::vformat changes
|
||||
// behavior and starts ignoring extra brackets.
|
||||
|
||||
// The following string should throw an error when passed to fmt::vformat.
|
||||
std::string errorStr = "This {} {is a test.";
|
||||
// Inlined with the method that directly returns the std::string input.
|
||||
try {
|
||||
CELEBORN_USER_FAIL(errorStr);
|
||||
} catch (const CelebornUserError& ve) {
|
||||
ASSERT_EQ(ve.message(), errorStr);
|
||||
}
|
||||
|
||||
// Inlined with the method that directly returns the char* input.
|
||||
try {
|
||||
CELEBORN_USER_FAIL(errorStr.c_str());
|
||||
} catch (const CelebornUserError& ve) {
|
||||
ASSERT_EQ(ve.message(), errorStr);
|
||||
}
|
||||
|
||||
// Inlined with the method that passes the errorStr and the next argument via
|
||||
// fmt::vformat. Should throw format_error.
|
||||
try {
|
||||
CELEBORN_USER_FAIL(errorStr, "definitely");
|
||||
} catch (const std::exception& e) {
|
||||
ASSERT_TRUE(folly::StringPiece{e.what()}.startsWith("argument not found"));
|
||||
}
|
||||
}
|
||||
@ -65,6 +65,21 @@ BOOST_VERSION="boost-1.84.0"
|
||||
ARROW_VERSION="15.0.0"
|
||||
STEMMER_VERSION="2.2.0"
|
||||
|
||||
# Install gtest library package for tests.
|
||||
function install_gtest {
|
||||
${SUDO} apt-get install -y libgtest-dev cmake
|
||||
mkdir -p $HOME/build
|
||||
pushd $HOME/build
|
||||
${SUDO} cmake /usr/src/googletest/googletest
|
||||
${SUDO} make
|
||||
${SUDO} cp lib/libgtest* /usr/lib/
|
||||
popd
|
||||
${SUDO} rm -rf $HOME/build
|
||||
${SUDO} mkdir /usr/local/lib/googletest
|
||||
${SUDO} ln -s /usr/lib/libgtest.a /usr/local/lib/googletest/libgtest.a
|
||||
${SUDO} ln -s /usr/lib/libgtest_main.a /usr/local/lib/googletest/libgtest_main.a
|
||||
}
|
||||
|
||||
# Install packages required for build.
|
||||
function install_build_prerequisites {
|
||||
${SUDO} apt update
|
||||
@ -82,6 +97,8 @@ function install_build_prerequisites {
|
||||
pkg-config \
|
||||
gdb \
|
||||
wget
|
||||
|
||||
install_gtest
|
||||
|
||||
# Install to /usr/local to make it available to all users.
|
||||
${SUDO} pip3 install cmake==3.28.3
|
||||
@ -140,8 +157,8 @@ function install_protobuf {
|
||||
cd ${DEPENDENCY_DIR}/protobuf
|
||||
./configure CXXFLAGS="-fPIC" --prefix=${INSTALL_PREFIX}
|
||||
make "-j${NPROC}"
|
||||
sudo make install
|
||||
sudo ldconfig
|
||||
${SUDO} make install
|
||||
${SUDO} ldconfig
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user