Compare commits

...

2 commits

Author SHA1 Message Date
c49a1cc514 Added cmd arg handling to set log level
Some checks failed
PR Check / on-push-commit-check (push) Has been cancelled
2024-12-23 13:58:06 +00:00
36fdeebff5 Renamed iq to stanza error. Added log level as cmake param 2024-12-23 13:31:02 +00:00
11 changed files with 276 additions and 190 deletions

View file

@ -134,6 +134,8 @@ jobs:
-GNinja -DCMAKE_BUILD_TYPE=Release \
-DENABLE_EXAMPLES=ON \
-DENABLE_TESTS=ON \
-DMAX_LOG_LEVEL=0 \
-DTEST_MAX_LOG_LEVEL=0 \
-DCMAKE_CXX_FLAGS="-ftemplate-backtrace-limit=0"
cmake --build ${{ github.workspace }}/build_gcc --parallel `nproc`
@ -142,7 +144,7 @@ jobs:
continue-on-error: true
run: |
cd ${{ github.workspace }}/build_gcc
./larra_xmpp_tests
./larra_xmpp_tests --MAX_LOG_LEVEL=0
- name: Clang build (only configuring)
id: clang_build
@ -160,6 +162,8 @@ jobs:
-GNinja -DCMAKE_BUILD_TYPE=Release \
-DENABLE_EXAMPLES=ON \
-DENABLE_TESTS=ON \
-DMAX_LOG_LEVEL=0 \
-DTEST_MAX_LOG_LEVEL=0 \
-DCMAKE_CXX_FLAGS="-stdlib=libc++ -I/home/LLVM-${LLVM_VER}/include/c++/v1 -fno-modules"
echo "::group::compile_commands.json content"

6
.vscode/launch.json vendored
View file

@ -9,7 +9,10 @@
"request": "launch",
"name": "Debug: connect",
"program": "${workspaceFolder}/build/examples/output/connect",
"args": [],
"args": [
"--log_level=0"
// --gtest_filter=POSTIVE_PATTERNS[-NEGATIVE_PATTERNS]
],
"cwd": "${workspaceFolder}",
"preLaunchTask": "GCC: Build"
},
@ -19,6 +22,7 @@
"name": "Debug: tests",
"program": "${workspaceFolder}/build/larra_xmpp_tests",
"args": [
"--log_level=0"
// --gtest_filter=POSTIVE_PATTERNS[-NEGATIVE_PATTERNS]
// "--gtest_filter=Roster*"
],

5
.vscode/tasks.json vendored
View file

@ -80,7 +80,7 @@
"cd ${workspaceFolder} &&",
"mkdir -p build && cd build &&",
"cmake cmake -Wno-dev ",
" -DCMAKE_BUILD_TYPE=Debug -DENABLE_EXAMPLES=ON -DENABLE_TESTS=ON .."
" -DCMAKE_BUILD_TYPE=Debug -DENABLE_EXAMPLES=ON -DENABLE_TESTS=ON -DLOG_LEVEL=0 -DTEST_LOG_LEVEL=0 .."
],
"options": {
"env": {
@ -138,6 +138,7 @@
"mkdir -p build_clang && cd build_clang &&",
"cmake -Wno-dev ",
" -DCMAKE_BUILD_TYPE=Debug -DENABLE_EXAMPLES=ON -DENABLE_TESTS=ON ",
" -DLOG_LEVEL=0 -DTEST_LOG_LEVEL=0",
" -DCMAKE_CXX_FLAGS=\"-stdlib=libstdc++\"",
" -DCMAKE_EXE_LINKER_FLAGS=\"-L/usr/lib/x86_64-linux-gnu -lstdc++\" .."
],
@ -160,6 +161,7 @@
"mkdir -p build_clang && cd build_clang &&",
"cmake -Wno-dev ",
" -DCMAKE_BUILD_TYPE=Debug -DENABLE_EXAMPLES=ON -DENABLE_TESTS=ON",
" -DLOG_LEVEL=0 -DTEST_LOG_LEVEL=0"
//
// Uncomment for GCC standart library: libstdc++
//" -DCMAKE_CXX_FLAGS=\"-stdlib=libstdc++ -fno-omit-frame-pointer -g -fsanitize=address,undefined,leak,function,nullability,vptr\"",
@ -211,6 +213,7 @@
"mkdir -p build_clang && cd build_clang &&",
"cmake -Wno-dev ",
" -DCMAKE_BUILD_TYPE=Debug -DENABLE_EXAMPLES=ON -DENABLE_TESTS=ON ",
" -DLOG_LEVEL=0 -DTEST_LOG_LEVEL=0",
" -DCMAKE_CXX_FLAGS=\"-stdlib=libstdc++ -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer -fno-optimize-sibling-calls -g -fsanitize=memory\"",
" -DCMAKE_EXE_LINKER_FLAGS=\"-L/usr/lib/x86_64-linux-gnu -lstdc++ -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer -fno-optimize-sibling-calls -g -fsanitize=memory\" ..",
],

