Skip to content

Commit

Permalink
Fix std::filesystem::path issue on MSVC, add more tests (#273)
Browse files Browse the repository at this point in the history
  • Loading branch information
SergiusTheBest committed Oct 13, 2023
1 parent 0d9baad commit a2b6113
Show file tree
Hide file tree
Showing 15 changed files with 7,575 additions and 81 deletions.
46 changes: 38 additions & 8 deletions include/plog/Record.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,12 @@ namespace plog
};

template <class T>
struct isConvertibleToNString : isConvertible<T, util::nstring> {};
struct isConvertibleToString : isConvertible<T, std::string> {};

#if PLOG_ENABLE_WCHAR_INPUT
template <class T>
struct isConvertibleToString : isConvertible<T, std::string> {};
struct isConvertibleToWString : isConvertible<T, std::wstring> {};
#endif

template <class T>
struct isContainer
Expand Down Expand Up @@ -168,18 +170,29 @@ namespace plog

#if defined(__clang__) || !defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 // skip for GCC < 4.5 due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=38600
#if !defined(_MSC_VER) || _MSC_VER > 1400 // MSVC 2005 doesn't understand `enableIf`, so drop all `meta`
// Print data that can be casted to `std::basic_string`.
// Print data that can be casted to `std::string`
template<class T>
inline typename meta::enableIf<meta::isConvertibleToString<T>::value, void>::type operator<<(util::nostringstream& stream, const T& data)
{
plog::detail::operator<<(stream, static_cast<std::string>(data));
}

#if PLOG_ENABLE_WCHAR_INPUT
// Print data that can be casted to `std::wstring`
template<class T>
inline typename meta::enableIf<meta::isConvertibleToNString<T>::value, void>::type operator<<(util::nostringstream& stream, const T& data)
inline typename meta::enableIf<meta::isConvertibleToWString<T>::value, void>::type operator<<(util::nostringstream& stream, const T& data)
{
plog::detail::operator<<(stream, static_cast<util::nstring>(data));
plog::detail::operator<<(stream, static_cast<std::wstring>(data));
}
#endif

// Print std containers
template<class T>
inline typename meta::enableIf<meta::isContainer<T>::value &&
!meta::isConvertibleToNString<T>::value &&
!meta::isConvertibleToString<T>::value &&
#if PLOG_ENABLE_WCHAR_INPUT
!meta::isConvertibleToWString<T>::value &&
#endif
!meta::isFilesystemPath<T>::value, void>::type operator<<(util::nostringstream& stream, const T& data)
{
stream << "[";
Expand All @@ -202,14 +215,15 @@ namespace plog
#endif

#ifdef __cplusplus_cli
// Print managed C++ `System::String^`
inline void operator<<(util::nostringstream& stream, System::String^ data)
{
cli::pin_ptr<const System::Char> ptr = PtrToStringChars(data);
plog::detail::operator<<(stream, static_cast<const wchar_t*>(ptr));
}
#endif

#if defined(_WIN32) && (!defined(_MSC_VER) || _MSC_VER > 1400) // MSVC 2005 doesn't understand `enableIf`, so drop all `meta`
#if PLOG_ENABLE_WCHAR_INPUT && (!defined(_MSC_VER) || _MSC_VER > 1400) // MSVC 2005 doesn't understand `enableIf`, so drop all `meta`
namespace meta
{
template<bool Value>
Expand Down Expand Up @@ -244,13 +258,29 @@ namespace plog
# endif //__cpp_char8_t
}

# if PLOG_CHAR_IS_UTF8
// Print types that can be streamed into `std::owstringstream` but not into `std::ostringstream` when we use UTF-8 on Windows
template<class T>
inline typename meta::enableIf<meta::isStreamable<T, std::wostream>::value &&
!meta::isStreamable<T, std::ostream>::value &&
!meta::isConvertibleToWString<T>::value, void>::type operator<<(std::ostringstream& stream, const T& data)
{
std::wostringstream ss;
ss << data;
stream << ss.str();
}
# else
// Print types that can be streamed into `std::ostringstream` but not into `std::owstringstream` when we use `wchar_t` on Windows
template<class T>
inline typename meta::enableIf<meta::isStreamable<T, std::ostream>::value && !meta::isStreamable<T, std::wostream>::value, void>::type operator<<(std::wostringstream& stream, const T& data)
inline typename meta::enableIf<meta::isStreamable<T, std::ostream>::value &&
!meta::isStreamable<T, std::wostream>::value &&
!meta::isConvertibleToString<T>::value, void>::type operator<<(std::wostringstream& stream, const T& data)
{
std::ostringstream ss;
ss << data;
stream << ss.str();
}
# endif
#endif
}

