refactor: start to refactor project

This commit is contained in:
yyc12345 committed 2025-06-20 23:38:34 +08:00
1 parent bec36b4b3c
commit df3b602110
70 files changed
+2093 -779

No files matched your search

+314
View File
@@ -0,0 +1,314 @@
# yaml-language-server: $schema=https://json.schemastore.org/clang-format.json
---
Language: Cpp
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveAssignments:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveBitFields:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveDeclarations:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveShortCaseStatements:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCaseArrows: false
AlignCaseColons: false
AlignConsecutiveTableGenBreakingDAGArgColons:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveTableGenCondOperatorColons:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveTableGenDefinitionColons:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignEscapedNewlines: DontAlign
AlignOperands: Align
AlignTrailingComments:
Kind: Always
OverEmptyLines: 0
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowBreakBeforeNoexceptSpecifier: Never
AllowShortBlocksOnASingleLine: Never
AllowShortCaseExpressionOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: false
AllowShortCompoundRequirementOnASingleLine: true
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: AllIfsAndElse
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AttributeMacros:
- __capability
BinPackArguments: false
BinPackParameters: false
BitFieldColonSpacing: Both
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterExternBlock: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
BreakAdjacentStringLiterals: true
BreakAfterAttributes: Leave
BreakAfterJavaFieldAnnotations: false
BreakAfterReturnType: None
BreakArrays: true
BreakBeforeBinaryOperators: All
BreakBeforeConceptDeclarations: Always
BreakBeforeBraces: Attach
BreakBeforeInlineASMColon: OnlyMultiline
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: AfterColon
BreakFunctionDefinitionParameters: false
BreakInheritanceList: BeforeColon
BreakStringLiterals: false
BreakTemplateDeclarations: Yes
ColumnLimit: 100
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: true
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- forever
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^<Q.*'
Priority: 200
SortPriority: 200
CaseSensitive: true
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: true
IndentExternBlock: AfterExternBlock
IndentGotoLabels: true
IndentPPDirectives: None
IndentRequiresClause: true
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertBraces: false
InsertNewlineAtEOF: false
InsertTrailingCommas: None
IntegerLiteralSeparator:
Binary: 0
BinaryMinDigits: 0
Decimal: 0
DecimalMinDigits: 0
Hex: 0
HexMinDigits: 0
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLines:
AtEndOfFile: false
AtStartOfBlock: false
AtStartOfFile: false
LambdaBodyIndentation: Signature
LineEnding: DeriveLF
MacroBlockBegin: ''
MacroBlockEnd: ''
MainIncludeChar: Quote
MaxEmptyLinesToKeep: 1
NamespaceIndentation: All
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 4
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PackConstructorInitializers: BinPack
PenaltyBreakAssignment: 150
PenaltyBreakBeforeFirstCallParameter: 300
PenaltyBreakComment: 500
PenaltyBreakFirstLessLess: 400
PenaltyBreakOpenParenthesis: 0
PenaltyBreakScopeResolution: 500
PenaltyBreakString: 600
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 50
PenaltyIndentedWhitespace: 0
PenaltyReturnTypeOnItsOwnLine: 300
PointerAlignment: Right
PPIndentWidth: -1
QualifierAlignment: Leave
ReferenceAlignment: Pointer
ReflowComments: false
RemoveBracesLLVM: false
RemoveParentheses: Leave
RemoveSemicolon: false
RequiresClausePosition: OwnLine
RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SkipMacroDefinitionBody: false
SortIncludes: CaseSensitive
SortJavaStaticImport: Before
SortUsingDeclarations: Lexicographic
SpaceAfterCStyleCast: true
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeJsonColon: false
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: true
AfterOverloadedOperator: false
AfterPlacementOperator: true
AfterRequiresInClause: false
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: Never
SpacesInContainerLiterals: false
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParens: Never
SpacesInParensOptions:
ExceptDoubleParentheses: false
InCStyleCasts: false
InConditionalStatements: false
InEmptyParentheses: false
Other: false
SpacesInSquareBrackets: false
Standard: Auto
StatementAttributeLikeMacros:
- Q_EMIT
- emit
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
- Q_CLASSINFO
- Q_ENUM
- Q_ENUM_NS
- Q_FLAG
- Q_FLAG_NS
- Q_GADGET
- Q_GADGET_EXPORT
- Q_INTERFACES
- Q_LOGGING_CATEGORY
- Q_MOC_INCLUDE
- Q_NAMESPACE
- Q_NAMESPACE_EXPORT
- Q_OBJECT
- Q_PROPERTY
- Q_REVISION
- Q_DISABLE_COPY
- Q_DISABLE_COPY_MOVE
- Q_SET_OBJECT_NAME
- QT_BEGIN_NAMESPACE
- QT_END_NAMESPACE
- QML_ADDED_IN_MINOR_VERSION
- QML_ANONYMOUS
- QML_ATTACHED
- QML_DECLARE_TYPE
- QML_DECLARE_TYPEINFO
- QML_ELEMENT
- QML_EXTENDED
- QML_EXTENDED_NAMESPACE
- QML_EXTRA_VERSION
- QML_FOREIGN
- QML_FOREIGN_NAMESPACE
- QML_IMPLEMENTS_INTERFACES
- QML_INTERFACE
- QML_NAMED_ELEMENT
- QML_REMOVED_IN_MINOR_VERSION
- QML_SINGLETON
- QML_UNAVAILABLE
- QML_UNCREATABLE
- QML_VALUE_TYPE
- YYCC_DELETE_COPY
- YYCC_DELETE_MOVE
- YYCC_DELETE_COPY_MOVE
- YYCC_DEFAULT_COPY
- YYCC_DEFAULT_MOVE
- YYCC_DEFAULT_COPY_MOVE
TableGenBreakInsideDAGArg: DontBreak
TabWidth: 4
UseTab: Never
VerilogBreakBetweenInstancePorts: true
WhitespaceSensitiveMacros:
- BOOST_PP_STRINGIZE
- CF_SWIFT_NAME
- NS_SWIFT_NAME
- PP_STRINGIZE
- STRINGIZE
...
+3
View File
@@ -0,0 +1,3 @@
[*.{cpp,hpp}]
indent_style = space
indent_size = 4
+9 -1
View File
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.23)
project(YYCC
VERSION 1.3.0
VERSION 2.0.0
LANGUAGES CXX
)
@@ -20,6 +20,14 @@ set(YYCC_INSTALL_BIN_PATH ${CMAKE_INSTALL_BINDIR} CACHE PATH
set(YYCC_INSTALL_DOC_PATH ${CMAKE_INSTALL_DOCDIR} CACHE PATH
"Non-arch doc install path relative to CMAKE_INSTALL_PREFIX unless set to an absolute path.")
# Include dependency.
# GTest is required if we build testbench
if (YYCC_BUILD_TESTBENCH)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
find_package(GTest REQUIRED)
endif ()
# Import 3 build targets
add_subdirectory(src)
if (YYCC_BUILD_TESTBENCH)
+58 -38
View File
@@ -1,7 +1,7 @@
# Configure version file
configure_file(
${CMAKE_CURRENT_LIST_DIR}/../cmake/YYCCVersion.hpp.in
${CMAKE_CURRENT_LIST_DIR}/YYCC/YYCCVersion.hpp
${CMAKE_CURRENT_LIST_DIR}/yycc/version.hpp.in
${CMAKE_CURRENT_LIST_DIR}/yycc/version.hpp
@ONLY
)
@@ -11,48 +11,68 @@ add_library(YYCCommonplace STATIC "")
target_sources(YYCCommonplace
PRIVATE
# Sources
YYCC/COMHelper.cpp
YYCC/ArgParser.cpp
YYCC/ConfigManager.cpp
YYCC/ConsoleHelper.cpp
YYCC/DialogHelper.cpp
YYCC/EncodingHelper.cpp
YYCC/ExceptionHelper.cpp
YYCC/StdPatch.cpp
YYCC/IOHelper.cpp
YYCC/StringHelper.cpp
YYCC/WinFctHelper.cpp
# Natvis (only for MSVC)
$<$<CXX_COMPILER_ID:MSVC>:YYCC.natvis>
yycc/string/reinterpret.cpp
yycc/string/op.cpp
# YYCC/COMHelper.cpp
# YYCC/ArgParser.cpp
# YYCC/ConfigManager.cpp
# YYCC/ConsoleHelper.cpp
# YYCC/DialogHelper.cpp
# YYCC/EncodingHelper.cpp
# YYCC/ExceptionHelper.cpp
# YYCC/StdPatch.cpp
# YYCC/IOHelper.cpp
# YYCC/StringHelper.cpp
# YYCC/WinFctHelper.cpp
# # Natvis (only for MSVC)
# $<$<CXX_COMPILER_ID:MSVC>:YYCC.natvis>
)
target_sources(YYCCommonplace
PUBLIC
FILE_SET HEADERS
FILES
# Headers
# Common headers
YYCC/Constraints.hpp
YYCC/COMHelper.hpp
YYCC/ArgParser.hpp
YYCC/ConfigManager.hpp
YYCC/ConsoleHelper.hpp
YYCC/DialogHelper.hpp
YYCC/EncodingHelper.hpp
YYCC/EnumHelper.hpp
YYCC/ExceptionHelper.hpp
YYCC/StdPatch.hpp
YYCC/IOHelper.hpp
YYCC/ParserHelper.hpp
YYCC/StringHelper.hpp
YYCC/WinFctHelper.hpp
# Windows including guard pair
YYCC/WinImportPrefix.hpp
YYCC/WinImportSuffix.hpp
# Internal
YYCC/YYCCVersion.hpp
YYCC/YYCCInternal.hpp
# Exposed
YYCCommonplace.hpp
yycc.hpp
yycc/version.hpp
yycc/prelude/core.hpp
yycc/prelude/rust.hpp
yycc/macro/version_cmp.hpp
yycc/macro/os_detector.hpp
yycc/macro/class_copy_move.hpp
yycc/string.hpp
yycc/string/reinterpret.hpp
yycc/string/op.hpp
yycc/rust/primitive.hpp
yycc/windows/unsafe_suppressor.hpp
yycc/windows/import_guard_head.hpp
yycc/windows/import_guard_tail.hpp
yycc/constraint.hpp
yycc/constraint/builder.hpp
# # Headers
# # Common headers
# YYCC/Constraints.hpp
# YYCC/COMHelper.hpp
# YYCC/ArgParser.hpp
# YYCC/ConfigManager.hpp
# YYCC/ConsoleHelper.hpp
# YYCC/DialogHelper.hpp
# YYCC/EncodingHelper.hpp
# YYCC/EnumHelper.hpp
# YYCC/ExceptionHelper.hpp
# YYCC/StdPatch.hpp
# YYCC/IOHelper.hpp
# YYCC/ParserHelper.hpp
# YYCC/StringHelper.hpp
# YYCC/WinFctHelper.hpp
# # Windows including guard pair
# YYCC/WinImportPrefix.hpp
# YYCC/WinImportSuffix.hpp
# # Internal
# YYCC/YYCCVersion.hpp
# YYCC/YYCCInternal.hpp
# # Exposed
# YYCCommonplace.hpp
)
# Setup header infomations
target_include_directories(YYCCommonplace
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+16
View File
@@ -0,0 +1,16 @@
#pragma once
// Library Version and Comparison Macros
#include "yycc/version.hpp"
#include "yycc/macro/version_cmp.hpp"
// Operating System Identifier Macros
#include "yycc/macro/os_detector.hpp"
// Windows Shitty Behavior Disable Macros
#include "yycc/windows/unsafe_suppressor.hpp"
// Batch Class Move / Copy Function Macros
#include "yycc/macro/class_copy_move.hpp"
namespace yycc {}
View File
Whitespace-only changes.
View File
Whitespace-only changes.
+78
View File
@@ -0,0 +1,78 @@
#pragma once
#include "macro/class_copy_move.hpp"
#include <functional>
#include <stdexcept>
#include <algorithm>
/// @brief The namespace containing generic constraint concept used varied in other modules.
namespace yycc::constraint {
/// @brief Function prototype used in Constraint for checking whether given value is valid.
/// @details Analyze given value, and return true if value is legal, otherwise false.
template<typename T>
using FnCheck = std::function<bool(const T&)>;
/// @brief Function prototype used in Constraint for clamping given value into a valid value.
/// @details Analyze given value, return clamped value.
template<typename T>
using FnClamp = std::function<T(const T&)>;
/**
* @brief The constraint applied to settings to limit its stored value.
* @tparam T The data type this constraint need to be processed with.
* @details
* Constraint class contains various features:
* \li Check: Check whether given value is in range.
* \li Clamp: Clamp given value into valid value.
* Every instances of Constraint can have some, or none of these features.
* So it is essential to check whether instance has corresponding features before using it.
*/
template<typename T>
class Constraint {
public:
Constraint(FnCheck<T>&& fn_check, FnClamp<T>&& fn_clamp) :
fn_check(std::move(fn_check)), fn_clamp(std::move(fn_clamp)) {}
YYCC_DELETE_COPY(Constraint)
YYCC_DEFAULT_MOVE(Constraint)
/**
* @brief Perform Check feature.
* @param[in] value The valid for checking.
* @return True if valid is okey, otherwise false.
* @exception std::logic_error Raised if this feature is not supported.
*/
bool check(const T& value) const {
if (!support_check()) {
throw std::logic_error("this Constraint do not support check operation");
} else {
return fn_check(value);
}
}
/**
* @brief Perform Clamp feature.
* @param[in] value The valid for clamping.
* @return The result after clamping.
* @exception std::logic_error Raised if this feature is not supported.
*/
T clamp(const T& value) const {
if (!support_clamp()) {
throw std::logic_error("this Constraint do not support clamp operation");
} else {
return fn_clamp(value);
}
}
/// @brief Check whether this Constraint support Check feature.
/// @return True if it support, otherwise false.
bool support_check() const noexcept { return this->fn_check != nullptr; }
/// @brief Check whether this Constraint support Clamp feature.
/// @return True if it support, otherwise false.
bool support_clamp() const noexcept { return this->fn_clamp != nullptr; }
private:
/// @brief Pointer to Check feature function.
FnCheck<T> fn_check;
/// @brief Pointer to Clamp feature function.
FnClamp<T> fn_clamp;
};
} // namespace yycc::core::constraint
+67
View File
@@ -0,0 +1,67 @@
#pragma once
#include "../constraint.hpp"
#include "../string.hpp"
#include <set>
#define NS_YYCC_STRING ::yycc::string
/// @brief The namespace containing convenient function building common used Constraint instance.
namespace yycc::constraint::builder {
/**
* @brief Build Constraint for arithmetic values by minimum and maximum value range.
* @tparam T An arithmetic or enum type (except bool) of underlying stored value.
* @param[in] min_value The minimum value of range (inclusive).
* @param[in] max_value The maximum value of range (inclusive).
* @return The generated constraint instance which can be directly applied.
*/
template<typename T,
std::enable_if_t<std::is_arithmetic_v<T> && !std::is_same_v<T, bool>, int> = 0>
Constraint<T> min_max_constraint(T min_value, T max_value) {
if (min_value > max_value)
throw std::invalid_argument("the max value must be equal or greater than min value");
auto fn_check = [min_value, max_value](const T& val) -> bool {
return (val <= max_value) && (val >= min_value);
};
auto fn_clamp = [min_value, max_value](const T& val) -> T {
return std::clamp(val, min_value, max_value);
};
return Constraint<T>(std::move(fn_check), std::move(fn_clamp));
}
/**
* @brief Get constraint for enum values by enumerating all possible values.
* @tparam T An enum type (except bool) of underlying stored value.
* @param[in] il An initializer list storing all possible values.
* @return The generated constraint instance which can be directly applied.
*/
template<typename T, std::enable_if_t<std::is_enum_v<T>, int> = 0>
Constraint<T> enum_constraint(const std::initializer_list<T>& il) {
std::set<T> data(il);
auto fn_check = [data](const T& val) -> bool { return data.find(val) != data.end(); };
return Constraint<T>(std::move(fn_check), nullptr);
}
/**
* @brief Get constraint for string values by enumerating all possible values.
* @param[in] il An initializer list storing all possible values.
* @return The generated constraint instance which can be directly applied.
* @remarks
* Caller must make sure that the string view passed in initializer list is valid until this Constraint life time gone.
* Becasue this generator will not copy your given string view into string.
*/
inline Constraint<NS_YYCC_STRING::u8string> GetStringEnumerationConstraint(
const std::initializer_list<NS_YYCC_STRING::u8string_view>& il) {
std::set<NS_YYCC_STRING::u8string_view> data(il);
auto fn_check = [data](const NS_YYCC_STRING::u8string& val) -> bool {
return data.find(NS_YYCC_STRING::u8string_view(val)) != data.end();
};
return Constraint<NS_YYCC_STRING::u8string>(std::move(fn_check), nullptr);
}
} // namespace yycc::constraint::builder
#undef NS_YYCC_STRING
+31
View File
@@ -0,0 +1,31 @@
#pragma once
/// @brief Explicitly remove copy (\c constructor and \c operator\=) for given class.
#define YYCC_DELETE_COPY(CLSNAME) \
CLSNAME(const CLSNAME&) = delete; \
CLSNAME& operator=(const CLSNAME&) = delete;
/// @brief Explicitly remove move (\c constructor and \c operator\=) for given class.
#define YYCC_DELETE_MOVE(CLSNAME) \
CLSNAME(CLSNAME&&) = delete; \
CLSNAME& operator=(CLSNAME&&) = delete;
/// @brief Explicitly remove (copy and move) (\c constructor and \c operator\=) for given class.
#define YYCC_DELETE_COPY_MOVE(CLSNAME) \
YYCC_DELETE_COPY(CLSNAME) \
YYCC_DELETE_MOVE(CLSNAME)
/// @brief Explicitly set default copy (\c constructor and \c operator\=) for given class.
#define YYCC_DEFAULT_COPY(CLSNAME) \
CLSNAME(const CLSNAME&) = default; \
CLSNAME& operator=(const CLSNAME&) = default;
/// @brief Explicitly set default move (\c constructor and \c operator\=) for given class.
#define YYCC_DEFAULT_MOVE(CLSNAME) \
CLSNAME(CLSNAME&&) = default; \
CLSNAME& operator=(CLSNAME&&) = default;
/// @brief Explicitly set default (copy and move) (\c constructor and \c operator\=) for given class.
#define YYCC_DEFAULT_COPY_MOVE(CLSNAME) \
YYCC_DEFAULT_COPY(CLSNAME) \
YYCC_DEFAULT_MOVE(CLSNAME)
+11
View File
@@ -0,0 +1,11 @@
#pragma once
// Define operating system macros
#define YYCC_OS_WINDOWS 2
#define YYCC_OS_LINUX 3
// Check current operating system.
#if defined(_WIN32)
#define YYCC_OS YYCC_OS_WINDOWS
#else
#define YYCC_OS YYCC_OS_LINUX
#endif
+26
View File
@@ -0,0 +1,26 @@
#pragma once
/// @brief Return true if left version number is equal to right version number, otherwise false.
#define YYCC_VERCMP_E(av1, av2, av3, bv1, bv2, bv3) ((av1) == (bv1) && (av2) == (bv2) && (av3) == (bv3))
/// @brief Return true if left version number is not equal to right version number, otherwise false.
#define YYCC_VERCMP_NE(av1, av2, av3, bv1, bv2, bv3) (!YYCC_VERCMP_E(av1, av2, av3, bv1, bv2, bv3))
/// @brief Return true if left version number is greater than right version number, otherwise false.
#define YYCC_VERCMP_G(av1, av2, av3, bv1, bv2, bv3) ( \
((av1) > (bv1)) || \
((av1) == (bv1) && (av2) > (bv2)) || \
((av1) == (bv1) && (av2) == (bv2) && (av3) > (bv3)) \
)
/// @brief Return true if left version number is greater than or equal to right version number, otherwise false.
#define YYCC_VERCMP_GE(av1, av2, av3, bv1, bv2, bv3) (YYCC_VERCMP_G(av1, av2, av3, bv1, bv2, bv3) || YYCC_VERCMP_E(av1, av2, av3, bv1, bv2, bv3))
/// @brief Return true if left version number is not lower than right version number, otherwise false.
#define YYCC_VERCMP_NL(av1, av2, av3, bv1, bv2, bv3) YYCC_VERCMP_GE(av1, av2, av3, bv1, bv2, bv3)
/// @brief Return true if left version number is lower than right version number, otherwise false.
#define YYCC_VERCMP_L(av1, av2, av3, bv1, bv2, bv3) ( \
((av1) < (bv1)) || \
((av1) == (bv1) && (av2) < (bv2)) || \
((av1) == (bv1) && (av2) == (bv2) && (av3) < (bv3)) \
)
/// @brief Return true if left version number is lower than or equal to right version number, otherwise false.
#define YYCC_VERCMP_LE(av1, av2, av3, bv1, bv2, bv3) (YYCC_VERCMP_L(av1, av2, av3, bv1, bv2, bv3) || YYCC_VERCMP_E(av1, av2, av3, bv1, bv2, bv3))
/// @brief Return true if left version number is not greater than right version number, otherwise false.
#define YYCC_VERCMP_NG(av1, av2, av3, bv1, bv2, bv3) YYCC_VERCMP_LE(av1, av2, av3, bv1, bv2, bv3)
View File
Whitespace-only changes.
View File
Whitespace-only changes.
View File
Whitespace-only changes.
+18
View File
@@ -0,0 +1,18 @@
#pragma once
// Prelude section
#include "../string.hpp"
namespace yycc::prelude {
#define NS_YYCC_STRING ::yycc::string
using u8char = NS_YYCC_STRING::u8char;
using u8string = NS_YYCC_STRING::u8string;
using u8string_view = NS_YYCC_STRING::u8string_view;
#undef NS_YYCC_STRING
} // namespace yycc::prelude
// Expose all members
using namespace yycc::prelude;
+41
View File
@@ -0,0 +1,41 @@
#pragma once
// Include YYCC prelude first
#include "core.hpp"
// Rust prelude section
#include "../rust/primitive.hpp"
#include <vector>
namespace yycc::prelude::rust {
// Include primitive types
#define NS_RUST_PRIMITIVE ::yycc::rust::primitive
using i8 = NS_RUST_PRIMITIVE::i8;
using i16 = NS_RUST_PRIMITIVE::i16;
using i32 = NS_RUST_PRIMITIVE::i32;
using i64 = NS_RUST_PRIMITIVE::i64;
using u8 = NS_RUST_PRIMITIVE::u8;
using u16 = NS_RUST_PRIMITIVE::u16;
using u32 = NS_RUST_PRIMITIVE::u32;
using u64 = NS_RUST_PRIMITIVE::u64;
using isize = NS_RUST_PRIMITIVE::isize;
using usize = NS_RUST_PRIMITIVE::usize;
using f32 = NS_RUST_PRIMITIVE::f32;
using f64 = NS_RUST_PRIMITIVE::f64;
using str = NS_RUST_PRIMITIVE::str;
#undef NS_RUST_PRIMITIVE
// Other types
using String = ::yycc::string::u8string;
template<typename T>
using Vec = std::vector<T>;
} // namespace yycc::prelude::rust
// Expose all members
using namespace yycc::prelude::rust;
View File
Whitespace-only changes.
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include <cinttypes>
#include <cstddef>
#include "../string.hpp"
namespace yycc::rust::primitive {
// `bool` is keyword so should not declare it anymore.
// `char` is keyword so should not declare it anymore.
using i8 = std::int8_t;
using i16 = std::int16_t;
using i32 = std::int32_t;
using i64 = std::int64_t;
using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;
using isize = std::ptrdiff_t;
using usize = std::size_t;
using f32 = float;
using f64 = double;
using str = ::yycc::string::u8string_view;
}
+41
View File
@@ -0,0 +1,41 @@
#pragma once
// Define the UTF8 char type we used.
// And do a polyfill if no embedded char8_t type.
#include <string>
#include <string_view>
namespace yycc::string {
/**
\typedef u8char_t
\brief YYCC UTF8 char type.
\details
This char type is an alias to \c char8_t if your current C++ standard support it.
Otherwise it is defined as <TT>unsigned char</TT> as C++ 20 stdandard does.
*/
/**
\typedef u8string
\brief YYCC UTF8 string container type.
\details
This type is defined as \c std::basic_string<yycc_char8_t>.
It is equal to \c std::u8string if your current C++ standard support it.
*/
/**
\typedef u8string_view
\brief YYCC UTF8 string view type.
\details
This type is defined as \c std::basic_string_view<yycc_char8_t>.
It is equal to \c std::u8string_view if your current C++ standard support it.
*/
#if defined(__cpp_char8_t)
using u8char = char8_t;
using u8string = std::u8string;
using u8string_view = std::u8string_view;
#else
using u8char = unsigned char;
using u8string = std::basic_string<u8char>;
using u8string_view = std::basic_string_view<u8char>;
#endif
} // namespace yycc::string
+226
View File
@@ -0,0 +1,226 @@
#include "op.hpp"
#include <algorithm>
#include "reinterpret.hpp"
#define NS_YYCC_STRING ::yycc::string
#define NS_YYCC_STRING_REINTERPRET ::yycc::string::reinterpret
namespace yycc::string::op {
#pragma region Printf VPrintf
bool printf(NS_YYCC_STRING::u8string& strl, const NS_YYCC_STRING::u8char* format, ...) {
va_list argptr;
va_start(argptr, format);
bool ret = vprintf(strl, format, argptr);
va_end(argptr);
return ret;
}
bool vprintf(NS_YYCC_STRING::u8string& strl, const NS_YYCC_STRING::u8char* format, va_list argptr) {
va_list args1;
va_copy(args1, argptr);
va_list args2;
va_copy(args2, argptr);
// the return value is desired char count without NULL terminal.
// minus number means error
int count = std::vsnprintf(
nullptr,
0,
NS_YYCC_STRING_REINTERPRET::as_ordinary(format),
args1
);
if (count < 0) {
// invalid length returned by vsnprintf.
return false;
}
va_end(args1);
// resize std::string to desired count.
// and pass its length + 1 to std::vsnprintf,
// because std::vsnprintf only can write "buf_size - 1" chars with a trailing NULL.
// however std::vsnprintf already have a trailing NULL, so we plus 1 for it.
strl.resize(count);
int write_result = std::vsnprintf(
NS_YYCC_STRING_REINTERPRET::as_ordinary(strl.data()),
strl.size() + 1,
NS_YYCC_STRING_REINTERPRET::as_ordinary(format),
args2
);
va_end(args2);
if (write_result < 0 || write_result > count) {
// invalid write result in vsnprintf.
return false;
}
return true;
}
NS_YYCC_STRING::u8string printf(const NS_YYCC_STRING::u8char* format, ...) {
NS_YYCC_STRING::u8string ret;
va_list argptr;
va_start(argptr, format);
vprintf(ret, format, argptr);
va_end(argptr);
return ret;
}
NS_YYCC_STRING::u8string vprintf(const NS_YYCC_STRING::u8char* format, va_list argptr) {
NS_YYCC_STRING::u8string ret;
va_list argcpy;
va_copy(argcpy, argptr);
vprintf(ret, format, argcpy);
va_end(argcpy);
return ret;
}
#pragma endregion
#pragma region Replace
void replace(NS_YYCC_STRING::u8string& strl, const NS_YYCC_STRING::u8string_view& _from_strl, const NS_YYCC_STRING::u8string_view& _to_strl) {
// Reference: https://stackoverflow.com/questions/3418231/replace-part-of-a-string-with-another-string
// check requirements
// from string should not be empty
NS_YYCC_STRING::u8string from_strl(_from_strl);
NS_YYCC_STRING::u8string to_strl(_to_strl);
if (from_strl.empty()) return;
// start replace one by one
size_t start_pos = 0;
while ((start_pos = strl.find(from_strl, start_pos)) != NS_YYCC_STRING::u8string::npos) {
strl.replace(start_pos, from_strl.size(), to_strl);
start_pos += to_strl.size(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
}
}
NS_YYCC_STRING::u8string replace(const NS_YYCC_STRING::u8string_view& _strl, const NS_YYCC_STRING::u8string_view& _from_strl, const NS_YYCC_STRING::u8string_view& _to_strl) {
// prepare result
NS_YYCC_STRING::u8string strl(_strl);
replace(strl, _from_strl, _to_strl);
// return value
return strl;
}
#pragma endregion
#pragma region Join
NS_YYCC_STRING::u8string join(JoinDataProvider fct_data, const NS_YYCC_STRING::u8string_view& decilmer) {
NS_YYCC_STRING::u8string ret;
bool is_first = true;
NS_YYCC_STRING::u8string_view element;
// fetch element
while (fct_data(element)) {
// insert decilmer
if (is_first) is_first = false;
else {
// append decilmer.
ret.append(decilmer);
}
// insert element if it is not empty
if (!element.empty())
ret.append(element);
}
return ret;
}
#pragma endregion
#pragma region Upper Lower
template<bool BIsToLower>
static void generic_lower_upper(NS_YYCC_STRING::u8string& strl) {
// References:
// https://en.cppreference.com/w/cpp/algorithm/transform
// https://en.cppreference.com/w/cpp/string/byte/tolower
std::transform(
strl.cbegin(), strl.cend(), strl.begin(),
[](unsigned char c) -> char {
if constexpr (BIsToLower) return std::tolower(c);
else return std::toupper(c);
}
);
}
void lower(NS_YYCC_STRING::u8string& strl) {
generic_lower_upper<true>(strl);
}
NS_YYCC_STRING::u8string lower(const NS_YYCC_STRING::u8string_view& strl) {
NS_YYCC_STRING::u8string ret(strl);
lower(ret);
return ret;
}
void upper(NS_YYCC_STRING::u8string& strl) {
generic_lower_upper<false>(strl);
}
NS_YYCC_STRING::u8string upper(const NS_YYCC_STRING::u8string_view& strl) {
// same as Lower, just replace char transform function.
NS_YYCC_STRING::u8string ret(strl);
upper(ret);
return ret;
}
#pragma endregion
#pragma region Split
std::vector<NS_YYCC_STRING::u8string> split(const NS_YYCC_STRING::u8string_view& strl, const NS_YYCC_STRING::u8string_view& _decilmer) {
// call split view
auto view_result = split_view(strl, _decilmer);
// copy string view result to string
std::vector<NS_YYCC_STRING::u8string> elems;
elems.reserve(view_result.size());
for (const auto& strl_view : view_result) {
elems.emplace_back(NS_YYCC_STRING::u8string(strl_view));
}
// return copied result
return elems;
}
std::vector<NS_YYCC_STRING::u8string_view> split_view(const NS_YYCC_STRING::u8string_view& strl, const NS_YYCC_STRING::u8string_view& _decilmer) {
// Reference:
// https://stackoverflow.com/questions/14265581/parse-split-a-string-in-c-using-string-delimiter-standard-c
// prepare return value
std::vector<NS_YYCC_STRING::u8string_view> elems;
// if string need to be splitted is empty, return original string (empty string).
// if decilmer is empty, return original string.
NS_YYCC_STRING::u8string decilmer(_decilmer);
if (strl.empty() || decilmer.empty()) {
elems.emplace_back(strl);
return elems;
}
// start spliting
std::size_t previous = 0, current;
while ((current = strl.find(decilmer.c_str(), previous)) != NS_YYCC_STRING::u8string::npos) {
elems.emplace_back(strl.substr(previous, current - previous));
previous = current + decilmer.size();
}
// try insert last part but prevent possible out of range exception
if (previous <= strl.size()) {
elems.emplace_back(strl.substr(previous));
}
return elems;
}
#pragma endregion
}
+156
View File
@@ -0,0 +1,156 @@
#pragma once
#include <cstdarg>
#include <functional>
#include <vector>
#include "../string.hpp"
#define NS_YYCC_STRING ::yycc::string
namespace yycc::string::op {
/**
* @brief Perform a string formatting operation.
* @param[out] strl
* The string container receiving the result.
* There is no guarantee that the content is not modified when function failed.
* @param[in] format The format string.
* @param[in] ... Argument list of format string.
* @return True if success, otherwise false.
*/
bool printf(NS_YYCC_STRING::u8string& strl, const NS_YYCC_STRING::u8char* format, ...);
/**
* @brief Perform a string formatting operation.
* @param[out] strl
* The string container receiving the result.
* There is no guarantee that the content is not modified when function failed.
* @param[in] format The format string.
* @param[in] argptr Argument list of format string.
* @return True if success, otherwise false.
*/
bool vprintf(NS_YYCC_STRING::u8string& strl, const NS_YYCC_STRING::u8char* format, va_list argptr);
/**
* @brief Perform a string formatting operation.
* @param[in] format The format string.
* @param[in] ... Argument list of format string.
* @return The formatting result. Empty string if error happened.
*/
NS_YYCC_STRING::u8string printf(const NS_YYCC_STRING::u8char* format, ...);
/**
* @brief Perform a string formatting operation.
* @param[in] format The format string.
* @param[in] argptr Argument list of format string.
* @return The formatting result. Empty string if error happened.
*/
NS_YYCC_STRING::u8string vprintf(const NS_YYCC_STRING::u8char* format, va_list argptr);
/**
* @brief Modify given string with all occurrences of substring \e old replaced by \e new.
* @param[in,out] strl The string for replacing
* @param[in] _from_strl The \e old string.
* @param[in] _to_strl The \e new string.
*/
void replace(NS_YYCC_STRING::u8string& strl, const NS_YYCC_STRING::u8string_view& _from_strl, const NS_YYCC_STRING::u8string_view& _to_strl);
/**
* @brief Return a copy with all occurrences of substring \e old replaced by \e new.
* @param[in] _strl The string for replacing
* @param[in] _from_strl The \e old string.
* @param[in] _to_strl The \e new string.
* @return The result of replacement.
*/
NS_YYCC_STRING::u8string replace(const NS_YYCC_STRING::u8string_view& _strl, const NS_YYCC_STRING::u8string_view& _from_strl, const NS_YYCC_STRING::u8string_view& _to_strl);
/**
* @brief The data provider of general join function.
* @details
* For programmer using lambda to implement this function pointer:
* \li During calling, implementation should assign the reference of string view passed in argument
* to the string which need to be joined.
* \li Function return true to continue joining. otherwise return false to stop joining.
* The argument content assigned in the calling returning false is not included in join process.
*/
using JoinDataProvider = std::function<bool(NS_YYCC_STRING::u8string_view&)>;
/**
* @brief Universal join function.
* @details
* This function use function pointer as a general data provider interface,
* so this function suit for all types container.
* You can use this universal join function for any custom container by
* using C++ lambda syntax to create a code block adapted to this function pointer.
* @param[in] fct_data The function pointer in JoinDataProvider type prividing the data to be joined.
* @param[in] decilmer The decilmer used for joining.
* @return The result string of joining.
*/
NS_YYCC_STRING::u8string join(JoinDataProvider fct_data, const NS_YYCC_STRING::u8string_view& decilmer);
/**
* @brief Specialized join function for standard library container.
* @tparam InputIt
* Must meet the requirements of LegacyInputIterator.
* It also can be dereferenced and then implicitly converted to NS_YYCC_STRING::u8string_view.
* @param[in] first The beginning of the range of elements to join.
* @param[in] last The terminal of the range of elements to join (exclusive).
* @param[in] decilmer The decilmer used for joining.
* @return The result string of joining.
*/
template<class InputIt>
NS_YYCC_STRING::u8string join(InputIt first, InputIt last, const NS_YYCC_STRING::u8string_view& decilmer) {
return join([&first, &last](NS_YYCC_STRING::u8string_view& view) -> bool {
// if we reach tail, return false to stop join process
if (first == last) return false;
// otherwise fetch data, inc iterator and return.
view = *first;
++first;
return true;
}, decilmer);
}
/**
* @brief Convert given string to lowercase.
* @param[in,out] strl The string to be lowercase.
*/
void lower(NS_YYCC_STRING::u8string& strl);
/**
* @brief Return a copy of the string converted to lowercase.
* @param[in] strl The string to be lowercase.
* @return The copy of the string converted to lowercase.
*/
NS_YYCC_STRING::u8string lower(const NS_YYCC_STRING::u8string_view& strl);
/**
* @brief Convert given string to uppercase.
* @param[in,out] strl The string to be uppercase.
*/
void upper(NS_YYCC_STRING::u8string& strl);
/**
* @brief Return a copy of the string converted to uppercase.
* @param[in] strl The string to be uppercase.
* @return The copy of the string converted to uppercase.
*/
NS_YYCC_STRING::u8string upper(const NS_YYCC_STRING::u8string_view& strl);
/**
* @brief Split given string with specified decilmer.
* @param[in] strl The string need to be splitting.
* @param[in] _decilmer The decilmer for splitting.
* @return
* The split result.
* \par
* If given string or decilmer are empty,
* the result container will only contain 1 entry which is equal to given string.
*/
std::vector<NS_YYCC_STRING::u8string> split(const NS_YYCC_STRING::u8string_view& strl, const NS_YYCC_STRING::u8string_view& _decilmer);
/**
* @brief Split given string with specified decilmer as string view.
* @param[in] strl The string need to be splitting.
* @param[in] _decilmer The decilmer for splitting.
* @return
* The split result with string view format.
* This will not produce any copy of original string.
* \par
* If given string or decilmer are empty,
* the result container will only contain 1 entry which is equal to given string.
* @see Split(const NS_YYCC_STRING::u8string_view&, const NS_YYCC_STRING::u8char*)
*/
std::vector<NS_YYCC_STRING::u8string_view> split_view(const NS_YYCC_STRING::u8string_view& strl, const NS_YYCC_STRING::u8string_view& _decilmer);
}
#undef NS_YYCC_STRING
+33
View File
@@ -0,0 +1,33 @@
#include "reinterpret.hpp"
#define NS_YYCC_STRING ::yycc::string
namespace yycc::string::reinterpret {
const NS_YYCC_STRING::u8char* as_utf8(const char* src) {
return reinterpret_cast<const NS_YYCC_STRING::u8char*>(src);
}
NS_YYCC_STRING::u8char* as_utf8(char* src) {
return reinterpret_cast<NS_YYCC_STRING::u8char*>(src);
}
NS_YYCC_STRING::u8string as_utf8(const std::string_view& src) {
return NS_YYCC_STRING::u8string(reinterpret_cast<const NS_YYCC_STRING::u8char*>(src.data()), src.size());
}
NS_YYCC_STRING::u8string_view as_utf8_view(const std::string_view& src) {
return NS_YYCC_STRING::u8string_view(reinterpret_cast<const NS_YYCC_STRING::u8char*>(src.data()), src.size());
}
const char* as_ordinary(const NS_YYCC_STRING::u8char* src) {
return reinterpret_cast<const char*>(src);
}
char* as_ordinary(NS_YYCC_STRING::u8char* src) {
return reinterpret_cast<char*>(src);
}
std::string as_ordinary(const NS_YYCC_STRING::u8string_view& src) {
return std::string(reinterpret_cast<const char*>(src.data()), src.size());
}
std::string_view as_ordinary_view(const NS_YYCC_STRING::u8string_view& src) {
return std::string_view(reinterpret_cast<const char*>(src.data()), src.size());
}
}
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include "../string.hpp"
#define NS_YYCC_STRING ::yycc::string
namespace yycc::string::reinterpret {
#define _YYCC_U8(strl) u8 ## strl ///< The assistant macro for YYCC_U8.
#define YYCC_U8(strl) (reinterpret_cast<const ::yycc::string::u8char*>(_YYCC_U8(strl))) ///< The macro for creating UTF8 string literal. See \ref library_encoding.
#define YYCC_U8_CHAR(chr) (static_cast<::yycc::string::u8char>(chr)) ///< The macro for casting ordinary char type into YYCC UTF8 char type.
const NS_YYCC_STRING::u8char* as_utf8(const char* src);
NS_YYCC_STRING::u8char* as_utf8(char* src);
NS_YYCC_STRING::u8string as_utf8(const std::string_view& src);
NS_YYCC_STRING::u8string_view as_utf8_view(const std::string_view& src);
const char* as_ordinary(const NS_YYCC_STRING::u8char* src);
char* as_ordinary(NS_YYCC_STRING::u8char* src);
std::string as_ordinary(const NS_YYCC_STRING::u8string_view& src);
std::string_view as_ordinary_view(const NS_YYCC_STRING::u8string_view& src);
}
#undef NS_YYCC_STRING
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#define YYCC_VER_MAJOR 2
#define YYCC_VER_MINOR 0
#define YYCC_VER_PATCH 0
File renamed without changes.
View File
Whitespace-only changes.
View File
Whitespace-only changes.
View File
Whitespace-only changes.
View File
Whitespace-only changes.
+19
View File
@@ -0,0 +1,19 @@
// It is by design that no pragma once or #if to prevent deplicated including.
// Because this header is the part of wrapper, not a real header.
// #pragma once
#include "../macro/os_detector.hpp"
#if YYCC_OS == YYCC_OS_WINDOWS
// Define 2 macros to disallow Windows generate MIN and MAX macros
// which cause std::min and std::max can not function as normal.
#if !defined(WIN32_LEAN_AND_MEAN)
#define WIN32_LEAN_AND_MEAN
#endif
#if !defined(NOMINMAX)
#define NOMINMAX
#endif
#endif
+23
View File
@@ -0,0 +1,23 @@
// It is by design that no pragma once or #if to prevent deplicated including.
// Because this header is the part of wrapper, not a real header.
// #pragma once
#include "../macro/os_detector.hpp"
#if YYCC_OS == YYCC_OS_WINDOWS
// Windows also will generate following macros
// which may cause the function sign is different in Windows and other platforms.
// So we simply remove them.
// Because #undef will not throw error if there are no matched macro,
// so we simply #undef them directly.
#undef GetObject
#undef GetClassName
#undef LoadImage
#undef GetTempPath
#undef GetModuleFileName
#undef CopyFile
#undef MoveFile
#undef DeleteFile
#endif
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include "../macro/os_detector.hpp"
// If we are in Windows,
// we need add 2 macros to disable Windows shitty warnings and errors of
// depracted functions and not secure functions.
#if YYCC_OS == YYCC_OS_WINDOWS
#if !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif
#if !defined(_CRT_NONSTDC_NO_DEPRECATE)
#define _CRT_NONSTDC_NO_DEPRECATE
#endif
#endif
+13 -8
View File
@@ -4,15 +4,22 @@ add_executable(YYCCTestbench "")
target_sources(YYCCTestbench
PRIVATE
main.cpp
yycc/constraint.cpp
yycc/constraint/builder.cpp
yycc/string.cpp
yycc/string/op.cpp
yycc/string/reinterpret.cpp
)
# Add YYCC as its library
# Setup headers
target_include_directories(YYCCTestbench
PRIVATE
YYCCommonplace
PUBLIC
"${CMAKE_CURRENT_LIST_DIR}"
)
# Setup libraries
target_link_libraries(YYCCTestbench
PRIVATE
YYCCommonplace
GTest::gtest_main
)
# Setup C++ standard
target_compile_features(YYCCTestbench PUBLIC cxx_std_17)
@@ -29,8 +36,6 @@ PRIVATE
$<$<CXX_COMPILER_ID:MSVC>:/utf-8>
)
# Install testbench only on Release mode
install(TARGETS YYCCTestbench
CONFIGURATIONS Release RelWithDebInfo MinSizeRel
RUNTIME DESTINATION ${YYCC_INSTALL_BIN_PATH}
)
# Discover all test
include(GoogleTest)
gtest_discover_tests(YYCCTestbench)
+3 -732
View File
@@ -1,735 +1,6 @@
#include <YYCCommonplace.hpp>
#include <cstdio>
#include <set>
#include <map>
namespace Console = YYCC::ConsoleHelper;
namespace YYCCTestbench {
#pragma region Unicode Test Data
// UNICODE Test Strings
// Ref: https://stackoverflow.com/questions/478201/how-to-test-an-application-for-correct-encoding-e-g-utf-8
#define TEST_UNICODE_STR_JAPAN "\u30E6\u30FC\u30B6\u30FC\u5225\u30B5\u30A4\u30C8"
#define TEST_UNICODE_STR_CHINA "\u7B80\u4F53\u4E2D\u6587"
#define TEST_UNICODE_STR_KOREA "\uD06C\uB85C\uC2A4 \uD50C\uB7AB\uD3FC\uC73C\uB85C"
#define TEST_UNICODE_STR_ISRAEL "\u05DE\u05D3\u05D5\u05E8\u05D9\u05DD \u05DE\u05D1\u05D5\u05E7\u05E9\u05D9\u05DD"
#define TEST_UNICODE_STR_EGYPT "\u0623\u0641\u0636\u0644 \u0627\u0644\u0628\u062D\u0648\u062B"
#define TEST_UNICODE_STR_GREECE "\u03A3\u1F72 \u03B3\u03BD\u03C9\u03C1\u03AF\u03B6\u03C9 \u1F00\u03C0\u1F78"
#define TEST_UNICODE_STR_RUSSIA "\u0414\u0435\u0441\u044F\u0442\u0443\u044E \u041C\u0435\u0436\u0434\u0443\u043D\u0430\u0440\u043E\u0434\u043D\u0443\u044E"
#define TEST_UNICODE_STR_THAILAND "\u0E41\u0E1C\u0E48\u0E19\u0E14\u0E34\u0E19\u0E2E\u0E31\u0E48\u0E19\u0E40\u0E2A\u0E37\u0E48\u0E2D\u0E21\u0E42\u0E17\u0E23\u0E21\u0E41\u0E2A\u0E19\u0E2A\u0E31\u0E07\u0E40\u0E27\u0E0A"
#define TEST_UNICODE_STR_FRANCE "fran\u00E7ais langue \u00E9trang\u00E8re"
#define TEST_UNICODE_STR_SPAIN "ma\u00F1ana ol\u00E9"
#define TEST_UNICODE_STR_MATHMATICS "\u222E E\u22C5da = Q, n \u2192 \u221E, \u2211 f(i) = \u220F g(i)"
#define TEST_UNICODE_STR_EMOJI "\U0001F363 \u2716 \U0001F37A" // sushi x beer mug
#define CONCAT(prefix, strl) prefix ## strl
#define CPP_U8_LITERAL(strl) YYCC_U8(strl)
#define CPP_U16_LITERAL(strl) CONCAT(u, strl)
#define CPP_U32_LITERAL(strl) CONCAT(U, strl)
#define CPP_WSTR_LITERAL(strl) CONCAT(L, strl)
static std::vector<YYCC::yycc_u8string> c_UTF8TestStrTable {
CPP_U8_LITERAL(TEST_UNICODE_STR_JAPAN),
CPP_U8_LITERAL(TEST_UNICODE_STR_CHINA),
CPP_U8_LITERAL(TEST_UNICODE_STR_KOREA),
CPP_U8_LITERAL(TEST_UNICODE_STR_ISRAEL),
CPP_U8_LITERAL(TEST_UNICODE_STR_EGYPT),
CPP_U8_LITERAL(TEST_UNICODE_STR_GREECE),
CPP_U8_LITERAL(TEST_UNICODE_STR_RUSSIA),
CPP_U8_LITERAL(TEST_UNICODE_STR_THAILAND),
CPP_U8_LITERAL(TEST_UNICODE_STR_FRANCE),
CPP_U8_LITERAL(TEST_UNICODE_STR_SPAIN),
CPP_U8_LITERAL(TEST_UNICODE_STR_MATHMATICS),
CPP_U8_LITERAL(TEST_UNICODE_STR_EMOJI),
};
static std::vector<std::wstring> c_WStrTestStrTable {
CPP_WSTR_LITERAL(TEST_UNICODE_STR_JAPAN),
CPP_WSTR_LITERAL(TEST_UNICODE_STR_CHINA),
CPP_WSTR_LITERAL(TEST_UNICODE_STR_KOREA),
CPP_WSTR_LITERAL(TEST_UNICODE_STR_ISRAEL),
CPP_WSTR_LITERAL(TEST_UNICODE_STR_EGYPT),
CPP_WSTR_LITERAL(TEST_UNICODE_STR_GREECE),
CPP_WSTR_LITERAL(TEST_UNICODE_STR_RUSSIA),
CPP_WSTR_LITERAL(TEST_UNICODE_STR_THAILAND),
CPP_WSTR_LITERAL(TEST_UNICODE_STR_FRANCE),
CPP_WSTR_LITERAL(TEST_UNICODE_STR_SPAIN),
CPP_WSTR_LITERAL(TEST_UNICODE_STR_MATHMATICS),
CPP_WSTR_LITERAL(TEST_UNICODE_STR_EMOJI),
};
static std::vector<std::u16string> c_UTF16TestStrTable {
CPP_U16_LITERAL(TEST_UNICODE_STR_JAPAN),
CPP_U16_LITERAL(TEST_UNICODE_STR_CHINA),
CPP_U16_LITERAL(TEST_UNICODE_STR_KOREA),
CPP_U16_LITERAL(TEST_UNICODE_STR_ISRAEL),
CPP_U16_LITERAL(TEST_UNICODE_STR_EGYPT),
CPP_U16_LITERAL(TEST_UNICODE_STR_GREECE),
CPP_U16_LITERAL(TEST_UNICODE_STR_RUSSIA),
CPP_U16_LITERAL(TEST_UNICODE_STR_THAILAND),
CPP_U16_LITERAL(TEST_UNICODE_STR_FRANCE),
CPP_U16_LITERAL(TEST_UNICODE_STR_SPAIN),
CPP_U16_LITERAL(TEST_UNICODE_STR_MATHMATICS),
CPP_U16_LITERAL(TEST_UNICODE_STR_EMOJI),
};
static std::vector<std::u32string> c_UTF32TestStrTable {
CPP_U32_LITERAL(TEST_UNICODE_STR_JAPAN),
CPP_U32_LITERAL(TEST_UNICODE_STR_CHINA),
CPP_U32_LITERAL(TEST_UNICODE_STR_KOREA),
CPP_U32_LITERAL(TEST_UNICODE_STR_ISRAEL),
CPP_U32_LITERAL(TEST_UNICODE_STR_EGYPT),
CPP_U32_LITERAL(TEST_UNICODE_STR_GREECE),
CPP_U32_LITERAL(TEST_UNICODE_STR_RUSSIA),
CPP_U32_LITERAL(TEST_UNICODE_STR_THAILAND),
CPP_U32_LITERAL(TEST_UNICODE_STR_FRANCE),
CPP_U32_LITERAL(TEST_UNICODE_STR_SPAIN),
CPP_U32_LITERAL(TEST_UNICODE_STR_MATHMATICS),
CPP_U32_LITERAL(TEST_UNICODE_STR_EMOJI),
};
#undef CPP_WSTR_LITERAL
#undef CPP_U32_LITERAL
#undef CPP_U16_LITERAL
#undef CPP_U8_LITERAL
#undef CONCAT
#pragma endregion
static void Assert(bool condition, const YYCC::yycc_char8_t* description) {
if (condition) {
Console::FormatLine(YYCC_U8(YYCC_COLOR_LIGHT_GREEN("OK: %s")), description);
} else {
Console::FormatLine(YYCC_U8(YYCC_COLOR_LIGHT_RED("Failed: %s\n")), description);
std::abort();
}
}
static void ConsoleTestbench() {
// Color Test
Console::EnableColorfulConsole();
Console::WriteLine(YYCC_U8("Color Test:"));
#define TEST_MACRO(col) Console::WriteLine(YYCC_U8("\t" YYCC_COLOR_ ## col ("\u2588\u2588") YYCC_COLOR_LIGHT_ ## col("\u2588\u2588") " " #col " / LIGHT " #col ));
// U+2588 is full block
TEST_MACRO(BLACK);
TEST_MACRO(RED);
TEST_MACRO(GREEN);
TEST_MACRO(YELLOW);
TEST_MACRO(BLUE);
TEST_MACRO(MAGENTA);
TEST_MACRO(CYAN);
TEST_MACRO(WHITE);
#undef TEST_MACRO
// UTF8 Output Test
Console::WriteLine(YYCC_U8("UTF8 Output Test:"));
for (const auto& strl : c_UTF8TestStrTable) {
Console::FormatLine(YYCC_U8("\t%s"), strl.c_str());
}
// UTF8 Input Test
Console::WriteLine(YYCC_U8("UTF8 Input Test:"));
for (const auto& strl : c_UTF8TestStrTable) {
Console::FormatLine(YYCC_U8("\tPlease type: %s"), strl.c_str());
Console::Write(YYCC_U8("\t> "));
YYCC::yycc_u8string gotten(Console::ReadLine());
if (gotten == strl) Console::FormatLine(YYCC_U8(YYCC_COLOR_LIGHT_GREEN("\tMatched! Got: %s")), gotten.c_str());
else Console::FormatLine(YYCC_U8(YYCC_COLOR_LIGHT_RED("\tNOT Matched! Got: %s")), gotten.c_str());
}
}
static void EncodingTestbench() {
// get test tuple size
size_t count = c_UTF8TestStrTable.size();
// check the convertion between given string
for (size_t i = 0u; i < count; ++i) {
// get item
const auto& u8str = c_UTF8TestStrTable[i];
const auto& u16str = c_UTF16TestStrTable[i];
const auto& u32str = c_UTF32TestStrTable[i];
// create cache variables
YYCC::yycc_u8string u8cache;
std::u16string u16cache;
std::u32string u32cache;
// do convertion check
Assert(YYCC::EncodingHelper::UTF8ToUTF16(u8str, u16cache) && u16cache == u16str, YYCC_U8("YYCC::EncodingHelper::UTF8ToUTF16"));
Assert(YYCC::EncodingHelper::UTF8ToUTF32(u8str, u32cache) && u32cache == u32str, YYCC_U8("YYCC::EncodingHelper::UTF8ToUTF32"));
Assert(YYCC::EncodingHelper::UTF16ToUTF8(u16str, u8cache) && u8cache == u8str, YYCC_U8("YYCC::EncodingHelper::UTF16ToUTF8"));
Assert(YYCC::EncodingHelper::UTF32ToUTF8(u32str, u8cache) && u8cache == u8str, YYCC_U8("YYCC::EncodingHelper::UTF32ToUTF8"));
}
// check wstring convertion on windows
#if YYCC_OS == YYCC_OS_WINDOWS
for (size_t i = 0u; i < count; ++i) {
// get item
const auto& u8str = c_UTF8TestStrTable[i];
const auto& wstr = c_WStrTestStrTable[i];
// create cache variables
YYCC::yycc_u8string u8cache;
std::wstring wcache;
// do convertion check
Assert(YYCC::EncodingHelper::UTF8ToWchar(u8str.c_str(), wcache) && wcache == wstr, YYCC_U8("YYCC::EncodingHelper::UTF8ToWchar"));
Assert(YYCC::EncodingHelper::WcharToUTF8(wstr.c_str(), u8cache) && u8cache == u8str, YYCC_U8("YYCC::EncodingHelper::WcharToUTF8"));
}
#endif
}
static void StringTestbench() {
// Test Printf
auto test_printf = YYCC::StringHelper::Printf(YYCC_U8("%s == %s"), YYCC_U8("Hello World"), YYCC_U8("Hello, world"));
Assert(test_printf == YYCC_U8("Hello World == Hello, world"), YYCC_U8("YYCC::StringHelper::Printf"));
// Test Replace
auto test_replace = YYCC::StringHelper::Replace(YYCC_U8("aabbcc"), YYCC_U8("bb"), YYCC_U8("dd")); // normal case
Assert(test_replace == YYCC_U8("aaddcc"), YYCC_U8("YYCC::StringHelper::Replace"));
test_replace = YYCC::StringHelper::Replace(YYCC_U8("aabbcc"), YYCC_U8("zz"), YYCC_U8("yy")); // no replace
Assert(test_replace == YYCC_U8("aabbcc"), YYCC_U8("YYCC::StringHelper::Replace"));
test_replace = YYCC::StringHelper::Replace(YYCC_U8("aabbcc"), YYCC::yycc_u8string_view(), YYCC_U8("zz")); // empty finding
Assert(test_replace == YYCC_U8("aabbcc"), YYCC_U8("YYCC::StringHelper::Replace"));
test_replace = YYCC::StringHelper::Replace(YYCC_U8("aaaabbaa"), YYCC_U8("aa"), YYCC_U8("")); // no replaced string
Assert(test_replace == YYCC_U8("bb"), YYCC_U8("YYCC::StringHelper::Replace"));
test_replace = YYCC::StringHelper::Replace(YYCC_U8("aaxcc"), YYCC_U8("x"), YYCC_U8("yx")); // nested replacing
Assert(test_replace == YYCC_U8("aayxcc"), YYCC_U8("YYCC::StringHelper::Replace"));
test_replace = YYCC::StringHelper::Replace(YYCC::yycc_u8string_view(), YYCC_U8(""), YYCC_U8("xy")); // empty source string
Assert(test_replace == YYCC_U8(""), YYCC_U8("YYCC::StringHelper::Replace"));
// Test Upper / Lower
auto test_lower = YYCC::StringHelper::Lower(YYCC_U8("LOWER"));
Assert(test_lower == YYCC_U8("lower"), YYCC_U8("YYCC::StringHelper::Lower"));
auto test_upper = YYCC::StringHelper::Upper(YYCC_U8("upper"));
Assert(test_upper == YYCC_U8("UPPER"), YYCC_U8("YYCC::StringHelper::Upper"));
// Test Join
std::vector<YYCC::yycc_u8string> test_join_container {
YYCC_U8(""), YYCC_U8("1"), YYCC_U8("2"), YYCC_U8("")
};
auto test_join = YYCC::StringHelper::Join(test_join_container.begin(), test_join_container.end(), YYCC_U8(", "));
Assert(test_join == YYCC_U8(", 1, 2, "), YYCC_U8("YYCC::StringHelper::Join"));
// Test Split
auto test_split = YYCC::StringHelper::Split(YYCC_U8(", 1, 2, "), YYCC_U8(", ")); // normal
Assert(test_split.size() == 4u, YYCC_U8("YYCC::StringHelper::Split"));
Assert(test_split[0] == YYCC_U8(""), YYCC_U8("YYCC::StringHelper::Split"));
Assert(test_split[1] == YYCC_U8("1"), YYCC_U8("YYCC::StringHelper::Split"));
Assert(test_split[2] == YYCC_U8("2"), YYCC_U8("YYCC::StringHelper::Split"));
Assert(test_split[3] == YYCC_U8(""), YYCC_U8("YYCC::StringHelper::Split"));
test_split = YYCC::StringHelper::Split(YYCC_U8("test"), YYCC_U8("-")); // no matched decilmer
Assert(test_split.size() == 1u, YYCC_U8("YYCC::StringHelper::Split"));
Assert(test_split[0] == YYCC_U8("test"), YYCC_U8("YYCC::StringHelper::Split"));
test_split = YYCC::StringHelper::Split(YYCC_U8("test"), YYCC::yycc_u8string_view()); // empty decilmer
Assert(test_split.size() == 1u, YYCC_U8("YYCC::StringHelper::Split"));
Assert(test_split[0] == YYCC_U8("test"), YYCC_U8("YYCC::StringHelper::Split"));
test_split = YYCC::StringHelper::Split(YYCC::yycc_u8string_view(), YYCC_U8("")); // empty source string
Assert(test_split.size() == 1u, YYCC_U8("YYCC::StringHelper::Split"));
Assert(test_split[0].empty(), YYCC_U8("YYCC::StringHelper::Split"));
}
static void ParserTestbench() {
// Test success TryParse
#define TEST_MACRO(type_t, value, string_value, ...) { \
YYCC::yycc_u8string cache_string(YYCC_U8(string_value)); \
type_t cache; \
Assert(YYCC::ParserHelper::TryParse<type_t>(cache_string, cache, ##__VA_ARGS__) && cache == value, YYCC_U8("YYCC::StringHelper::TryParse<" #type_t ">")); \
}
TEST_MACRO(int8_t, INT8_C(-61), "-61");
TEST_MACRO(uint8_t, UINT8_C(200), "200");
TEST_MACRO(int16_t, INT16_C(6161), "6161");
TEST_MACRO(uint16_t, UINT16_C(32800), "32800");
TEST_MACRO(int32_t, INT32_C(61616161), "61616161");
TEST_MACRO(uint32_t, UINT32_C(4294967293), "4294967293");
TEST_MACRO(int64_t, INT64_C(616161616161), "616161616161");
TEST_MACRO(uint64_t, UINT64_C(9223372036854775807), "9223372036854775807");
TEST_MACRO(uint32_t, UINT32_C(0xffff), "ffff", 16);
TEST_MACRO(float, 1.0f, "1.0");
TEST_MACRO(double, 1.0, "1.0");
TEST_MACRO(bool, true, "true");
#undef TEST_MACRO
// Test failed TryParse
#define TEST_MACRO(type_t, string_value, ...) { \
YYCC::yycc_u8string cache_string(YYCC_U8(string_value)); \
type_t cache; \
Assert(!YYCC::ParserHelper::TryParse<type_t>(cache_string, cache, ##__VA_ARGS__), YYCC_U8("YYCC::StringHelper::TryParse<" #type_t ">")); \
}
TEST_MACRO(int8_t, "6161");
TEST_MACRO(uint8_t, "32800");
TEST_MACRO(int16_t, "61616161");
TEST_MACRO(uint16_t, "4294967293");
TEST_MACRO(int32_t, "616161616161");
TEST_MACRO(uint32_t, "9223372036854775807");
TEST_MACRO(int64_t, "616161616161616161616161");
TEST_MACRO(uint64_t, "92233720368547758079223372036854775807");
TEST_MACRO(float, "1e40");
TEST_MACRO(double, "1e114514");
TEST_MACRO(bool, "hello, world!");
#undef TEST_MACRO
// Test ToString
#define TEST_MACRO(type_t, value, string_value, ...) { \
type_t cache = value; \
YYCC::yycc_u8string ret(YYCC::ParserHelper::ToString<type_t>(cache, ##__VA_ARGS__)); \
Assert(ret == YYCC_U8(string_value), YYCC_U8("YYCC::StringHelper::ToString<" #type_t ">")); \
}
TEST_MACRO(int8_t, INT8_C(-61), "-61");
TEST_MACRO(uint8_t, UINT8_C(200), "200");
TEST_MACRO(int16_t, INT16_C(6161), "6161");
TEST_MACRO(uint16_t, UINT16_C(32800), "32800");
TEST_MACRO(int32_t, INT32_C(61616161), "61616161");
TEST_MACRO(uint32_t, UINT32_C(4294967293), "4294967293");
TEST_MACRO(int64_t, INT64_C(616161616161), "616161616161");
TEST_MACRO(uint64_t, UINT64_C(9223372036854775807), "9223372036854775807");
TEST_MACRO(uint32_t, UINT32_C(0xffff), "ffff", 16);
TEST_MACRO(float, 1.0f, "1.0", std::chars_format::fixed, 1);
TEST_MACRO(double, 1.0, "1.0", std::chars_format::fixed, 1);
TEST_MACRO(bool, true, "true");
#undef TEST_MACRO
}
static void DialogTestbench() {
#if YYCC_OS == YYCC_OS_WINDOWS
YYCC::yycc_u8string ret;
std::vector<YYCC::yycc_u8string> rets;
YYCC::DialogHelper::FileDialog params;
auto& filters = params.ConfigreFileTypes();
filters.Add(YYCC_U8("Microsoft Word (*.docx; *.doc)"), { YYCC_U8("*.docx"), YYCC_U8("*.doc") });
filters.Add(YYCC_U8("Microsoft Excel (*.xlsx; *.xls)"), { YYCC_U8("*.xlsx"), YYCC_U8("*.xls") });
filters.Add(YYCC_U8("Microsoft PowerPoint (*.pptx; *.ppt)"), { YYCC_U8("*.pptx"), YYCC_U8("*.ppt") });
filters.Add(YYCC_U8("Text File (*.txt)"), { YYCC_U8("*.txt") });
filters.Add(YYCC_U8("All Files (*.*)"), { YYCC_U8("*.*") });
params.SetDefaultFileTypeIndex(0u);
if (YYCC::DialogHelper::OpenFileDialog(params, ret)) {
Console::FormatLine(YYCC_U8("Open File: %s"), ret.c_str());
}
if (YYCC::DialogHelper::OpenMultipleFileDialog(params, rets)) {
Console::WriteLine(YYCC_U8("Open Multiple Files:"));
for (const auto& item : rets) {
Console::FormatLine(YYCC_U8("\t%s"), item.c_str());
}
}
if (YYCC::DialogHelper::SaveFileDialog(params, ret)) {
Console::FormatLine(YYCC_U8("Save File: %s"), ret.c_str());
}
params.Clear();
if (YYCC::DialogHelper::OpenFolderDialog(params, ret)) {
Console::FormatLine(YYCC_U8("Open Folder: %s"), ret.c_str());
}
#endif
}
static void ExceptionTestbench() {
#if YYCC_OS == YYCC_OS_WINDOWS
YYCC::ExceptionHelper::Register([](const YYCC::yycc_u8string& log_path, const YYCC::yycc_u8string& coredump_path) -> void {
MessageBoxW(
NULL,
YYCC::EncodingHelper::UTF8ToWchar(
YYCC::StringHelper::Printf(YYCC_U8("Log generated:\nLog path: %s\nCore dump path: %s"), log_path.c_str(), coredump_path.c_str())
).c_str(),
L"Fatal Error", MB_OK + MB_ICONERROR
);
}
);
// Perform a div zero exception.
#if defined (YYCC_DEBUG_UE_FILTER)
// Reference: https://stackoverflow.com/questions/20981982/is-it-possible-to-debug-unhandledexceptionfilters-with-a-debugger
__try {
// all of code normally inside of main or WinMain here...
int i = 1, j = 0;
int k = i / j;
} __except (YYCC::ExceptionHelper::DebugCallUExceptionImpl(GetExceptionInformation())) {
OutputDebugStringW(L"executed filter function\n");
}
#else
int i = 1, j = 0;
int k = i / j;
#endif
YYCC::ExceptionHelper::Unregister();
#endif
}
static void WinFctTestbench() {
#if YYCC_OS == YYCC_OS_WINDOWS
HMODULE test_current_module;
Assert((test_current_module = YYCC::WinFctHelper::GetCurrentModule()) != nullptr, YYCC_U8("YYCC::WinFctHelper::GetCurrentModule"));
Console::FormatLine(YYCC_U8("Current Module HANDLE: 0x%" PRI_XPTR_LEFT_PADDING PRIXPTR), test_current_module);
YYCC::yycc_u8string test_temp;
Assert(YYCC::WinFctHelper::GetTempDirectory(test_temp), YYCC_U8("YYCC::WinFctHelper::GetTempDirectory"));
Console::FormatLine(YYCC_U8("Temp Directory: %s"), test_temp.c_str());
YYCC::yycc_u8string test_module_name;
Assert(YYCC::WinFctHelper::GetModuleFileName(YYCC::WinFctHelper::GetCurrentModule(), test_module_name), YYCC_U8("YYCC::WinFctHelper::GetModuleFileName"));
Console::FormatLine(YYCC_U8("Current Module File Name: %s"), test_module_name.c_str());
YYCC::yycc_u8string test_localappdata_path;
Assert(YYCC::WinFctHelper::GetLocalAppData(test_localappdata_path), YYCC_U8("YYCC::WinFctHelper::GetLocalAppData"));
Console::FormatLine(YYCC_U8("Local AppData: %s"), test_localappdata_path.c_str());
Assert(YYCC::WinFctHelper::IsValidCodePage(static_cast<UINT>(1252)), YYCC_U8("YYCC::WinFctHelper::IsValidCodePage"));
Assert(!YYCC::WinFctHelper::IsValidCodePage(static_cast<UINT>(114514)), YYCC_U8("YYCC::WinFctHelper::IsValidCodePage"));
// MARK: There is no testbench for MoveFile, CopyFile DeleteFile.
// Because they can operate file system files.
// And may cause test environment entering unstable status.
#endif
}
static void StdPatchTestbench() {
// Std Path
std::filesystem::path test_path;
for (const auto& strl : c_UTF8TestStrTable) {
test_path /= YYCC::StdPatch::ToStdPath(strl);
}
YYCC::yycc_u8string test_slashed_path(YYCC::StdPatch::ToUTF8Path(test_path));
#if YYCC_OS == YYCC_OS_WINDOWS
std::wstring wdecilmer(1u, std::filesystem::path::preferred_separator);
YYCC::yycc_u8string decilmer(YYCC::EncodingHelper::WcharToUTF8(wdecilmer));
#else
YYCC::yycc_u8string decilmer(1u, std::filesystem::path::preferred_separator);
#endif
YYCC::yycc_u8string test_joined_path(YYCC::StringHelper::Join(c_UTF8TestStrTable.begin(), c_UTF8TestStrTable.end(), decilmer));
Assert(test_slashed_path == test_joined_path, YYCC_U8("YYCC::StdPatch::ToStdPath, YYCC::StdPatch::ToUTF8Path"));
// StartsWith, EndsWith
YYCC::yycc_u8string test_starts_ends_with(YYCC_U8("aaabbbccc"));
Assert(YYCC::StdPatch::StartsWith(test_starts_ends_with, YYCC_U8("aaa")), YYCC_U8("YYCC::StdPatch::StartsWith"));
Assert(!YYCC::StdPatch::StartsWith(test_starts_ends_with, YYCC_U8("ccc")), YYCC_U8("YYCC::StdPatch::StartsWith"));
Assert(!YYCC::StdPatch::EndsWith(test_starts_ends_with, YYCC_U8("aaa")), YYCC_U8("YYCC::StdPatch::EndsWith"));
Assert(YYCC::StdPatch::EndsWith(test_starts_ends_with, YYCC_U8("ccc")), YYCC_U8("YYCC::StdPatch::EndsWith"));
// Contains
std::set<int> test_set { 1, 2, 3, 4, 6, 7 };
Assert(YYCC::StdPatch::Contains(test_set, static_cast<int>(1)), YYCC_U8("YYCC::StdPatch::Contains"));
Assert(!YYCC::StdPatch::Contains(test_set, static_cast<int>(5)), YYCC_U8("YYCC::StdPatch::Contains"));
std::map<int, float> test_map { { 1, 1.0f }, { 4, 4.0f } };
Assert(YYCC::StdPatch::Contains(test_map, static_cast<int>(1)), YYCC_U8("YYCC::StdPatch::Contains"));
Assert(!YYCC::StdPatch::Contains(test_map, static_cast<int>(5)), YYCC_U8("YYCC::StdPatch::Contains"));
}
enum class TestFlagEnum : uint8_t {
Test1 = 0b00000000,
Test2 = 0b00000001,
Test3 = 0b00000010,
Test4 = 0b00000100,
Test5 = 0b00001000,
Test6 = 0b00010000,
Test7 = 0b00100000,
Test8 = 0b01000000,
Test9 = 0b10000000,
Inverted = 0b01111111,
Merged = Test3 + Test5,
};
static void EnumHelperTestbench() {
TestFlagEnum val;
Assert(YYCC::EnumHelper::Merge(TestFlagEnum::Test3, TestFlagEnum::Test5) == TestFlagEnum::Merged, YYCC_U8("YYCC::EnumHelper::Merge"));
Assert(YYCC::EnumHelper::Invert(TestFlagEnum::Test9) == TestFlagEnum::Inverted, YYCC_U8("YYCC::EnumHelper::Invert"));
val = YYCC::EnumHelper::Merge(TestFlagEnum::Test3, TestFlagEnum::Test5);
YYCC::EnumHelper::Mask(val, TestFlagEnum::Test3);
Assert(YYCC::EnumHelper::Bool(val), YYCC_U8("YYCC::EnumHelper::Mask"));
val = YYCC::EnumHelper::Merge(TestFlagEnum::Test3, TestFlagEnum::Test5);
YYCC::EnumHelper::Mask(val, TestFlagEnum::Test4);
Assert(!YYCC::EnumHelper::Bool(val), YYCC_U8("YYCC::EnumHelper::Mask"));
val = TestFlagEnum::Test3;
YYCC::EnumHelper::Add(val, TestFlagEnum::Test5);
Assert(val == TestFlagEnum::Merged, YYCC_U8("YYCC::EnumHelper::Add"));
val = TestFlagEnum::Merged;
YYCC::EnumHelper::Remove(val, TestFlagEnum::Test5);
Assert(val == TestFlagEnum::Test3, YYCC_U8("YYCC::EnumHelper::Remove"));
val = YYCC::EnumHelper::Merge(TestFlagEnum::Test3, TestFlagEnum::Test5);
Assert(YYCC::EnumHelper::Has(val, TestFlagEnum::Test3), YYCC_U8("YYCC::EnumHelper::Has"));
Assert(!YYCC::EnumHelper::Has(val, TestFlagEnum::Test4), YYCC_U8("YYCC::EnumHelper::Has"));
Assert(!YYCC::EnumHelper::Bool(TestFlagEnum::Test1), YYCC_U8("YYCC::EnumHelper::Bool"));
Assert(YYCC::EnumHelper::Bool(TestFlagEnum::Test2), YYCC_U8("YYCC::EnumHelper::Bool"));
}
static void VersionMacroTestbench() {
Assert(YYCC_VERCMP_E(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_E"));
Assert(!YYCC_VERCMP_NE(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_NE"));
Assert(YYCC_VERCMP_G(1, 2, 3, 0, 2, 5), YYCC_U8("YYCC_VERCMP_G"));
Assert(YYCC_VERCMP_GE(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_GE"));
Assert(YYCC_VERCMP_NL(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_NL"));
Assert(YYCC_VERCMP_L(0, 2, 5, 1, 2, 3), YYCC_U8("YYCC_VERCMP_L"));
Assert(YYCC_VERCMP_LE(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_LE"));
Assert(YYCC_VERCMP_NG(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_NG"));
}
enum class TestEnum : int8_t {
Test1, Test2, Test3
};
class TestConfigManager {
public:
TestConfigManager() :
m_IntSetting(YYCC_U8("int-setting"), INT32_C(0)),
m_FloatSetting(YYCC_U8("float-setting"), 0.0f),
m_StringSetting(YYCC_U8("string-setting"), YYCC_U8("")),
m_BoolSetting(YYCC_U8("bool-setting"), false),
m_ClampedFloatSetting(YYCC_U8("clamped-float-setting"), 0.0f, YYCC::Constraints::GetMinMaxRangeConstraint<float>(-1.0f, 1.0f)),
m_EnumSetting(YYCC_U8("enum-setting"), TestEnum::Test1),
m_CoreManager(YYCC_U8("test.cfg"), UINT64_C(0), {
&m_IntSetting, &m_FloatSetting, &m_StringSetting, &m_BoolSetting, &m_ClampedFloatSetting, &m_EnumSetting
}) {}
~TestConfigManager() {}
void PrintSettings() {
Console::WriteLine(YYCC_U8("Config Manager Settings:"));
Console::FormatLine(YYCC_U8("\tint-setting: %" PRIi32), m_IntSetting.Get());
Console::FormatLine(YYCC_U8("\tfloat-setting: %f"), m_FloatSetting.Get());
Console::FormatLine(YYCC_U8("\tstring-setting: %s"), m_StringSetting.Get().c_str());
Console::FormatLine(YYCC_U8("\tbool-setting: %s"), m_BoolSetting.Get() ? YYCC_U8("true") : YYCC_U8("false"));
Console::FormatLine(YYCC_U8("\tfloat-setting: %f"), m_ClampedFloatSetting.Get());
Console::FormatLine(YYCC_U8("\tenum-setting: %" PRIi8), static_cast<std::underlying_type_t<TestEnum>>(m_EnumSetting.Get()));
}
YYCC::ConfigManager::NumberSetting<int32_t> m_IntSetting;
YYCC::ConfigManager::NumberSetting<float> m_FloatSetting;
YYCC::ConfigManager::StringSetting m_StringSetting;
YYCC::ConfigManager::NumberSetting<bool> m_BoolSetting;
YYCC::ConfigManager::NumberSetting<float> m_ClampedFloatSetting;
YYCC::ConfigManager::NumberSetting<TestEnum> m_EnumSetting;
YYCC::ConfigManager::CoreManager m_CoreManager;
};
static void ConfigManagerTestbench() {
// init cfg manager
TestConfigManager test;
// test constraint works
Assert(!test.m_ClampedFloatSetting.Set(2.0f), YYCC_U8("YYCC::Constraints::Constraint"));
Assert(test.m_ClampedFloatSetting.Get() == 0.0f, YYCC_U8("YYCC::Constraints::Constraint"));
// test modify settings
#define TEST_MACRO(member_name, set_val) { \
Assert(test.member_name.Set(set_val), YYCC_U8("YYCC::ConfigManager::AbstractSetting::Set")); \
Assert(test.member_name.Get() == set_val, YYCC_U8("YYCC::ConfigManager::AbstractSetting::Set")); \
}
TEST_MACRO(m_IntSetting, INT32_C(114));
TEST_MACRO(m_FloatSetting, 2.0f);
TEST_MACRO(m_StringSetting, YYCC_U8("fuck"));
TEST_MACRO(m_BoolSetting, true);
TEST_MACRO(m_ClampedFloatSetting, 0.5f);
TEST_MACRO(m_EnumSetting, TestEnum::Test2);
#undef TEST_MACRO
// test save
test.PrintSettings();
Assert(test.m_CoreManager.Save(), YYCC_U8("YYCC::ConfigManager::CoreManager::Save"));
// test reset
test.m_CoreManager.Reset();
test.PrintSettings();
Assert(test.m_IntSetting.Get() == INT32_C(0), YYCC_U8("YYCC::ConfigManager::CoreManager::Reset"));
Assert(test.m_FloatSetting.Get() == 0.0f, YYCC_U8("YYCC::ConfigManager::CoreManager::Reset"));
Assert(test.m_StringSetting.Get() == YYCC_U8(""), YYCC_U8("YYCC::ConfigManager::CoreManager::Reset"));
Assert(test.m_BoolSetting.Get() == false, YYCC_U8("YYCC::ConfigManager::CoreManager::Reset"));
Assert(test.m_ClampedFloatSetting.Get() == 0.0f, YYCC_U8("YYCC::ConfigManager::CoreManager::Reset"));
Assert(test.m_EnumSetting.Get() == TestEnum::Test1, YYCC_U8("YYCC::ConfigManager::CoreManager::Reset"));
// test load
YYCC::ConfigManager::ConfigLoadResult wrong_result = YYCC::EnumHelper::Merge(
YYCC::ConfigManager::ConfigLoadResult::ItemError,
YYCC::ConfigManager::ConfigLoadResult::BrokenFile
);
Assert(!YYCC::EnumHelper::Has(test.m_CoreManager.Load(), wrong_result), YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
test.PrintSettings();
Assert(test.m_IntSetting.Get() == INT32_C(114), YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
Assert(test.m_FloatSetting.Get() == 2.0f, YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
Assert(test.m_StringSetting.Get() == YYCC_U8("fuck"), YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
Assert(test.m_BoolSetting.Get() == true, YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
Assert(test.m_ClampedFloatSetting.Get() == 0.5f, YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
Assert(test.m_EnumSetting.Get() == TestEnum::Test2, YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
}
class TestArgParser {
public:
TestArgParser() :
m_IntArgument(YYCC_U8("int"), YYCC_U8_CHAR('i'), YYCC_U8("integral argument"), YYCC_U8("114514")),
m_FloatArgument(nullptr, YYCC_U8_CHAR('f'), nullptr, nullptr, true),
m_StringArgument(YYCC_U8("string"), YYCC::ArgParser::AbstractArgument::NO_SHORT_NAME, nullptr, nullptr, true),
m_BoolArgument(nullptr, YYCC_U8_CHAR('b'), nullptr),
m_ClampedFloatArgument(YYCC_U8("clamped-float"), YYCC::ArgParser::AbstractArgument::NO_SHORT_NAME, nullptr, nullptr, true, YYCC::Constraints::GetMinMaxRangeConstraint<float>(-1.0f, 1.0f)),
m_OptionContext(YYCC_U8("TestArgParser"), YYCC_U8("This is the testbench of argument parser."), {
&m_IntArgument, &m_FloatArgument, &m_StringArgument,
&m_BoolArgument, &m_ClampedFloatArgument
}) {}
~TestArgParser() {}
YYCC::ArgParser::NumberArgument<int32_t> m_IntArgument;
YYCC::ArgParser::NumberArgument<float> m_FloatArgument;
YYCC::ArgParser::StringArgument m_StringArgument;
YYCC::ArgParser::SwitchArgument m_BoolArgument;
YYCC::ArgParser::NumberArgument<float> m_ClampedFloatArgument;
YYCC::ArgParser::OptionContext m_OptionContext;
};
static void ArgParserTestbench(int argc, char* argv[]) {
// test command line getter
{
YYCC::ConsoleHelper::WriteLine(YYCC_U8("YYCC::ArgParser::ArgumentList::CreateFromStd"));
auto result = YYCC::ArgParser::ArgumentList::CreateFromStd(argc, argv);
for (result.Reset(); !result.IsEOF(); result.Next()) {
YYCC::ConsoleHelper::FormatLine(YYCC_U8("\t%s"), result.Argument().c_str());
}
}
#if YYCC_OS == YYCC_OS_WINDOWS
{
YYCC::ConsoleHelper::WriteLine(YYCC_U8("YYCC::ArgParser::ArgumentList::CreateFromWin32"));
auto result = YYCC::ArgParser::ArgumentList::CreateFromWin32();
for (result.Reset(); !result.IsEOF(); result.Next()) {
YYCC::ConsoleHelper::FormatLine(YYCC_U8("\t%s"), result.Argument().c_str());
}
}
#endif
// test option context
// init option context
TestArgParser test;
#define PREPARE_DATA(...) const char* test_argv[] = { __VA_ARGS__ }; \
auto al = YYCC::ArgParser::ArgumentList::CreateFromStd(sizeof(test_argv) / sizeof(char*), const_cast<char**>(test_argv));
// normal test
{
PREPARE_DATA("exec", "-i", "114514");
Assert(test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
Assert(test.m_IntArgument.IsCaptured() && test.m_IntArgument.Get() == UINT32_C(114514), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
Assert(!test.m_BoolArgument.IsCaptured(), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
Assert(!test.m_FloatArgument.IsCaptured(), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
Assert(!test.m_StringArgument.IsCaptured(), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
Assert(!test.m_BoolArgument.IsCaptured(), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
Assert(!test.m_ClampedFloatArgument.IsCaptured(), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
test.m_OptionContext.Reset();
}
// no argument
{
PREPARE_DATA("exec");
Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
test.m_OptionContext.Reset();
}
// error argument
{
PREPARE_DATA("exec", "-?", "114514");
Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
test.m_OptionContext.Reset();
}
// lost argument
{
PREPARE_DATA("exec", "-i");
Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
test.m_OptionContext.Reset();
}
// dplicated assign
{
PREPARE_DATA("exec", "-i", "114514" "--int", "114514");
Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
test.m_OptionContext.Reset();
}
// extra useless argument
{
PREPARE_DATA("exec", "-i", "114514" "1919810");
Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
test.m_OptionContext.Reset();
}
// invalid clamp argument
{
PREPARE_DATA("exec", "-i", "114514", "--clamped-float", "114.0");
Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
test.m_OptionContext.Reset();
}
// full argument
{
PREPARE_DATA("exec", "-i", "114514", "-f", "2.0", "--string", "fuck", "-b", "--clamped-float", "0.5");
Assert(test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
Assert(test.m_IntArgument.IsCaptured() && test.m_IntArgument.Get() == UINT32_C(114514), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
Assert(test.m_FloatArgument.IsCaptured() && test.m_FloatArgument.Get() == 2.0f, YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
Assert(test.m_StringArgument.IsCaptured() && test.m_StringArgument.Get() == YYCC_U8("fuck"), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
Assert(test.m_BoolArgument.IsCaptured(), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
Assert(test.m_ClampedFloatArgument.IsCaptured() && test.m_ClampedFloatArgument.Get() == 0.5f, YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
test.m_OptionContext.Reset();
}
// Help text
test.m_OptionContext.Help();
#undef PREPARE_DATA
}
}
#include <gtest/gtest.h>
int main(int argc, char* argv[]) {
#if YYCC_VERCMP_NE(YYCC_VER_MAJOR, YYCC_VER_MINOR, YYCC_VER_PATCH, 1, 3 ,0)
#error "The YYCC library used when compiling is not match code expected, this may cause build error."
#error "If you trust it, please annotate these preprocessor statement, otherwise please contact developer."
#endif
// common testbench
// normal
YYCCTestbench::EncodingTestbench();
YYCCTestbench::StringTestbench();
YYCCTestbench::ParserTestbench();
YYCCTestbench::WinFctTestbench();
YYCCTestbench::StdPatchTestbench();
YYCCTestbench::EnumHelperTestbench();
YYCCTestbench::VersionMacroTestbench();
// advanced
YYCCTestbench::ConfigManagerTestbench();
YYCCTestbench::ArgParserTestbench(argc, argv);
// testbench which may terminal app or ordering input
YYCCTestbench::ConsoleTestbench();
YYCCTestbench::DialogTestbench();
YYCCTestbench::ExceptionTestbench();
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
+735
View File
@@ -0,0 +1,735 @@
#include <YYCCommonplace.hpp>
#include <cstdio>
#include <set>
#include <map>
namespace Console = YYCC::ConsoleHelper;
namespace YYCCTestbench {
#pragma region Unicode Test Data
// UNICODE Test Strings
// Ref: https://stackoverflow.com/questions/478201/how-to-test-an-application-for-correct-encoding-e-g-utf-8
#define TEST_UNICODE_STR_JAPAN "\u30E6\u30FC\u30B6\u30FC\u5225\u30B5\u30A4\u30C8"
#define TEST_UNICODE_STR_CHINA "\u7B80\u4F53\u4E2D\u6587"
#define TEST_UNICODE_STR_KOREA "\uD06C\uB85C\uC2A4 \uD50C\uB7AB\uD3FC\uC73C\uB85C"
#define TEST_UNICODE_STR_ISRAEL "\u05DE\u05D3\u05D5\u05E8\u05D9\u05DD \u05DE\u05D1\u05D5\u05E7\u05E9\u05D9\u05DD"
#define TEST_UNICODE_STR_EGYPT "\u0623\u0641\u0636\u0644 \u0627\u0644\u0628\u062D\u0648\u062B"
#define TEST_UNICODE_STR_GREECE "\u03A3\u1F72 \u03B3\u03BD\u03C9\u03C1\u03AF\u03B6\u03C9 \u1F00\u03C0\u1F78"
#define TEST_UNICODE_STR_RUSSIA "\u0414\u0435\u0441\u044F\u0442\u0443\u044E \u041C\u0435\u0436\u0434\u0443\u043D\u0430\u0440\u043E\u0434\u043D\u0443\u044E"
#define TEST_UNICODE_STR_THAILAND "\u0E41\u0E1C\u0E48\u0E19\u0E14\u0E34\u0E19\u0E2E\u0E31\u0E48\u0E19\u0E40\u0E2A\u0E37\u0E48\u0E2D\u0E21\u0E42\u0E17\u0E23\u0E21\u0E41\u0E2A\u0E19\u0E2A\u0E31\u0E07\u0E40\u0E27\u0E0A"
#define TEST_UNICODE_STR_FRANCE "fran\u00E7ais langue \u00E9trang\u00E8re"
#define TEST_UNICODE_STR_SPAIN "ma\u00F1ana ol\u00E9"
#define TEST_UNICODE_STR_MATHMATICS "\u222E E\u22C5da = Q, n \u2192 \u221E, \u2211 f(i) = \u220F g(i)"
#define TEST_UNICODE_STR_EMOJI "\U0001F363 \u2716 \U0001F37A" // sushi x beer mug
#define CONCAT(prefix, strl) prefix ## strl
#define CPP_U8_LITERAL(strl) YYCC_U8(strl)
#define CPP_U16_LITERAL(strl) CONCAT(u, strl)
#define CPP_U32_LITERAL(strl) CONCAT(U, strl)
#define CPP_WSTR_LITERAL(strl) CONCAT(L, strl)
static std::vector<YYCC::yycc_u8string> c_UTF8TestStrTable {
CPP_U8_LITERAL(TEST_UNICODE_STR_JAPAN),
CPP_U8_LITERAL(TEST_UNICODE_STR_CHINA),
CPP_U8_LITERAL(TEST_UNICODE_STR_KOREA),
CPP_U8_LITERAL(TEST_UNICODE_STR_ISRAEL),
CPP_U8_LITERAL(TEST_UNICODE_STR_EGYPT),
CPP_U8_LITERAL(TEST_UNICODE_STR_GREECE),
CPP_U8_LITERAL(TEST_UNICODE_STR_RUSSIA),
CPP_U8_LITERAL(TEST_UNICODE_STR_THAILAND),
CPP_U8_LITERAL(TEST_UNICODE_STR_FRANCE),
CPP_U8_LITERAL(TEST_UNICODE_STR_SPAIN),
CPP_U8_LITERAL(TEST_UNICODE_STR_MATHMATICS),
CPP_U8_LITERAL(TEST_UNICODE_STR_EMOJI),
};
static std::vector<std::wstring> c_WStrTestStrTable {
CPP_WSTR_LITERAL(TEST_UNICODE_STR_JAPAN),
CPP_WSTR_LITERAL(TEST_UNICODE_STR_CHINA),
CPP_WSTR_LITERAL(TEST_UNICODE_STR_KOREA),
CPP_WSTR_LITERAL(TEST_UNICODE_STR_ISRAEL),
CPP_WSTR_LITERAL(TEST_UNICODE_STR_EGYPT),
CPP_WSTR_LITERAL(TEST_UNICODE_STR_GREECE),
CPP_WSTR_LITERAL(TEST_UNICODE_STR_RUSSIA),
CPP_WSTR_LITERAL(TEST_UNICODE_STR_THAILAND),
CPP_WSTR_LITERAL(TEST_UNICODE_STR_FRANCE),
CPP_WSTR_LITERAL(TEST_UNICODE_STR_SPAIN),
CPP_WSTR_LITERAL(TEST_UNICODE_STR_MATHMATICS),
CPP_WSTR_LITERAL(TEST_UNICODE_STR_EMOJI),
};
static std::vector<std::u16string> c_UTF16TestStrTable {
CPP_U16_LITERAL(TEST_UNICODE_STR_JAPAN),
CPP_U16_LITERAL(TEST_UNICODE_STR_CHINA),
CPP_U16_LITERAL(TEST_UNICODE_STR_KOREA),
CPP_U16_LITERAL(TEST_UNICODE_STR_ISRAEL),
CPP_U16_LITERAL(TEST_UNICODE_STR_EGYPT),
CPP_U16_LITERAL(TEST_UNICODE_STR_GREECE),
CPP_U16_LITERAL(TEST_UNICODE_STR_RUSSIA),
CPP_U16_LITERAL(TEST_UNICODE_STR_THAILAND),
CPP_U16_LITERAL(TEST_UNICODE_STR_FRANCE),
CPP_U16_LITERAL(TEST_UNICODE_STR_SPAIN),
CPP_U16_LITERAL(TEST_UNICODE_STR_MATHMATICS),
CPP_U16_LITERAL(TEST_UNICODE_STR_EMOJI),
};
static std::vector<std::u32string> c_UTF32TestStrTable {
CPP_U32_LITERAL(TEST_UNICODE_STR_JAPAN),
CPP_U32_LITERAL(TEST_UNICODE_STR_CHINA),
CPP_U32_LITERAL(TEST_UNICODE_STR_KOREA),
CPP_U32_LITERAL(TEST_UNICODE_STR_ISRAEL),
CPP_U32_LITERAL(TEST_UNICODE_STR_EGYPT),
CPP_U32_LITERAL(TEST_UNICODE_STR_GREECE),
CPP_U32_LITERAL(TEST_UNICODE_STR_RUSSIA),
CPP_U32_LITERAL(TEST_UNICODE_STR_THAILAND),
CPP_U32_LITERAL(TEST_UNICODE_STR_FRANCE),
CPP_U32_LITERAL(TEST_UNICODE_STR_SPAIN),
CPP_U32_LITERAL(TEST_UNICODE_STR_MATHMATICS),
CPP_U32_LITERAL(TEST_UNICODE_STR_EMOJI),
};
#undef CPP_WSTR_LITERAL
#undef CPP_U32_LITERAL
#undef CPP_U16_LITERAL
#undef CPP_U8_LITERAL
#undef CONCAT
#pragma endregion
static void Assert(bool condition, const YYCC::yycc_char8_t* description) {
if (condition) {
Console::FormatLine(YYCC_U8(YYCC_COLOR_LIGHT_GREEN("OK: %s")), description);
} else {
Console::FormatLine(YYCC_U8(YYCC_COLOR_LIGHT_RED("Failed: %s\n")), description);
std::abort();
}
}
static void ConsoleTestbench() {
// Color Test
Console::EnableColorfulConsole();
Console::WriteLine(YYCC_U8("Color Test:"));
#define TEST_MACRO(col) Console::WriteLine(YYCC_U8("\t" YYCC_COLOR_ ## col ("\u2588\u2588") YYCC_COLOR_LIGHT_ ## col("\u2588\u2588") " " #col " / LIGHT " #col ));
// U+2588 is full block
TEST_MACRO(BLACK);
TEST_MACRO(RED);
TEST_MACRO(GREEN);
TEST_MACRO(YELLOW);
TEST_MACRO(BLUE);
TEST_MACRO(MAGENTA);
TEST_MACRO(CYAN);
TEST_MACRO(WHITE);
#undef TEST_MACRO
// UTF8 Output Test
Console::WriteLine(YYCC_U8("UTF8 Output Test:"));
for (const auto& strl : c_UTF8TestStrTable) {
Console::FormatLine(YYCC_U8("\t%s"), strl.c_str());
}
// UTF8 Input Test
Console::WriteLine(YYCC_U8("UTF8 Input Test:"));
for (const auto& strl : c_UTF8TestStrTable) {
Console::FormatLine(YYCC_U8("\tPlease type: %s"), strl.c_str());
Console::Write(YYCC_U8("\t> "));
YYCC::yycc_u8string gotten(Console::ReadLine());
if (gotten == strl) Console::FormatLine(YYCC_U8(YYCC_COLOR_LIGHT_GREEN("\tMatched! Got: %s")), gotten.c_str());
else Console::FormatLine(YYCC_U8(YYCC_COLOR_LIGHT_RED("\tNOT Matched! Got: %s")), gotten.c_str());
}
}
static void EncodingTestbench() {
// get test tuple size
size_t count = c_UTF8TestStrTable.size();
// check the convertion between given string
for (size_t i = 0u; i < count; ++i) {
// get item
const auto& u8str = c_UTF8TestStrTable[i];
const auto& u16str = c_UTF16TestStrTable[i];
const auto& u32str = c_UTF32TestStrTable[i];
// create cache variables
YYCC::yycc_u8string u8cache;
std::u16string u16cache;
std::u32string u32cache;
// do convertion check
Assert(YYCC::EncodingHelper::UTF8ToUTF16(u8str, u16cache) && u16cache == u16str, YYCC_U8("YYCC::EncodingHelper::UTF8ToUTF16"));
Assert(YYCC::EncodingHelper::UTF8ToUTF32(u8str, u32cache) && u32cache == u32str, YYCC_U8("YYCC::EncodingHelper::UTF8ToUTF32"));
Assert(YYCC::EncodingHelper::UTF16ToUTF8(u16str, u8cache) && u8cache == u8str, YYCC_U8("YYCC::EncodingHelper::UTF16ToUTF8"));
Assert(YYCC::EncodingHelper::UTF32ToUTF8(u32str, u8cache) && u8cache == u8str, YYCC_U8("YYCC::EncodingHelper::UTF32ToUTF8"));
}
// check wstring convertion on windows
#if YYCC_OS == YYCC_OS_WINDOWS
for (size_t i = 0u; i < count; ++i) {
// get item
const auto& u8str = c_UTF8TestStrTable[i];
const auto& wstr = c_WStrTestStrTable[i];
// create cache variables
YYCC::yycc_u8string u8cache;
std::wstring wcache;
// do convertion check
Assert(YYCC::EncodingHelper::UTF8ToWchar(u8str.c_str(), wcache) && wcache == wstr, YYCC_U8("YYCC::EncodingHelper::UTF8ToWchar"));
Assert(YYCC::EncodingHelper::WcharToUTF8(wstr.c_str(), u8cache) && u8cache == u8str, YYCC_U8("YYCC::EncodingHelper::WcharToUTF8"));
}
#endif
}
static void StringTestbench() {
// Test Printf
auto test_printf = YYCC::StringHelper::Printf(YYCC_U8("%s == %s"), YYCC_U8("Hello World"), YYCC_U8("Hello, world"));
Assert(test_printf == YYCC_U8("Hello World == Hello, world"), YYCC_U8("YYCC::StringHelper::Printf"));
// Test Replace
auto test_replace = YYCC::StringHelper::Replace(YYCC_U8("aabbcc"), YYCC_U8("bb"), YYCC_U8("dd")); // normal case
Assert(test_replace == YYCC_U8("aaddcc"), YYCC_U8("YYCC::StringHelper::Replace"));
test_replace = YYCC::StringHelper::Replace(YYCC_U8("aabbcc"), YYCC_U8("zz"), YYCC_U8("yy")); // no replace
Assert(test_replace == YYCC_U8("aabbcc"), YYCC_U8("YYCC::StringHelper::Replace"));
test_replace = YYCC::StringHelper::Replace(YYCC_U8("aabbcc"), YYCC::yycc_u8string_view(), YYCC_U8("zz")); // empty finding
Assert(test_replace == YYCC_U8("aabbcc"), YYCC_U8("YYCC::StringHelper::Replace"));
test_replace = YYCC::StringHelper::Replace(YYCC_U8("aaaabbaa"), YYCC_U8("aa"), YYCC_U8("")); // no replaced string
Assert(test_replace == YYCC_U8("bb"), YYCC_U8("YYCC::StringHelper::Replace"));
test_replace = YYCC::StringHelper::Replace(YYCC_U8("aaxcc"), YYCC_U8("x"), YYCC_U8("yx")); // nested replacing
Assert(test_replace == YYCC_U8("aayxcc"), YYCC_U8("YYCC::StringHelper::Replace"));
test_replace = YYCC::StringHelper::Replace(YYCC::yycc_u8string_view(), YYCC_U8(""), YYCC_U8("xy")); // empty source string
Assert(test_replace == YYCC_U8(""), YYCC_U8("YYCC::StringHelper::Replace"));
// Test Upper / Lower
auto test_lower = YYCC::StringHelper::Lower(YYCC_U8("LOWER"));
Assert(test_lower == YYCC_U8("lower"), YYCC_U8("YYCC::StringHelper::Lower"));
auto test_upper = YYCC::StringHelper::Upper(YYCC_U8("upper"));
Assert(test_upper == YYCC_U8("UPPER"), YYCC_U8("YYCC::StringHelper::Upper"));
// Test Join
std::vector<YYCC::yycc_u8string> test_join_container {
YYCC_U8(""), YYCC_U8("1"), YYCC_U8("2"), YYCC_U8("")
};
auto test_join = YYCC::StringHelper::Join(test_join_container.begin(), test_join_container.end(), YYCC_U8(", "));
Assert(test_join == YYCC_U8(", 1, 2, "), YYCC_U8("YYCC::StringHelper::Join"));
// Test Split
auto test_split = YYCC::StringHelper::Split(YYCC_U8(", 1, 2, "), YYCC_U8(", ")); // normal
Assert(test_split.size() == 4u, YYCC_U8("YYCC::StringHelper::Split"));
Assert(test_split[0] == YYCC_U8(""), YYCC_U8("YYCC::StringHelper::Split"));
Assert(test_split[1] == YYCC_U8("1"), YYCC_U8("YYCC::StringHelper::Split"));
Assert(test_split[2] == YYCC_U8("2"), YYCC_U8("YYCC::StringHelper::Split"));
Assert(test_split[3] == YYCC_U8(""), YYCC_U8("YYCC::StringHelper::Split"));
test_split = YYCC::StringHelper::Split(YYCC_U8("test"), YYCC_U8("-")); // no matched decilmer
Assert(test_split.size() == 1u, YYCC_U8("YYCC::StringHelper::Split"));
Assert(test_split[0] == YYCC_U8("test"), YYCC_U8("YYCC::StringHelper::Split"));
test_split = YYCC::StringHelper::Split(YYCC_U8("test"), YYCC::yycc_u8string_view()); // empty decilmer
Assert(test_split.size() == 1u, YYCC_U8("YYCC::StringHelper::Split"));
Assert(test_split[0] == YYCC_U8("test"), YYCC_U8("YYCC::StringHelper::Split"));
test_split = YYCC::StringHelper::Split(YYCC::yycc_u8string_view(), YYCC_U8("")); // empty source string
Assert(test_split.size() == 1u, YYCC_U8("YYCC::StringHelper::Split"));
Assert(test_split[0].empty(), YYCC_U8("YYCC::StringHelper::Split"));
}
static void ParserTestbench() {
// Test success TryParse
#define TEST_MACRO(type_t, value, string_value, ...) { \
YYCC::yycc_u8string cache_string(YYCC_U8(string_value)); \
type_t cache; \
Assert(YYCC::ParserHelper::TryParse<type_t>(cache_string, cache, ##__VA_ARGS__) && cache == value, YYCC_U8("YYCC::StringHelper::TryParse<" #type_t ">")); \
}
TEST_MACRO(int8_t, INT8_C(-61), "-61");
TEST_MACRO(uint8_t, UINT8_C(200), "200");
TEST_MACRO(int16_t, INT16_C(6161), "6161");
TEST_MACRO(uint16_t, UINT16_C(32800), "32800");
TEST_MACRO(int32_t, INT32_C(61616161), "61616161");
TEST_MACRO(uint32_t, UINT32_C(4294967293), "4294967293");
TEST_MACRO(int64_t, INT64_C(616161616161), "616161616161");
TEST_MACRO(uint64_t, UINT64_C(9223372036854775807), "9223372036854775807");
TEST_MACRO(uint32_t, UINT32_C(0xffff), "ffff", 16);
TEST_MACRO(float, 1.0f, "1.0");
TEST_MACRO(double, 1.0, "1.0");
TEST_MACRO(bool, true, "true");
#undef TEST_MACRO
// Test failed TryParse
#define TEST_MACRO(type_t, string_value, ...) { \
YYCC::yycc_u8string cache_string(YYCC_U8(string_value)); \
type_t cache; \
Assert(!YYCC::ParserHelper::TryParse<type_t>(cache_string, cache, ##__VA_ARGS__), YYCC_U8("YYCC::StringHelper::TryParse<" #type_t ">")); \
}
TEST_MACRO(int8_t, "6161");
TEST_MACRO(uint8_t, "32800");
TEST_MACRO(int16_t, "61616161");
TEST_MACRO(uint16_t, "4294967293");
TEST_MACRO(int32_t, "616161616161");
TEST_MACRO(uint32_t, "9223372036854775807");
TEST_MACRO(int64_t, "616161616161616161616161");
TEST_MACRO(uint64_t, "92233720368547758079223372036854775807");
TEST_MACRO(float, "1e40");
TEST_MACRO(double, "1e114514");
TEST_MACRO(bool, "hello, world!");
#undef TEST_MACRO
// Test ToString
#define TEST_MACRO(type_t, value, string_value, ...) { \
type_t cache = value; \
YYCC::yycc_u8string ret(YYCC::ParserHelper::ToString<type_t>(cache, ##__VA_ARGS__)); \
Assert(ret == YYCC_U8(string_value), YYCC_U8("YYCC::StringHelper::ToString<" #type_t ">")); \
}
TEST_MACRO(int8_t, INT8_C(-61), "-61");
TEST_MACRO(uint8_t, UINT8_C(200), "200");
TEST_MACRO(int16_t, INT16_C(6161), "6161");
TEST_MACRO(uint16_t, UINT16_C(32800), "32800");
TEST_MACRO(int32_t, INT32_C(61616161), "61616161");
TEST_MACRO(uint32_t, UINT32_C(4294967293), "4294967293");
TEST_MACRO(int64_t, INT64_C(616161616161), "616161616161");
TEST_MACRO(uint64_t, UINT64_C(9223372036854775807), "9223372036854775807");
TEST_MACRO(uint32_t, UINT32_C(0xffff), "ffff", 16);
TEST_MACRO(float, 1.0f, "1.0", std::chars_format::fixed, 1);
TEST_MACRO(double, 1.0, "1.0", std::chars_format::fixed, 1);
TEST_MACRO(bool, true, "true");
#undef TEST_MACRO
}
static void DialogTestbench() {
#if YYCC_OS == YYCC_OS_WINDOWS
YYCC::yycc_u8string ret;
std::vector<YYCC::yycc_u8string> rets;
YYCC::DialogHelper::FileDialog params;
auto& filters = params.ConfigreFileTypes();
filters.Add(YYCC_U8("Microsoft Word (*.docx; *.doc)"), { YYCC_U8("*.docx"), YYCC_U8("*.doc") });
filters.Add(YYCC_U8("Microsoft Excel (*.xlsx; *.xls)"), { YYCC_U8("*.xlsx"), YYCC_U8("*.xls") });
filters.Add(YYCC_U8("Microsoft PowerPoint (*.pptx; *.ppt)"), { YYCC_U8("*.pptx"), YYCC_U8("*.ppt") });
filters.Add(YYCC_U8("Text File (*.txt)"), { YYCC_U8("*.txt") });
filters.Add(YYCC_U8("All Files (*.*)"), { YYCC_U8("*.*") });
params.SetDefaultFileTypeIndex(0u);
if (YYCC::DialogHelper::OpenFileDialog(params, ret)) {
Console::FormatLine(YYCC_U8("Open File: %s"), ret.c_str());
}
if (YYCC::DialogHelper::OpenMultipleFileDialog(params, rets)) {
Console::WriteLine(YYCC_U8("Open Multiple Files:"));
for (const auto& item : rets) {
Console::FormatLine(YYCC_U8("\t%s"), item.c_str());
}
}
if (YYCC::DialogHelper::SaveFileDialog(params, ret)) {
Console::FormatLine(YYCC_U8("Save File: %s"), ret.c_str());
}
params.Clear();
if (YYCC::DialogHelper::OpenFolderDialog(params, ret)) {
Console::FormatLine(YYCC_U8("Open Folder: %s"), ret.c_str());
}
#endif
}
static void ExceptionTestbench() {
#if YYCC_OS == YYCC_OS_WINDOWS
YYCC::ExceptionHelper::Register([](const YYCC::yycc_u8string& log_path, const YYCC::yycc_u8string& coredump_path) -> void {
MessageBoxW(
NULL,
YYCC::EncodingHelper::UTF8ToWchar(
YYCC::StringHelper::Printf(YYCC_U8("Log generated:\nLog path: %s\nCore dump path: %s"), log_path.c_str(), coredump_path.c_str())
).c_str(),
L"Fatal Error", MB_OK + MB_ICONERROR
);
}
);
// Perform a div zero exception.
#if defined (YYCC_DEBUG_UE_FILTER)
// Reference: https://stackoverflow.com/questions/20981982/is-it-possible-to-debug-unhandledexceptionfilters-with-a-debugger
__try {
// all of code normally inside of main or WinMain here...
int i = 1, j = 0;
int k = i / j;
} __except (YYCC::ExceptionHelper::DebugCallUExceptionImpl(GetExceptionInformation())) {
OutputDebugStringW(L"executed filter function\n");
}
#else
int i = 1, j = 0;
int k = i / j;
#endif
YYCC::ExceptionHelper::Unregister();
#endif
}
static void WinFctTestbench() {
#if YYCC_OS == YYCC_OS_WINDOWS
HMODULE test_current_module;
Assert((test_current_module = YYCC::WinFctHelper::GetCurrentModule()) != nullptr, YYCC_U8("YYCC::WinFctHelper::GetCurrentModule"));
Console::FormatLine(YYCC_U8("Current Module HANDLE: 0x%" PRI_XPTR_LEFT_PADDING PRIXPTR), test_current_module);
YYCC::yycc_u8string test_temp;
Assert(YYCC::WinFctHelper::GetTempDirectory(test_temp), YYCC_U8("YYCC::WinFctHelper::GetTempDirectory"));
Console::FormatLine(YYCC_U8("Temp Directory: %s"), test_temp.c_str());
YYCC::yycc_u8string test_module_name;
Assert(YYCC::WinFctHelper::GetModuleFileName(YYCC::WinFctHelper::GetCurrentModule(), test_module_name), YYCC_U8("YYCC::WinFctHelper::GetModuleFileName"));
Console::FormatLine(YYCC_U8("Current Module File Name: %s"), test_module_name.c_str());
YYCC::yycc_u8string test_localappdata_path;
Assert(YYCC::WinFctHelper::GetLocalAppData(test_localappdata_path), YYCC_U8("YYCC::WinFctHelper::GetLocalAppData"));
Console::FormatLine(YYCC_U8("Local AppData: %s"), test_localappdata_path.c_str());
Assert(YYCC::WinFctHelper::IsValidCodePage(static_cast<UINT>(1252)), YYCC_U8("YYCC::WinFctHelper::IsValidCodePage"));
Assert(!YYCC::WinFctHelper::IsValidCodePage(static_cast<UINT>(114514)), YYCC_U8("YYCC::WinFctHelper::IsValidCodePage"));
// MARK: There is no testbench for MoveFile, CopyFile DeleteFile.
// Because they can operate file system files.
// And may cause test environment entering unstable status.
#endif
}
static void StdPatchTestbench() {
// Std Path
std::filesystem::path test_path;
for (const auto& strl : c_UTF8TestStrTable) {
test_path /= YYCC::StdPatch::ToStdPath(strl);
}
YYCC::yycc_u8string test_slashed_path(YYCC::StdPatch::ToUTF8Path(test_path));
#if YYCC_OS == YYCC_OS_WINDOWS
std::wstring wdecilmer(1u, std::filesystem::path::preferred_separator);
YYCC::yycc_u8string decilmer(YYCC::EncodingHelper::WcharToUTF8(wdecilmer));
#else
YYCC::yycc_u8string decilmer(1u, std::filesystem::path::preferred_separator);
#endif
YYCC::yycc_u8string test_joined_path(YYCC::StringHelper::Join(c_UTF8TestStrTable.begin(), c_UTF8TestStrTable.end(), decilmer));
Assert(test_slashed_path == test_joined_path, YYCC_U8("YYCC::StdPatch::ToStdPath, YYCC::StdPatch::ToUTF8Path"));
// StartsWith, EndsWith
YYCC::yycc_u8string test_starts_ends_with(YYCC_U8("aaabbbccc"));
Assert(YYCC::StdPatch::StartsWith(test_starts_ends_with, YYCC_U8("aaa")), YYCC_U8("YYCC::StdPatch::StartsWith"));
Assert(!YYCC::StdPatch::StartsWith(test_starts_ends_with, YYCC_U8("ccc")), YYCC_U8("YYCC::StdPatch::StartsWith"));
Assert(!YYCC::StdPatch::EndsWith(test_starts_ends_with, YYCC_U8("aaa")), YYCC_U8("YYCC::StdPatch::EndsWith"));
Assert(YYCC::StdPatch::EndsWith(test_starts_ends_with, YYCC_U8("ccc")), YYCC_U8("YYCC::StdPatch::EndsWith"));
// Contains
std::set<int> test_set { 1, 2, 3, 4, 6, 7 };
Assert(YYCC::StdPatch::Contains(test_set, static_cast<int>(1)), YYCC_U8("YYCC::StdPatch::Contains"));
Assert(!YYCC::StdPatch::Contains(test_set, static_cast<int>(5)), YYCC_U8("YYCC::StdPatch::Contains"));
std::map<int, float> test_map { { 1, 1.0f }, { 4, 4.0f } };
Assert(YYCC::StdPatch::Contains(test_map, static_cast<int>(1)), YYCC_U8("YYCC::StdPatch::Contains"));
Assert(!YYCC::StdPatch::Contains(test_map, static_cast<int>(5)), YYCC_U8("YYCC::StdPatch::Contains"));
}
enum class TestFlagEnum : uint8_t {
Test1 = 0b00000000,
Test2 = 0b00000001,
Test3 = 0b00000010,
Test4 = 0b00000100,
Test5 = 0b00001000,
Test6 = 0b00010000,
Test7 = 0b00100000,
Test8 = 0b01000000,
Test9 = 0b10000000,
Inverted = 0b01111111,
Merged = Test3 + Test5,
};
static void EnumHelperTestbench() {
TestFlagEnum val;
Assert(YYCC::EnumHelper::Merge(TestFlagEnum::Test3, TestFlagEnum::Test5) == TestFlagEnum::Merged, YYCC_U8("YYCC::EnumHelper::Merge"));
Assert(YYCC::EnumHelper::Invert(TestFlagEnum::Test9) == TestFlagEnum::Inverted, YYCC_U8("YYCC::EnumHelper::Invert"));
val = YYCC::EnumHelper::Merge(TestFlagEnum::Test3, TestFlagEnum::Test5);
YYCC::EnumHelper::Mask(val, TestFlagEnum::Test3);
Assert(YYCC::EnumHelper::Bool(val), YYCC_U8("YYCC::EnumHelper::Mask"));
val = YYCC::EnumHelper::Merge(TestFlagEnum::Test3, TestFlagEnum::Test5);
YYCC::EnumHelper::Mask(val, TestFlagEnum::Test4);
Assert(!YYCC::EnumHelper::Bool(val), YYCC_U8("YYCC::EnumHelper::Mask"));
val = TestFlagEnum::Test3;
YYCC::EnumHelper::Add(val, TestFlagEnum::Test5);
Assert(val == TestFlagEnum::Merged, YYCC_U8("YYCC::EnumHelper::Add"));
val = TestFlagEnum::Merged;
YYCC::EnumHelper::Remove(val, TestFlagEnum::Test5);
Assert(val == TestFlagEnum::Test3, YYCC_U8("YYCC::EnumHelper::Remove"));
val = YYCC::EnumHelper::Merge(TestFlagEnum::Test3, TestFlagEnum::Test5);
Assert(YYCC::EnumHelper::Has(val, TestFlagEnum::Test3), YYCC_U8("YYCC::EnumHelper::Has"));
Assert(!YYCC::EnumHelper::Has(val, TestFlagEnum::Test4), YYCC_U8("YYCC::EnumHelper::Has"));
Assert(!YYCC::EnumHelper::Bool(TestFlagEnum::Test1), YYCC_U8("YYCC::EnumHelper::Bool"));
Assert(YYCC::EnumHelper::Bool(TestFlagEnum::Test2), YYCC_U8("YYCC::EnumHelper::Bool"));
}
static void VersionMacroTestbench() {
Assert(YYCC_VERCMP_E(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_E"));
Assert(!YYCC_VERCMP_NE(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_NE"));
Assert(YYCC_VERCMP_G(1, 2, 3, 0, 2, 5), YYCC_U8("YYCC_VERCMP_G"));
Assert(YYCC_VERCMP_GE(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_GE"));
Assert(YYCC_VERCMP_NL(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_NL"));
Assert(YYCC_VERCMP_L(0, 2, 5, 1, 2, 3), YYCC_U8("YYCC_VERCMP_L"));
Assert(YYCC_VERCMP_LE(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_LE"));
Assert(YYCC_VERCMP_NG(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_NG"));
}
enum class TestEnum : int8_t {
Test1, Test2, Test3
};
class TestConfigManager {
public:
TestConfigManager() :
m_IntSetting(YYCC_U8("int-setting"), INT32_C(0)),
m_FloatSetting(YYCC_U8("float-setting"), 0.0f),
m_StringSetting(YYCC_U8("string-setting"), YYCC_U8("")),
m_BoolSetting(YYCC_U8("bool-setting"), false),
m_ClampedFloatSetting(YYCC_U8("clamped-float-setting"), 0.0f, YYCC::Constraints::GetMinMaxRangeConstraint<float>(-1.0f, 1.0f)),
m_EnumSetting(YYCC_U8("enum-setting"), TestEnum::Test1),
m_CoreManager(YYCC_U8("test.cfg"), UINT64_C(0), {
&m_IntSetting, &m_FloatSetting, &m_StringSetting, &m_BoolSetting, &m_ClampedFloatSetting, &m_EnumSetting
}) {}
~TestConfigManager() {}
void PrintSettings() {
Console::WriteLine(YYCC_U8("Config Manager Settings:"));
Console::FormatLine(YYCC_U8("\tint-setting: %" PRIi32), m_IntSetting.Get());
Console::FormatLine(YYCC_U8("\tfloat-setting: %f"), m_FloatSetting.Get());
Console::FormatLine(YYCC_U8("\tstring-setting: %s"), m_StringSetting.Get().c_str());
Console::FormatLine(YYCC_U8("\tbool-setting: %s"), m_BoolSetting.Get() ? YYCC_U8("true") : YYCC_U8("false"));
Console::FormatLine(YYCC_U8("\tfloat-setting: %f"), m_ClampedFloatSetting.Get());
Console::FormatLine(YYCC_U8("\tenum-setting: %" PRIi8), static_cast<std::underlying_type_t<TestEnum>>(m_EnumSetting.Get()));
}
YYCC::ConfigManager::NumberSetting<int32_t> m_IntSetting;
YYCC::ConfigManager::NumberSetting<float> m_FloatSetting;
YYCC::ConfigManager::StringSetting m_StringSetting;
YYCC::ConfigManager::NumberSetting<bool> m_BoolSetting;
YYCC::ConfigManager::NumberSetting<float> m_ClampedFloatSetting;
YYCC::ConfigManager::NumberSetting<TestEnum> m_EnumSetting;
YYCC::ConfigManager::CoreManager m_CoreManager;
};
static void ConfigManagerTestbench() {
// init cfg manager
TestConfigManager test;
// test constraint works
Assert(!test.m_ClampedFloatSetting.Set(2.0f), YYCC_U8("YYCC::Constraints::Constraint"));
Assert(test.m_ClampedFloatSetting.Get() == 0.0f, YYCC_U8("YYCC::Constraints::Constraint"));
// test modify settings
#define TEST_MACRO(member_name, set_val) { \
Assert(test.member_name.Set(set_val), YYCC_U8("YYCC::ConfigManager::AbstractSetting::Set")); \
Assert(test.member_name.Get() == set_val, YYCC_U8("YYCC::ConfigManager::AbstractSetting::Set")); \
}
TEST_MACRO(m_IntSetting, INT32_C(114));
TEST_MACRO(m_FloatSetting, 2.0f);
TEST_MACRO(m_StringSetting, YYCC_U8("fuck"));
TEST_MACRO(m_BoolSetting, true);
TEST_MACRO(m_ClampedFloatSetting, 0.5f);
TEST_MACRO(m_EnumSetting, TestEnum::Test2);
#undef TEST_MACRO
// test save
test.PrintSettings();
Assert(test.m_CoreManager.Save(), YYCC_U8("YYCC::ConfigManager::CoreManager::Save"));
// test reset
test.m_CoreManager.Reset();
test.PrintSettings();
Assert(test.m_IntSetting.Get() == INT32_C(0), YYCC_U8("YYCC::ConfigManager::CoreManager::Reset"));
Assert(test.m_FloatSetting.Get() == 0.0f, YYCC_U8("YYCC::ConfigManager::CoreManager::Reset"));
Assert(test.m_StringSetting.Get() == YYCC_U8(""), YYCC_U8("YYCC::ConfigManager::CoreManager::Reset"));
Assert(test.m_BoolSetting.Get() == false, YYCC_U8("YYCC::ConfigManager::CoreManager::Reset"));
Assert(test.m_ClampedFloatSetting.Get() == 0.0f, YYCC_U8("YYCC::ConfigManager::CoreManager::Reset"));
Assert(test.m_EnumSetting.Get() == TestEnum::Test1, YYCC_U8("YYCC::ConfigManager::CoreManager::Reset"));
// test load
YYCC::ConfigManager::ConfigLoadResult wrong_result = YYCC::EnumHelper::Merge(
YYCC::ConfigManager::ConfigLoadResult::ItemError,
YYCC::ConfigManager::ConfigLoadResult::BrokenFile
);
Assert(!YYCC::EnumHelper::Has(test.m_CoreManager.Load(), wrong_result), YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
test.PrintSettings();
Assert(test.m_IntSetting.Get() == INT32_C(114), YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
Assert(test.m_FloatSetting.Get() == 2.0f, YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
Assert(test.m_StringSetting.Get() == YYCC_U8("fuck"), YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
Assert(test.m_BoolSetting.Get() == true, YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
Assert(test.m_ClampedFloatSetting.Get() == 0.5f, YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
Assert(test.m_EnumSetting.Get() == TestEnum::Test2, YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
}
class TestArgParser {
public:
TestArgParser() :
m_IntArgument(YYCC_U8("int"), YYCC_U8_CHAR('i'), YYCC_U8("integral argument"), YYCC_U8("114514")),
m_FloatArgument(nullptr, YYCC_U8_CHAR('f'), nullptr, nullptr, true),
m_StringArgument(YYCC_U8("string"), YYCC::ArgParser::AbstractArgument::NO_SHORT_NAME, nullptr, nullptr, true),
m_BoolArgument(nullptr, YYCC_U8_CHAR('b'), nullptr),
m_ClampedFloatArgument(YYCC_U8("clamped-float"), YYCC::ArgParser::AbstractArgument::NO_SHORT_NAME, nullptr, nullptr, true, YYCC::Constraints::GetMinMaxRangeConstraint<float>(-1.0f, 1.0f)),
m_OptionContext(YYCC_U8("TestArgParser"), YYCC_U8("This is the testbench of argument parser."), {
&m_IntArgument, &m_FloatArgument, &m_StringArgument,
&m_BoolArgument, &m_ClampedFloatArgument
}) {}
~TestArgParser() {}
YYCC::ArgParser::NumberArgument<int32_t> m_IntArgument;
YYCC::ArgParser::NumberArgument<float> m_FloatArgument;
YYCC::ArgParser::StringArgument m_StringArgument;
YYCC::ArgParser::SwitchArgument m_BoolArgument;
YYCC::ArgParser::NumberArgument<float> m_ClampedFloatArgument;
YYCC::ArgParser::OptionContext m_OptionContext;
};
static void ArgParserTestbench(int argc, char* argv[]) {
// test command line getter
{
YYCC::ConsoleHelper::WriteLine(YYCC_U8("YYCC::ArgParser::ArgumentList::CreateFromStd"));
auto result = YYCC::ArgParser::ArgumentList::CreateFromStd(argc, argv);
for (result.Reset(); !result.IsEOF(); result.Next()) {
YYCC::ConsoleHelper::FormatLine(YYCC_U8("\t%s"), result.Argument().c_str());
}
}
#if YYCC_OS == YYCC_OS_WINDOWS
{
YYCC::ConsoleHelper::WriteLine(YYCC_U8("YYCC::ArgParser::ArgumentList::CreateFromWin32"));
auto result = YYCC::ArgParser::ArgumentList::CreateFromWin32();
for (result.Reset(); !result.IsEOF(); result.Next()) {
YYCC::ConsoleHelper::FormatLine(YYCC_U8("\t%s"), result.Argument().c_str());
}
}
#endif
// test option context
// init option context
TestArgParser test;
#define PREPARE_DATA(...) const char* test_argv[] = { __VA_ARGS__ }; \
auto al = YYCC::ArgParser::ArgumentList::CreateFromStd(sizeof(test_argv) / sizeof(char*), const_cast<char**>(test_argv));
// normal test
{
PREPARE_DATA("exec", "-i", "114514");
Assert(test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
Assert(test.m_IntArgument.IsCaptured() && test.m_IntArgument.Get() == UINT32_C(114514), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
Assert(!test.m_BoolArgument.IsCaptured(), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
Assert(!test.m_FloatArgument.IsCaptured(), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
Assert(!test.m_StringArgument.IsCaptured(), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
Assert(!test.m_BoolArgument.IsCaptured(), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
Assert(!test.m_ClampedFloatArgument.IsCaptured(), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
test.m_OptionContext.Reset();
}
// no argument
{
PREPARE_DATA("exec");
Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
test.m_OptionContext.Reset();
}
// error argument
{
PREPARE_DATA("exec", "-?", "114514");
Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
test.m_OptionContext.Reset();
}
// lost argument
{
PREPARE_DATA("exec", "-i");
Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
test.m_OptionContext.Reset();
}
// dplicated assign
{
PREPARE_DATA("exec", "-i", "114514" "--int", "114514");
Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
test.m_OptionContext.Reset();
}
// extra useless argument
{
PREPARE_DATA("exec", "-i", "114514" "1919810");
Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
test.m_OptionContext.Reset();
}
// invalid clamp argument
{
PREPARE_DATA("exec", "-i", "114514", "--clamped-float", "114.0");
Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
test.m_OptionContext.Reset();
}
// full argument
{
PREPARE_DATA("exec", "-i", "114514", "-f", "2.0", "--string", "fuck", "-b", "--clamped-float", "0.5");
Assert(test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
Assert(test.m_IntArgument.IsCaptured() && test.m_IntArgument.Get() == UINT32_C(114514), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
Assert(test.m_FloatArgument.IsCaptured() && test.m_FloatArgument.Get() == 2.0f, YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
Assert(test.m_StringArgument.IsCaptured() && test.m_StringArgument.Get() == YYCC_U8("fuck"), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
Assert(test.m_BoolArgument.IsCaptured(), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
Assert(test.m_ClampedFloatArgument.IsCaptured() && test.m_ClampedFloatArgument.Get() == 0.5f, YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
test.m_OptionContext.Reset();
}
// Help text
test.m_OptionContext.Help();
#undef PREPARE_DATA
}
}
int main(int argc, char* argv[]) {
#if YYCC_VERCMP_NE(YYCC_VER_MAJOR, YYCC_VER_MINOR, YYCC_VER_PATCH, 1, 3 ,0)
#error "The YYCC library used when compiling is not match code expected, this may cause build error."
#error "If you trust it, please annotate these preprocessor statement, otherwise please contact developer."
#endif
// common testbench
// normal
YYCCTestbench::EncodingTestbench();
YYCCTestbench::StringTestbench();
YYCCTestbench::ParserTestbench();
YYCCTestbench::WinFctTestbench();
YYCCTestbench::StdPatchTestbench();
YYCCTestbench::EnumHelperTestbench();
YYCCTestbench::VersionMacroTestbench();
// advanced
YYCCTestbench::ConfigManagerTestbench();
YYCCTestbench::ArgParserTestbench(argc, argv);
// testbench which may terminal app or ordering input
YYCCTestbench::ConsoleTestbench();
YYCCTestbench::DialogTestbench();
YYCCTestbench::ExceptionTestbench();
}
+49
View File
@@ -0,0 +1,49 @@
#include <gtest/gtest.h>
#include <yycc.hpp>
#include <yycc/constraint.hpp>
#include <yycc/prelude/rust.hpp>
#define CONSTRAINT ::yycc::constraint::Constraint
namespace yycctest::constraint {
template<typename T>
bool check(const T& value) {
return false;
}
template<typename T>
T clamp(const T& value) {
return value;
}
TEST(Constraint, Normal) {
CONSTRAINT<u32> instance(check<u32>, clamp<u32>);
EXPECT_TRUE(instance.support_check());
EXPECT_TRUE(instance.support_clamp());
EXPECT_FALSE(instance.check(0));
EXPECT_EQ(instance.clamp(0), 0);
}
TEST(Constraint, SomeNone) {
{
CONSTRAINT<u32> instance(check<u32>, nullptr);
EXPECT_TRUE(instance.support_check());
EXPECT_FALSE(instance.support_clamp());
EXPECT_FALSE(instance.check(0));
}
{
CONSTRAINT<u32> instance(nullptr, clamp<u32>);
EXPECT_FALSE(instance.support_check());
EXPECT_TRUE(instance.support_clamp());
EXPECT_EQ(instance.clamp(0), 0);
}
}
TEST(Constraint, AllNone) {
CONSTRAINT<u32> instance(nullptr, nullptr);
EXPECT_FALSE(instance.support_check());
EXPECT_FALSE(instance.support_clamp());
}
}
+5
View File
@@ -0,0 +1,5 @@
#include <gtest/gtest.h>
namespace yycctest::constraint::builder {
}
+2
View File
@@ -0,0 +1,2 @@
namespace yycctest::string {}
+37
View File
@@ -0,0 +1,37 @@
#include <gtest/gtest.h>
#include <yycc.hpp>
#include <yycc/string/op.hpp>
#include <yycc/string/reinterpret.hpp>
#define OP ::yycc::string::op
namespace yycctest::string::op {
TEST(StringOp, Printf) {
auto rv = OP::printf(YYCC_U8("%s == %s"), YYCC_U8("Hello World"), YYCC_U8("Hello, world"));
EXPECT_EQ(rv, YYCC_U8("Hello World == Hello, world"));
}
TEST(StringOp, Replace) {
}
TEST(StringOp, Lower) {
auto rv = OP::lower(YYCC_U8("LOWER"));
EXPECT_EQ(rv, YYCC_U8("lower"));
}
TEST(StringOp, Upper) {
auto rv = OP::upper(YYCC_U8("upper"));
EXPECT_EQ(rv, YYCC_U8("UPPER"));
}
TEST(StringOp, Join) {
}
TEST(StringOp, Split) {
}
}
+6
View File
@@ -0,0 +1,6 @@
#include <gtest/gtest.h>
#include <yycc.hpp>
namespace yycctest::string::reinterpret {
}