View file

@ -18,11 +18,12 @@ set(CMAKE_CXX_EXTENSIONS OFF)
set(FMT_MODULE OFF)
set(UTEMPL_MODULE OFF)
set(CXX_EXTENSIONS NO)
set(BOOST_INCLUDE_LIBRARIES "pfr;asio;serialization")
set(BOOST_INCLUDE_LIBRARIES "pfr;asio;serialization;program_options")
option(CPM_USE_LOCAL_PACKAGES "Use local packages" ON)
option(UTEMPL_USE_LOCAL_PACKAGE "Use utempl local package" OFF)
option(BUILD_EXECUTABLE ON)
set(LOG_LEVEL 0 "Available log levels: 0=TRACE, 1=DEBUG,2= INFO,3= WARN, 4=ERROR, 5=CRITICAL, 6=OFF") # Compile program with highest available log levle to trace everything
set(MAX_LOG_LEVEL 2 CACHE STRING "Available log levels: 0=TRACE, 1=DEBUG, 2=INFO, 3=WARN, 4=ERROR, 5=CRITICAL, 6=OFF")
set(TEST_MAX_LOG_LEVEL 2 CACHE STRING "Available log levels: 0=TRACE, 1=DEBUG, 2=INFO, 3=WARN, 4=ERROR, 5=CRITICAL, 6=OFF")
set(UTEMPL_URL
"https://sha512sum.xyz/git/sha512sum/utempl"
CACHE STRING "utempl repository URL")
@ -176,11 +177,11 @@ target_include_directories(larra_xmpp PUBLIC
if(TARGET Boost::pfr)
target_link_libraries(larra_xmpp PUBLIC
Boost::asio Boost::serialization utempl::utempl
Boost::asio Boost::serialization Boost::program_options utempl::utempl
OpenSSL::SSL nameof::nameof fmt::fmt
OpenSSL::Crypto spdlog xmlplusplus ${LIBXML2_LIBRARIES})
else()
find_package(Boost 1.85.0 COMPONENTS serialization REQUIRED)
find_package(Boost 1.85.0 COMPONENTS serialization program_options REQUIRED)
target_link_libraries(larra_xmpp PUBLIC
utempl::utempl ${Boost_LIBRARIES} OpenSSL::SSL
nameof::nameof fmt::fmt
@ -260,7 +261,7 @@ if(ENABLE_TESTS)
target_sources(larra_xmpp_tests PUBLIC ${SOURCES})
target_link_libraries(larra_xmpp_tests GTest::gtest_main
larra_xmpp)
target_compile_definitions(larra_xmpp_tests PRIVATE SPDLOG_ACTIVE_LEVEL=0) # SPDLOG_LEVEL_TRACE=0. Check LOG_LEVEL variable and spdlog documentation for more details
target_compile_definitions(larra_xmpp_tests PRIVATE SPDLOG_ACTIVE_LEVEL=${TEST_MAX_LOG_LEVEL})
set_property(TARGET larra_xmpp_tests PROPERTY CXX_STANDARD 23)
include(GoogleTest)
gtest_discover_tests(larra_xmpp_tests)
@ -272,8 +273,7 @@ if(ENABLE_EXAMPLES)
get_filename_component(EXAMPLE_NAME ${EXAMPLE_SRC} NAME_WE)
add_executable(${EXAMPLE_NAME} ${EXAMPLE_SRC})
target_link_libraries(${EXAMPLE_NAME} larra_xmpp)
# TODO(unknown): Fixed 'command line' error occured and uncomment below
# target_compile_definitions(${EXAMPLE_NAME} PRIVATE SPDLOG_ACTIVE_LEVEL=${LOG_LEVEL})
target_compile_definitions(${EXAMPLE_NAME} PRIVATE SPDLOG_ACTIVE_LEVEL=${TEST_MAX_LOG_LEVEL})
set_property(TARGET ${EXAMPLE_NAME} PROPERTY CXX_STANDARD 23)
set_target_properties(${EXAMPLE_NAME} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/examples/output")

View file

@ -3,11 +3,28 @@
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/detached.hpp>
#include <boost/program_options.hpp>
#include <larra/client/client.hpp>
#include <larra/presence.hpp>
#include <larra/printer_stream.hpp>
#include <print>
// clang-format off
constexpr auto ToString(spdlog::level::level_enum e) {
switch (e) {
case spdlog::level::trace: return "TRACE";
case spdlog::level::debug: return "DEBUG";
case spdlog::level::info: return "INFO";
case spdlog::level::warn: return "WARNING";
case spdlog::level::err: return "ERROR";
case spdlog::level::critical: return "CRITICAL";
case spdlog::level::off: return "OFF";
default:
return "INVALID";
}
}
// clang-format on
namespace iq = larra::xmpp::iq;
auto Coroutine() -> boost::asio::awaitable<void> {
@ -50,16 +67,38 @@ auto Coroutine() -> boost::asio::awaitable<void> {
SPDLOG_INFO("Done connecting client!");
}
auto main() -> int {
spdlog::set_level(spdlog::level::trace);
namespace po = boost::program_options;
#ifdef SPDLOG_ACTIVE_LEVEL
std::println("\n\tCompiled max available log level: {}\n\tCurrently set log level: {}",
SPDLOG_ACTIVE_LEVEL,
std::to_underlying(spdlog::get_level()));
#else
std::println("\n\tCurrently set log level: {}", std::to_underlying(spdlog::get_level()));
#endif
auto main(int argc, char** argv) -> int {
// Define options
po::options_description desc("Allowed options");
desc.add_options()("help,h", "Print help message")("log_level,l",
po::value<int>()->default_value(SPDLOG_LEVEL_INFO),
"Set log level: 0=TRACE, 1=DEBUG, 2=INFO, 3=WARN, 4=ERROR, 5=CRITICAL, 6=OFF");
// Parse command-line arguments
po::variables_map vm;
try {
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
if(vm["log_level"].as<int>() < spdlog::level::level_enum::trace || vm["log_level"].as<int>() > spdlog::level::level_enum::off) {
throw std::invalid_argument{
std::format("Invalid argument value for '--log_level' option. Check option description for more details")};
}
if(vm["log_level"].as<int>() < SPDLOG_ACTIVE_LEVEL) {
SPDLOG_WARN("Specified log_level '{}' is lower than max available one '{}'. Log level will be changed according to the maximum one",
vm["log_level"].as<int>(),
SPDLOG_ACTIVE_LEVEL);
}
} catch(const po::error& e) {
SPDLOG_CRITICAL("Cmd parse error: {}", e.what());
return 1;
}
// Cmd options handling
spdlog::set_level(static_cast<spdlog::level::level_enum>(vm["log_level"].as<int>()));
std::println("\nEnvironment setup:\n\tCurrently set log level: {}\n", ToString(spdlog::get_level()));
boost::asio::io_context io_context;
boost::asio::co_spawn(io_context, Coroutine(), boost::asio::detached);

View file

@ -1,9 +1,9 @@
#pragma once
#include <spdlog/spdlog.h>
#include <larra/iq_error.hpp>
#include <larra/jid.hpp>
#include <larra/serialization.hpp>
#include <larra/stanza_error.hpp>
#include <larra/utils.hpp>
#include <optional>
#include <stdexcept>
@ -118,7 +118,7 @@ using Error = BaseImplWithPayload<kErrorName, Payload>;
} // namespace iq
using IqError = iq::Error<iq::error::IqError>;
using IqError = iq::Error<stanza::error::StanzaError>;
template <typename Payload>
using Iq = std::variant<iq::Get<Payload>, iq::Set<Payload>, iq::Result<Payload>, IqError>;

View file

@ -1,152 +0,0 @@
#pragma once
#include <libxml++/libxml++.h>
#include <larra/utils.hpp>
#include <optional>
#include <variant>
namespace larra::xmpp::iq::error {
namespace impl {
struct IqBaseError : std::exception {};
// DO NOT MOVE TO ANOTHER NAMESPACE(where no heirs). VIA friend A FUNCTION IS ADDED THAT VIA ADL WILL BE SEARCHED FOR HEIRS
// C++20 modules very unstable in clangd :(
template <typename T>
struct IqErrorImpl : IqBaseError {
static constexpr auto kDefaultName = "error";
static constexpr auto kDefaultNamespace = "urn:ietf:params:xml:ns:xmpp-stanzas";
static inline const auto kKebabCaseName = static_cast<std::string>(utils::ToKebabCaseName<T>());
static constexpr auto kErrorMessage = [] -> std::string_view {
static constexpr auto name = nameof::nameof_short_type<T>();
static constexpr auto str = [] {
return std::array{std::string_view{"Stanza IQ Error: "}, std::string_view{name}, std::string_view{"\0", 1}} | std::views::join;
};
static constexpr auto array = str() | std::ranges::to<utils::RangeToWrapper<std::array<char, std::ranges::distance(str())>>>();
return {array.data(), array.size() - 1};
}();
std::optional<std::string> by{};
std::string type;
// TODO(unknown): Add "optional text children" support for stanza error. Check "XML Stanzas" -> "Syntax" for more details
static constexpr auto TryParse(xmlpp::Element* element) -> std::optional<T> {
if(not element) {
return std::nullopt;
}
auto by = element->get_attribute("by");
auto type = element->get_attribute("type");
if(not type) {
return std::nullopt;
}
auto node = element->get_first_child(kKebabCaseName);
if(not node) {
return std::nullopt;
}
T obj;
obj.type = type->get_value();
if(by) {
obj.by = std::optional{by->get_value()};
}
return obj;
}
static constexpr auto Parse(xmlpp::Element* element) -> T {
return TryParse(element).value();
}
friend constexpr auto operator<<(xmlpp::Element* element, const T& obj) -> void {
element->set_attribute("type", obj.type);
if(obj.by) {
element->set_attribute("by", *obj.by);
}
auto node = element->add_child_element(kKebabCaseName);
node->set_namespace_declaration(kDefaultNamespace);
}
constexpr auto operator==(const IqErrorImpl<T>&) const -> bool {
return true;
};
[[nodiscard]] constexpr auto what() const noexcept -> const char* override {
return kErrorMessage.data();
}
};
// Helper class to prevent parsing response stream into an expected return type if its name is an 'error'
struct UnknownIqError : IqBaseError {
static constexpr auto kDefaultName = "stream:error";
static constexpr std::string_view kErrorMessage = "Unknown XMPP stream error";
static constexpr auto TryParse(xmlpp::Element* element) {
return std::optional{UnknownIqError{}};
}
static constexpr auto Parse(xmlpp::Element* element) {
return TryParse(element).value();
}
friend constexpr auto operator<<(xmlpp::Element* element, const UnknownIqError& obj) -> void {
throw std::format("'{}' must never be written into the real stream!", kErrorMessage);
}
constexpr auto operator==(const UnknownIqError&) const -> bool {
return true;
};
[[nodiscard]] constexpr auto what() const noexcept -> const char* override {
return kErrorMessage.data();
}
};
struct BadRequest : IqErrorImpl<BadRequest> {};
struct Conflict : IqErrorImpl<Conflict> {};
struct FeatureNotImplemented : IqErrorImpl<FeatureNotImplemented> {};
struct Forbidden : IqErrorImpl<Forbidden> {};
struct Gone : IqErrorImpl<Gone> {};
struct InternalServerError : IqErrorImpl<InternalServerError> {};
struct ItemNotFound : IqErrorImpl<ItemNotFound> {};
struct JidMalformed : IqErrorImpl<JidMalformed> {};
struct NotAcceptable : IqErrorImpl<NotAcceptable> {};
struct NotAllowed : IqErrorImpl<NotAllowed> {};
struct NotAuthorized : IqErrorImpl<NotAuthorized> {};
struct PolicyViolation : IqErrorImpl<PolicyViolation> {};
struct RecipientUnavailable : IqErrorImpl<RecipientUnavailable> {};
struct Redirect : IqErrorImpl<Redirect> {};
struct RegistrationRequired : IqErrorImpl<RegistrationRequired> {};
struct RemoteServerNotFound : IqErrorImpl<RemoteServerNotFound> {};
struct RemoteServerTimeout : IqErrorImpl<RemoteServerTimeout> {};
struct ResourceConstraint : IqErrorImpl<ResourceConstraint> {};
struct ServiceUnavailable : IqErrorImpl<ServiceUnavailable> {};
struct SubscriptionRequired : IqErrorImpl<SubscriptionRequired> {};
struct UndefinedCondition : IqErrorImpl<UndefinedCondition> {};
struct UnexpectedRequest : IqErrorImpl<UnexpectedRequest> {};
} // namespace impl
using IqError = std::variant<impl::BadRequest,
impl::Conflict,
impl::FeatureNotImplemented,
impl::Forbidden,
impl::Gone,
impl::InternalServerError,
impl::ItemNotFound,
impl::JidMalformed,
impl::NotAcceptable,
impl::NotAllowed,
impl::NotAuthorized,
impl::PolicyViolation,
impl::RecipientUnavailable,
impl::Redirect,
impl::RegistrationRequired,
impl::RemoteServerNotFound,
impl::RemoteServerTimeout,
impl::ResourceConstraint,
impl::ServiceUnavailable,
impl::SubscriptionRequired,
impl::UndefinedCondition,
impl::UnexpectedRequest,
impl::UnknownIqError>;
static_assert(!std::is_same_v<typename std::variant_alternative_t<std::variant_size_v<IqError> - 1, IqError>, IqError>,
"'UnknownIqError' must be at the end of 'IqError' variant");
} // namespace larra::xmpp::iq::error

View file

@ -0,0 +1,148 @@
#pragma once
#include <libxml++/libxml++.h>
#include <larra/utils.hpp>
#include <optional>
#include <variant>
namespace larra::xmpp::stanza::error {
struct StanzaBaseError : std::exception {};
// DO NOT MOVE TO ANOTHER NAMESPACE(where no heirs). VIA friend A FUNCTION IS ADDED THAT VIA ADL WILL BE SEARCHED FOR HEIRS
// C++20 modules very unstable in clangd :(
template <typename T>
struct StanzaErrorImpl : StanzaBaseError {
static constexpr auto kDefaultName = "error";
static constexpr auto kDefaultNamespace = "urn:ietf:params:xml:ns:xmpp-stanzas";
static inline const auto kKebabCaseName = static_cast<std::string>(utils::ToKebabCaseName<T>());
static constexpr auto kErrorMessage = [] -> std::string_view {
static constexpr auto name = nameof::nameof_short_type<T>();
static constexpr auto str = [] {
return std::array{std::string_view{"Stanza IQ Error: "}, std::string_view{name}, std::string_view{"\0", 1}} | std::views::join;
};
static constexpr auto array = str() | std::ranges::to<utils::RangeToWrapper<std::array<char, std::ranges::distance(str())>>>();
return {array.data(), array.size() - 1};
}();
std::optional<std::string> by{};
std::string type;
// TODO(unknown): Add "optional text children" support for stanza error. Check "XML Stanzas" -> "Syntax" for more details
static constexpr auto TryParse(xmlpp::Element* element) -> std::optional<T> {
if(not element) {
return std::nullopt;
}
auto by = element->get_attribute("by");
auto type = element->get_attribute("type");
if(not type) {
return std::nullopt;
}
auto node = element->get_first_child(kKebabCaseName);
if(not node) {
return std::nullopt;
}
T obj;
obj.type = type->get_value();
if(by) {
obj.by = std::optional{by->get_value()};
}
return obj;
}
static constexpr auto Parse(xmlpp::Element* element) -> T {
return TryParse(element).value();
}
friend constexpr auto operator<<(xmlpp::Element* element, const T& obj) -> void {
element->set_attribute("type", obj.type);
if(obj.by) {
element->set_attribute("by", *obj.by);
}
auto node = element->add_child_element(kKebabCaseName);
node->set_namespace_declaration(kDefaultNamespace);
}
constexpr auto operator==(const StanzaErrorImpl<T>&) const -> bool {
return true;
};
[[nodiscard]] constexpr auto what() const noexcept -> const char* override {
return kErrorMessage.data();
}
};
// Helper class to prevent parsing response stream into an expected return type if its name is an 'error'
struct UnknownStanzaError : StanzaBaseError {
static constexpr auto kDefaultName = "stream:error";
static constexpr std::string_view kErrorMessage = "Unknown XMPP stream error";
static constexpr auto TryParse(xmlpp::Element* element) {
return std::optional{UnknownStanzaError{}};
}
static constexpr auto Parse(xmlpp::Element* element) {
return TryParse(element).value();
}
friend constexpr auto operator<<(xmlpp::Element* element, const UnknownStanzaError& obj) -> void {
throw std::runtime_error{std::format("'{}' must never be written into the real stream!", kErrorMessage)};
}
constexpr auto operator==(const UnknownStanzaError&) const -> bool {
return true;
};
[[nodiscard]] constexpr auto what() const noexcept -> const char* override {
return kErrorMessage.data();
}
};
struct BadRequest : StanzaErrorImpl<BadRequest> {};
struct Conflict : StanzaErrorImpl<Conflict> {};
struct FeatureNotImplemented : StanzaErrorImpl<FeatureNotImplemented> {};
struct Forbidden : StanzaErrorImpl<Forbidden> {};
struct Gone : StanzaErrorImpl<Gone> {};
struct InternalServerError : StanzaErrorImpl<InternalServerError> {};
struct ItemNotFound : StanzaErrorImpl<ItemNotFound> {};
struct JidMalformed : StanzaErrorImpl<JidMalformed> {};
struct NotAcceptable : StanzaErrorImpl<NotAcceptable> {};
struct NotAllowed : StanzaErrorImpl<NotAllowed> {};
struct NotAuthorized : StanzaErrorImpl<NotAuthorized> {};
struct PolicyViolation : StanzaErrorImpl<PolicyViolation> {};
struct RecipientUnavailable : StanzaErrorImpl<RecipientUnavailable> {};
struct Redirect : StanzaErrorImpl<Redirect> {};
struct RegistrationRequired : StanzaErrorImpl<RegistrationRequired> {};
struct RemoteServerNotFound : StanzaErrorImpl<RemoteServerNotFound> {};
struct RemoteServerTimeout : StanzaErrorImpl<RemoteServerTimeout> {};
struct ResourceConstraint : StanzaErrorImpl<ResourceConstraint> {};
struct ServiceUnavailable : StanzaErrorImpl<ServiceUnavailable> {};
struct SubscriptionRequired : StanzaErrorImpl<SubscriptionRequired> {};
struct UndefinedCondition : StanzaErrorImpl<UndefinedCondition> {};
struct UnexpectedRequest : StanzaErrorImpl<UnexpectedRequest> {};
using StanzaError = std::variant<BadRequest,
Conflict,
FeatureNotImplemented,
Forbidden,
Gone,
InternalServerError,
ItemNotFound,
JidMalformed,
NotAcceptable,
NotAllowed,
NotAuthorized,
PolicyViolation,
RecipientUnavailable,
Redirect,
RegistrationRequired,
RemoteServerNotFound,
RemoteServerTimeout,
ResourceConstraint,
ServiceUnavailable,
SubscriptionRequired,
UndefinedCondition,
UnexpectedRequest,
UnknownStanzaError>;
static_assert(std::is_same_v<typename std::variant_alternative_t<std::variant_size_v<StanzaError> - 1, StanzaError>, UnknownStanzaError>,
"'UnknownStanzaError' must be at the end of 'StanzaError' variant");
} // namespace larra::xmpp::stanza::error

View file

@ -52,7 +52,7 @@ struct UnknownXmppError : BaseError {
return TryParse(element).value();
}
friend constexpr auto operator<<(xmlpp::Element* element, const UnknownXmppError& obj) -> void {
throw std::format("'{}' must never be written into the real stream!", kErrorMessage);
throw std::runtime_error{std::format("'{}' must never be written into the real stream!", kErrorMessage)};
}
[[nodiscard]] constexpr auto what() const noexcept -> const char* override {
return kErrorMessage.data();
@ -112,7 +112,8 @@ using StreamError = std::variant<error::stream::BadFormat,
error::stream::UnsupportedVersion,
error::stream::UnknownXmppError>;
static_assert(!std::is_same_v<typename std::variant_alternative_t<std::variant_size_v<StreamError> - 1, StreamError>, StreamError>,
"'UnknownXmppError' must be at the end of 'StreamError' variant");
static_assert(
std::is_same_v<typename std::variant_alternative_t<std::variant_size_v<StreamError> - 1, StreamError>, error::stream::UnknownXmppError>,
"'UnknownXmppError' must be at the end of 'StreamError' variant");
} // namespace larra::xmpp

View file

@ -69,7 +69,7 @@ TEST(IQ, ParseForbiddenError) {
ASSERT_TRUE(std::holds_alternative<IqError>(iqRes));
auto errorRes = std::get<IqError>(iqRes);
ASSERT_TRUE(std::holds_alternative<iq::error::impl::Forbidden>(errorRes.payload));
ASSERT_TRUE(std::holds_alternative<stanza::error::Forbidden>(errorRes.payload));
}
TEST(IQ, IqErrThrowVisitorThrow) {
@ -87,7 +87,7 @@ TEST(IQ, IqErrThrowVisitorThrow) {
static constexpr auto throwErrMsg = "Stanza IQ Error: Forbidden";
try {
std::visit(utempl::Overloaded([](iq::Result<SomeStruct> r) {}, IqErrThrowVisitor<SomeStruct>{visitorErrMsg}), std::move(iqRes));
} catch(const iq::error::impl::IqBaseError& err) {
} catch(const stanza::error::StanzaBaseError& err) {
ASSERT_STREQ(throwErrMsg, err.what());
return;
} catch(const std::runtime_error& err) {
@ -114,7 +114,7 @@ TEST(IQ, IqErrThrowVisitorThrowGet) {
static constexpr auto throwErrMsg = "Test Error: 'Get' is an invalid type for IQ result. Expected 'Result' or 'Error'";
try {
std::visit(utempl::Overloaded([](iq::Result<SomeStruct> r) {}, IqErrThrowVisitor<SomeStruct>{"Test Error"}), std::move(iqRes));
} catch(const iq::error::impl::IqBaseError& err) {
} catch(const stanza::error::StanzaBaseError& err) {
ASSERT_TRUE(false) << "\tERROR: Invalid throw type throw";
} catch(const std::runtime_error& err) {
ASSERT_STREQ(throwErrMsg, err.what());
@ -141,7 +141,7 @@ TEST(IQ, IqErrThrowVisitorThrowSet) {
static constexpr auto throwErrMsg = "Test Error: 'Set' is an invalid type for IQ result. Expected 'Result' or 'Error'";
try {
std::visit(utempl::Overloaded([](iq::Result<SomeStruct> r) {}, IqErrThrowVisitor<SomeStruct>{"Test Error"}), std::move(iqRes));
} catch(const iq::error::impl::IqBaseError& err) {
} catch(const stanza::error::StanzaBaseError& err) {
ASSERT_TRUE(false) << "\tERROR: Invalid throw type throw";
} catch(const std::runtime_error& err) {
ASSERT_STREQ(throwErrMsg, err.what());

View file

@ -1,21 +1,60 @@
#include <gtest/gtest.h>
#include <spdlog/spdlog.h>
#include <boost/program_options.hpp>
#include <print>
#include <utility>
#include <stdexcept>
class PreconfigureEnvironment : public ::testing::Environment {
public:
void SetUp() override {
spdlog::set_level(spdlog::level::trace);
std::println("\nPreconfigureEnvironment setup:\n\tCompiled max availabel log level: {}\n\tCurrently set log level: {}",
SPDLOG_ACTIVE_LEVEL,
std::to_underlying(spdlog::get_level()));
// clang-format off
constexpr auto ToString(spdlog::level::level_enum e) {
switch (e) {
case spdlog::level::trace: return "TRACE";
case spdlog::level::debug: return "DEBUG";
case spdlog::level::info: return "INFO";
case spdlog::level::warn: return "WARNING";
case spdlog::level::err: return "ERROR";
case spdlog::level::critical: return "CRITICAL";
case spdlog::level::off: return "OFF";
default:
return "INVALID";
}
};
}
// clang-format on
namespace po = boost::program_options;
auto main(int argc, char** argv) -> int {
::testing::InitGoogleTest(&argc, argv);
::testing::AddGlobalTestEnvironment(new PreconfigureEnvironment); // NOLINT GTest takes ownership
// Define options
po::options_description desc("Allowed options");
desc.add_options()("help,h", "Print help message")("log_level,l",
po::value<int>()->default_value(SPDLOG_LEVEL_INFO),
"Set log level: 0=TRACE, 1=DEBUG, 2=INFO, 3=WARN, 4=ERROR, 5=CRITICAL, 6=OFF");
// Parse command-line arguments
po::variables_map vm;
try {
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
if(vm["log_level"].as<int>() < spdlog::level::level_enum::trace || vm["log_level"].as<int>() > spdlog::level::level_enum::off) {
throw std::invalid_argument{
std::format("Invalid argument value for '--log_level' option. Check option description for more details")};
}
if(vm["log_level"].as<int>() < SPDLOG_ACTIVE_LEVEL) {
SPDLOG_WARN("Specified log_level '{}' is lower than max available one '{}'. Log level will be changed according to the maximum one",
vm["log_level"].as<int>(),
SPDLOG_ACTIVE_LEVEL);
}
} catch(const po::error& e) {
SPDLOG_CRITICAL("Cmd parse error: {}", e.what());
return 1;
}
// Cmd options handling
spdlog::set_level(static_cast<spdlog::level::level_enum>(vm["log_level"].as<int>()));
std::println("\nEnvironment setup:\n\tCurrently set log level: {}\n", ToString(spdlog::get_level()));
return RUN_ALL_TESTS();
}