Expand Down
147 changes: 139 additions & 8 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,20 +1,151 @@
cmake_minimum_required(VERSION 3.0)
if(${CMAKE_VERSION} VERSION_LESS 3.27.0)
cmake_minimum_required(VERSION 3.1)
else()
cmake_minimum_required(VERSION 3.6)
endif()

project(PlogTest CXX)

add_executable(${PROJECT_NAME}
doctest.h
#
# Specify test sources
#

set(SOURCES
CastToString.cpp
Common.h
Conditional.cpp
TestAppender.h
Main.cpp
Path.cpp
Printf.cpp
SimpleTypes.cpp
StringTypes.cpp
StdManipulators.cpp
StdContainers.cpp
StdManipulators.cpp
StdStreamable.cpp
)
target_link_libraries(${PROJECT_NAME} plog::plog)
set_target_properties(${PROJECT_NAME} PROPERTIES COMPILE_FLAGS "-DDOCTEST_CONFIG_NO_POSIX_SIGNALS")
set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER Test)

add_test(${PROJECT_NAME} ${PROJECT_NAME})
#
# Check wchar_t platform support
#

include(CheckTypeSize)
CHECK_TYPE_SIZE(wchar_t PLOG_SIZEOF_WCHAR)

#
# Test creation functions to avoid code duplication
#

function(plog_add_test _target)
add_executable(${_target} ${SOURCES})

target_link_libraries(${_target} plog::plog)
target_compile_definitions(${_target} PRIVATE DOCTEST_CONFIG_NO_POSIX_SIGNALS DOCTEST_CONFIG_NO_MULTITHREADING)
set_target_properties(${_target} PROPERTIES FOLDER Test)

# Add std::filesystem support library for GCC
if(CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0)
target_link_libraries(${_target} stdc++fs)
endif()

# Enable conformance mode to be more strict
if(MSVC AND NOT (MSVC_VERSION LESS 1900)) # Visual Studio 2015 and higher
target_compile_options(${_target} PRIVATE "/permissive-")
endif()

add_test(${_target} ${_target})
endfunction()

function(plog_add_test_utf8 _target)
if(MSVC AND NOT (MSVC_VERSION LESS 1900)) # Visual Studio 2015 and higher
plog_add_test(${_target})
target_compile_options(${_target} PRIVATE "/utf-8")
endif()
endfunction()

function(plog_add_test_wchar _target)
if(NOT WIN32 AND NOT BSD AND PLOG_SIZEOF_WCHAR) # FIXME: for some reason wchar_t is not working on BSD
plog_add_test(${_target})
target_compile_definitions(${_target} PRIVATE PLOG_ENABLE_WCHAR_INPUT=1)

if(APPLE)
target_link_libraries(${_target}_wchar -liconv)
endif()
endif()
endfunction()

#
# Create a basic test with default compiler features
#

plog_add_test(${PROJECT_NAME})

#
# Create a test for Utf8Everywhere mode with default compiler features
#

plog_add_test_utf8(${PROJECT_NAME}_utf8)

#
# Create a test for wchar input with default compiler features
#

plog_add_test_wchar(${PROJECT_NAME}_wchar)


#
# Check if running a descent version of CMake and enable it
#

if(CMAKE_VERSION VERSION_LESS 3.8.0)
return()
endif()

cmake_policy(VERSION 3.8)

set(PLOG_CXX_STANDARDS_LIST LIST cxx_std_11 cxx_std_14 cxx_std_17 cxx_std_20 cxx_std_23)

function(plog_get_latest_cxx_std _output)
foreach(cxx_std IN ITEMS ${PLOG_CXX_STANDARDS_LIST})
if(${cxx_std} IN_LIST CMAKE_CXX_COMPILE_FEATURES)
set(${_output} ${cxx_std} PARENT_SCOPE)
endif()
endforeach()
endfunction()

plog_get_latest_cxx_std(PLOG_LATEST_CXX_STD)

function(plog_set_target_cxx_standard _target _cxx_std)
if(TARGET ${_target})
target_compile_features(${_target} PRIVATE ${_cxx_std})
endif()
endfunction()

#
# Create basic tests for different C++ Standard versions
#

foreach(cxx_std IN ITEMS ${PLOG_CXX_STANDARDS_LIST})
if(${cxx_std} IN_LIST CMAKE_CXX_COMPILE_FEATURES)
plog_add_test(${PROJECT_NAME}_${cxx_std})
plog_set_target_cxx_standard(${PROJECT_NAME}_${cxx_std} ${cxx_std})
endif()
endforeach()

#
# Create a test for Utf8Everywhere mode with the latest C++ Standard
#

if(PLOG_LATEST_CXX_STD)
plog_add_test_utf8(${PROJECT_NAME}_${PLOG_LATEST_CXX_STD}_utf8)
plog_set_target_cxx_standard(${PROJECT_NAME}_${PLOG_LATEST_CXX_STD}_utf8 ${PLOG_LATEST_CXX_STD})
endif()

#
# Create a test for wchar input with the latest C++ Standard
#

if(PLOG_LATEST_CXX_STD)
plog_add_test_wchar(${PROJECT_NAME}_${PLOG_LATEST_CXX_STD}_wchar)
plog_set_target_cxx_standard(${PROJECT_NAME}_${PLOG_LATEST_CXX_STD}_wchar ${PLOG_LATEST_CXX_STD})
endif()
55 changes: 55 additions & 0 deletions test/CastToString.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#include "Common.h"

class CastableToString
{
public:
operator std::string() const
{
return "object";
}
};

#if PLOG_ENABLE_WCHAR_INPUT
class CastableToWString
{
public:
operator std::wstring() const
{
return L"object";
}
};
#endif

SCENARIO("cast to string")
{
GIVEN("logger is initialised")
{
plog::TestAppender testAppender;
plog::Logger<PLOG_DEFAULT_INSTANCE_ID> logger(plog::verbose);
logger.addAppender(&testAppender);

WHEN("type is castable to std::string")
{
CastableToString var;
PLOGI << var;

THEN("the result is as expected")
{
CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("object"));
}
}

#if PLOG_ENABLE_WCHAR_INPUT
WHEN("type is castable to std::wstring")
{
CastableToWString var;
PLOGI << var;

THEN("the result is as expected")
{
CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("object"));
}
}
#endif
}
}
16 changes: 16 additions & 0 deletions test/Common.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once

#if defined(__cpp_constexpr) && (!defined(__MINGW32__) || defined(__MINGW64_VERSION_MAJOR))
# include "doctest/2.4.11/doctest.h" // C++11 and higher
#else
# include "doctest/1.2.9/doctest.h" // pre C++11
#endif

#include <plog/Log.h>
#include "TestAppender.h"

#ifdef __has_include
# if __has_include(<version>)
# include <version>
# endif
#endif
6 changes: 2 additions & 4 deletions test/Conditional.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
#include "doctest.h"
#include <plog/Log.h>
#include "TestAppender.h"
#include "Common.h"

SCENARIO("conditional logging")
{
Expand Down Expand Up @@ -47,7 +45,7 @@ SCENARIO("conditional logging")
CHECK_EQ(var, 5 + 1);
}
}

WHEN("log level check is false")
{
int var = 0;
Expand Down
2 changes: 1 addition & 1 deletion test/Main.cpp
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "doctest.h"
#include "Common.h"
27 changes: 27 additions & 0 deletions test/Path.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#include "Common.h"

#ifdef __cpp_lib_filesystem
# include <filesystem>

SCENARIO("std::filesystem::path")
{
GIVEN("logger is initialised")
{
plog::TestAppender testAppender;
plog::Logger<PLOG_DEFAULT_INSTANCE_ID> logger(plog::verbose);
logger.addAppender(&testAppender);

WHEN("type is std::filesystem::path")
{
std::filesystem::path path("/usr/lib");
PLOGI << path;

THEN("the result is as expected")
{
CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("/usr/lib"));
}
}
}
}

#endif
Loading

0 comments on commit a2b6113

Please sign in to comment.