Files
UnrealEngine/Engine/Source/Programs/AutoRTFMTests/Private/CString.cpp
2025-05-18 13:04:45 +08:00

1774 lines
40 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "AutoRTFM.h"
#include "AutoRTFMTesting.h"
#include "Catch2Includes.h"
#include "CoreGlobals.h"
#include "Logging/LogVerbosity.h"
#include "Misc/FeedbackContext.h"
#include <catch_amalgamated.hpp>
#include <charconv>
#include <cstring>
#include <system_error>
#ifdef __clang__
#define CLANG_BEGIN_DISABLE_OPTIMIZATIONS _Pragma("clang optimize off")
#define CLANG_END_DISABLE_OPTIMIZATIONS _Pragma("clang optimize on")
#define CLANG_BEGIN_DISABLE_WARN_FORMAT_TRUNCATED \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wunknown-warning-option\"") \
_Pragma("clang diagnostic ignored \"-Wformat-truncation\"")
#define CLANG_END_DISABLE_WARN_FORMAT_TRUNCATED \
_Pragma("clang diagnostic pop")
#else
#define CLANG_BEGIN_DISABLE_OPTIMIZATIONS
#define CLANG_END_DISABLE_OPTIMIZATIONS
#define CLANG_BEGIN_DISABLE_WARN_FORMAT_TRUNCATED
#define CLANG_END_DISABLE_WARN_FORMAT_TRUNCATED
#endif
#ifdef _MSC_VER
#define MSVC_BEGIN_DISABLE_WARN_UNSAFE_FUNCTION \
__pragma(warning(push)) \
__pragma(warning(disable : 4996))
#define MSVC_END_DISABLE_UNSAFE_FN __pragma(warning(pop))
#else
#define MSVC_BEGIN_DISABLE_WARN_UNSAFE_FUNCTION
#define MSVC_END_DISABLE_UNSAFE_FN
#endif
namespace
{
// The expected AutoRTFM warning message when attempting to printf() using a
// format string containing a '%n' format specifier.
static constexpr auto* kPercentNWarning = TEXT("AutoRTFM does not support format strings containing '%n'");
// CheckWCharFunction calls Function with WChar outside a transaction, in an
// aborted transaction and committed transaction. The value returned when called
// in the committed transaction is expected to match the value returned when
// called outside the transaction.
// Function must be the deterministic function with the signature 'int(wchar_t)'.
template<typename FUNCTION>
void CheckWCharFunction(FUNCTION&& Function, wchar_t WChar)
{
int Expect = Function(WChar);
SECTION("With Abort")
{
int Got = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Got = Function(WChar);
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(0 == Got);
}
SECTION("With Commit")
{
int Got = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { Got = Function(WChar); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(Expect == Got);
}
}
// CheckStringToNumberFunction calls Function with String outside a transaction,
// in an aborted transaction and committed transaction, once with a valid
// end pointer and again with a null end pointer. The value returned and the
// assigned end-pointer when called in the committed transaction is expected to
// match the values when called outside the transaction.
// Function must be the deterministic function with the signature
// 'RESULT(const CHAR* String, CHAR** EndPtr, ADDITIONAL_ARGS...)'.
template<typename RESULT, typename CHAR, typename ... ADDITIONAL_ARGS>
void CheckStringToNumberFunction(RESULT Function(const CHAR*, CHAR**, ADDITIONAL_ARGS...), const CHAR* String, ADDITIONAL_ARGS... AdditionalArgs)
{
CHAR* ExpectEndPtr = nullptr;
RESULT ExpectValue = Function(String, &ExpectEndPtr, AdditionalArgs...);
CHAR* GotEndPtr = nullptr;
RESULT GotValue = 0;
SECTION("With EndPtr")
{
SECTION("With Abort")
{
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
GotValue = Function(String, &GotEndPtr, AdditionalArgs...);
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(0 == GotValue);
REQUIRE(nullptr == GotEndPtr);
}
SECTION("With Commit")
{
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&]
{
GotValue = Function(String, &GotEndPtr, AdditionalArgs...);
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(ExpectValue == GotValue);
REQUIRE(ExpectEndPtr == GotEndPtr);
}
}
SECTION("Null EndPtr")
{
SECTION("With Abort")
{
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
GotValue = Function(String, nullptr, AdditionalArgs...);
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(0 == GotValue);
}
SECTION("With Commit")
{
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { GotValue = Function(String, nullptr, AdditionalArgs...); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(ExpectValue == GotValue);
}
}
}
// CheckStdToChars calls std::to_chars with (char*, char*, ARGS...) outside a
// transaction, in an aborted transaction and committed transaction.
// The value returned and the string buffer content is expected to match the
// values when called outside the transaction.
template<typename ... ARGS>
void CheckStdToChars(ARGS ... Args)
{
static constexpr size_t BufferOffset = 2;
static constexpr size_t BufferSize = 32;
char ExpectedBuffer[]
{
'#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#',
'#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '\0',
};
static_assert(sizeof(ExpectedBuffer) == BufferSize);
const std::to_chars_result ExpectedResult = std::to_chars(&ExpectedBuffer[BufferOffset], &ExpectedBuffer[BufferSize-1], Args...);
char Buffer[]
{
'#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#',
'#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '\0',
};
static_assert(sizeof(Buffer) == BufferSize);
SECTION("With Abort")
{
AutoRTFM::Testing::Abort([&]
{
std::to_chars_result Result = std::to_chars(&Buffer[BufferOffset], &Buffer[BufferSize-1], Args...);
REQUIRE(Result.ec == ExpectedResult.ec);
REQUIRE(Result.ptr - Buffer == ExpectedResult.ptr - ExpectedBuffer);
REQUIRE(std::string_view(Buffer) == std::string_view(ExpectedBuffer));
AutoRTFM::AbortTransaction();
});
REQUIRE(std::string_view(Buffer) == "###############################");
}
SECTION("With Commit")
{
AutoRTFM::Testing::Commit([&]
{
std::to_chars_result Result = std::to_chars(&Buffer[BufferOffset], &Buffer[BufferSize-1], Args...);
REQUIRE(Result.ec == ExpectedResult.ec);
REQUIRE(Result.ptr - Buffer == ExpectedResult.ptr - ExpectedBuffer);
REQUIRE(std::string_view(Buffer) == std::string_view(ExpectedBuffer));
});
REQUIRE(std::string_view(Buffer) == std::string_view(ExpectedBuffer));
}
}
// A helper class that for the lifetime of the object, intercepts and records
// UE_LOG warnings.
class FCaptureWarningContext : private FFeedbackContext
{
public:
FCaptureWarningContext() : OldContext(GWarn) { GWarn = this; }
~FCaptureWarningContext() { GWarn = OldContext; }
void Serialize(const TCHAR* V, ELogVerbosity::Type Verbosity, const FName& Category) override
{
if (Verbosity == ELogVerbosity::Warning)
{
Warnings.push_back(V);
}
else
{
OldContext->Serialize(V, Verbosity, Category);
}
}
void Serialize(const TCHAR* V, ELogVerbosity::Type Verbosity, const FName& Category, double Time) override
{
if (Verbosity == ELogVerbosity::Warning)
{
Warnings.push_back(V);
}
else
{
OldContext->Serialize(V, Verbosity, Category, Time);
}
}
const std::vector<FString>& GetWarnings() const
{
return Warnings;
}
private:
FCaptureWarningContext(const FCaptureWarningContext&) = delete;
FCaptureWarningContext& operator = (const FCaptureWarningContext&) = delete;
FFeedbackContext* OldContext = nullptr;
std::vector<FString> Warnings;
};
} // anonymous namespace
TEST_CASE("CString.memcpy")
{
const char* const From = "Kittie says meow";
char To[] = "Doggie says woof";
SECTION("Direct")
{
SECTION("With Abort")
{
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
memcpy(To, From, 6);
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE("Doggie says woof" == std::string_view(To));
}
SECTION("With Commit")
{
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { memcpy(To, From, 6); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE("Kittie says woof" == std::string_view(To));
}
}
SECTION("Indirect")
{
using FnPtr = void*(*)(void*, const void*, size_t);
FnPtr Fn = &memcpy;
SECTION("With Abort")
{
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Fn(To, From, 6);
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE("Doggie says woof" == std::string_view(To));
}
SECTION("With Commit")
{
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { Fn(To, From, 6); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE("Kittie says woof" == std::string_view(To));
}
}
}
TEST_CASE("CString.memmove")
{
char To[] = "Hello, world!";
SECTION("Direct")
{
SECTION("With Abort")
{
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
memmove(To + 7, To, 5);
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE("Hello, world!" == std::string_view(To));
}
SECTION("With Commit")
{
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { memmove(To + 7, To, 5); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE("Hello, Hello!" == std::string_view(To));
}
}
SECTION("Indirect")
{
using FnPtr = void*(*)(void*, const void*, size_t);
FnPtr Fn = &memmove;
SECTION("With Abort")
{
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Fn(To + 7, To, 5);
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE("Hello, world!" == std::string_view(To));
}
SECTION("With Commit")
{
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { Fn(To + 7, To, 5); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE("Hello, Hello!" == std::string_view(To));
}
}
}
TEST_CASE("CString.memset")
{
char To[] = "Hello, world!";
SECTION("Direct")
{
SECTION("With Abort")
{
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
memset(To + 7, 'X', 5);
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE("Hello, world!" == std::string_view(To));
}
SECTION("With Commit")
{
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { memset(To + 7, 'X', 5); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE("Hello, XXXXX!" == std::string_view(To));
}
}
SECTION("Indirect")
{
using FnPtr = void*(*)(void*, int, size_t);
FnPtr Fn = &memset;
SECTION("With Abort")
{
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Fn(To + 7, 'X', 5);
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE("Hello, world!" == std::string_view(To));
}
SECTION("With Commit")
{
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { Fn(To + 7, 'X', 5); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE("Hello, XXXXX!" == std::string_view(To));
}
}
}
TEST_CASE("CString.strcpy")
{
MSVC_BEGIN_DISABLE_WARN_UNSAFE_FUNCTION
const char From[] = "Kittie says meow";
char To[] = "Doggie says woof____";
static_assert(sizeof(From) <= sizeof(To));
SECTION("With Abort")
{
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
strcpy(To, From);
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE("Doggie says woof____" == std::string_view(To));
}
SECTION("With Commit")
{
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { strcpy(To, From); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE("Kittie says meow" == std::string_view(To));
}
MSVC_END_DISABLE_UNSAFE_FN
}
TEST_CASE("CString.strncpy")
{
MSVC_BEGIN_DISABLE_WARN_UNSAFE_FUNCTION
const char* const From = "Kittie says meow";
char To[] = "Doggie says woof";
SECTION("With Abort")
{
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
strncpy(To, From, 6);
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE("Doggie says woof" == std::string_view(To));
}
SECTION("With Commit")
{
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { strncpy(To, From, 6); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE("Kittie says woof" == std::string_view(To));
}
MSVC_END_DISABLE_UNSAFE_FN
}
TEST_CASE("CString.strcat")
{
MSVC_BEGIN_DISABLE_WARN_UNSAFE_FUNCTION
constexpr unsigned Size = 128;
char To[Size] = "Hello";
SECTION("With Abort")
{
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
strcat(To, ", world!");
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE("Hello" == std::string_view(To));
}
SECTION("With Commit")
{
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { strcat(To, ", world!"); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE("Hello, world!" == std::string_view(To));
}
MSVC_END_DISABLE_UNSAFE_FN
}
TEST_CASE("CString.strncat")
{
MSVC_BEGIN_DISABLE_WARN_UNSAFE_FUNCTION
constexpr unsigned Size = 128;
char To[Size] = "Hello";
SECTION("With Abort")
{
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
strncat(To, ", world! Not this!", 8);
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE("Hello" == std::string_view(To));
}
SECTION("With Commit")
{
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { strncat(To, ", world! Not this!", 8); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE("Hello, world!" == std::string_view(To));
}
MSVC_END_DISABLE_UNSAFE_FN
}
TEST_CASE("CString.memcmp")
{
constexpr unsigned Size = 128;
char A[Size] = "This";
SECTION("With Abort")
{
int Compare = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Compare = memcmp(A, "That", 4);
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(0 == Compare);
}
SECTION("With Commit")
{
int Compare = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { Compare = memcmp(A, "That", 4); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(0 < Compare);
}
}
TEST_CASE("CString.strcmp")
{
const char* A = "This";
SECTION("With Abort")
{
int Compare = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Compare = strcmp(A, "That");
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(0 == Compare);
}
SECTION("With Commit")
{
int Compare = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { Compare = strcmp(A, "That"); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(0 < Compare);
}
}
TEST_CASE("CString.strncmp")
{
const char* A = "This";
SECTION("With Abort")
{
int Compare = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Compare = strncmp(A, "That", 3);
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(0 == Compare);
}
SECTION("With Commit")
{
int Compare = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { Compare = strncmp(A, "That", 3); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(0 < Compare);
}
}
TEST_CASE("CString.strchr")
{
const char* A = "Thinking";
SECTION("With Abort")
{
const char* Value = nullptr;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Value = strchr(A, 'i');
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(nullptr == Value);
}
SECTION("With Commit")
{
const char* Value = nullptr;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { Value = strchr(A, 'i'); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE((A + 2) == Value);
}
}
TEST_CASE("CString.strrchr")
{
const char* A = "Thinking";
SECTION("With Abort")
{
const char* Value = nullptr;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Value = strrchr(A, 'i');
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(nullptr == Value);
}
SECTION("With Commit")
{
const char* Value = nullptr;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { Value = strrchr(A, 'i'); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE((A + 5) == Value);
}
}
TEST_CASE("CString.strstr")
{
const char* A = "This";
SECTION("With Abort")
{
const char* Value = nullptr;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Value = strstr(A, "is");
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(nullptr == Value);
}
SECTION("With Commit")
{
const char* Value = nullptr;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { Value = strstr(A, "is"); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE((A + 2) == Value);
}
}
TEST_CASE("CString.strlen")
{
const char* A = "This";
SECTION("With Abort")
{
size_t Value = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Value = strlen(A);
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(0 == Value);
}
SECTION("With Commit")
{
size_t Value = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { Value = strlen(A); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(4 == Value);
}
}
TEST_CASE("CString.wcscpy")
{
MSVC_BEGIN_DISABLE_WARN_UNSAFE_FUNCTION
const wchar_t From[] = L"Kittie says meow";
wchar_t To[] = L"Doggie says woof____";
static_assert(sizeof(From) <= sizeof(To));
SECTION("With Abort")
{
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
wcscpy(To, From);
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(std::wstring_view(L"Doggie says woof____") == std::wstring_view(To));
}
SECTION("With Commit")
{
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { wcscpy(To, From); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(std::wstring_view(L"Kittie says meow") == std::wstring_view(To));
}
MSVC_END_DISABLE_UNSAFE_FN
}
TEST_CASE("CString.wcsncpy")
{
MSVC_BEGIN_DISABLE_WARN_UNSAFE_FUNCTION
const wchar_t* const From = L"Kittie says meow";
wchar_t To[] = L"Doggie says woof";
SECTION("With Abort")
{
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
wcsncpy(To, From, 6);
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(std::wstring_view(L"Doggie says woof") == std::wstring_view(To));
}
SECTION("With Commit")
{
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { wcsncpy(To, From, 6); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(std::wstring_view(L"Kittie says woof") == std::wstring_view(To));
}
MSVC_END_DISABLE_UNSAFE_FN
}
TEST_CASE("CString.wcsncmp")
{
const wchar_t* A = L"This";
SECTION("With Abort")
{
int Compare = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Compare = wcsncmp(A, L"That", 3);
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(0 == Compare);
}
SECTION("With Commit")
{
int Compare = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { Compare = wcsncmp(A, L"That", 3); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(0 < Compare);
}
}
TEST_CASE("CString.wcschr")
{
SECTION("Const")
{
const wchar_t* A = L"Thinking";
SECTION("With Abort")
{
const wchar_t* Value = nullptr;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Value = wcschr(A, L'i');
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(nullptr == Value);
}
SECTION("With Commit")
{
const wchar_t* Value = nullptr;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { Value = wcschr(A, L'i'); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE((A + 2) == Value);
}
}
SECTION("Non-Const")
{
wchar_t A[] = L"Thinking";
SECTION("With Abort")
{
wchar_t* Value = nullptr;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Value = wcschr(A, L'i');
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(nullptr == Value);
}
SECTION("With Commit")
{
wchar_t* Value = nullptr;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { Value = wcschr(A, L'i'); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE((A + 2) == Value);
}
}
}
TEST_CASE("CString.wcsstr")
{
SECTION("Const")
{
const wchar_t* A = L"Thinking";
SECTION("With Abort")
{
const wchar_t* Value = nullptr;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Value = wcsstr(A, L"ink");
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(nullptr == Value);
}
SECTION("With Commit")
{
const wchar_t* Value = nullptr;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { Value = wcsstr(A, L"ink"); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE((A + 2) == Value);
}
}
SECTION("Non-Const")
{
wchar_t A[] = L"Thinking";
SECTION("With Abort")
{
wchar_t* Value = nullptr;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Value = wcsstr(A, L"ink");
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(nullptr == Value);
}
SECTION("With Commit")
{
wchar_t* Value = nullptr;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { Value = wcsstr(A, L"ink"); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE((A + 2) == Value);
}
}
}
TEST_CASE("CString.wcsrchr")
{
const wchar_t* A = L"This";
SECTION("With Abort")
{
const wchar_t* Return = nullptr;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Return = wcsrchr(A, 'h');
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(nullptr == Return);
}
SECTION("With Commit")
{
const wchar_t* Return = nullptr;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { Return = wcsrchr(A, 'h'); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(nullptr != Return);
REQUIRE('h' == *Return);
}
}
TEST_CASE("CString.wcscmp")
{
const wchar_t* A = L"This";
SECTION("With Abort")
{
int Compare = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Compare = wcscmp(A, L"That");
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(0 == Compare);
}
SECTION("With Commit")
{
int Compare = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { Compare = wcscmp(A, L"That"); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(0 < Compare);
}
}
TEST_CASE("CString.wcslen")
{
const wchar_t* A = L"This";
SECTION("With Abort")
{
size_t Value = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Value = wcslen(A);
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(0 == Value);
}
SECTION("With Commit")
{
size_t Value = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&] { Value = wcslen(A); });
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(4 == Value);
}
}
TEST_CASE("CString.wcstod")
{
CheckStringToNumberFunction(wcstod, L"42.5abc");
}
TEST_CASE("CString.wcstof")
{
CheckStringToNumberFunction(wcstof, L"42.5abc");
}
TEST_CASE("CString.wcstold")
{
CheckStringToNumberFunction(wcstold, L"42.5abc");
}
TEST_CASE("CString.strtol")
{
CheckStringToNumberFunction(strtol, "42.5abc", 10);
}
TEST_CASE("CString.strtoll")
{
CheckStringToNumberFunction(strtoll, "42.5abc", 10);
}
TEST_CASE("CString.strtoul")
{
CheckStringToNumberFunction(strtoul, "42.5abc", 10);
}
TEST_CASE("CString.strtoull")
{
CheckStringToNumberFunction(strtoull, "42.5abc", 10);
}
TEST_CASE("CString.wcstol")
{
CheckStringToNumberFunction(wcstol, L"42abc", 10);
}
TEST_CASE("CString.wcstoll")
{
CheckStringToNumberFunction(wcstoll, L"42abc", 10);
}
TEST_CASE("CString.wcstoul")
{
CheckStringToNumberFunction(wcstoul, L"42abc", 10);
}
TEST_CASE("CString.wcstoull")
{
CheckStringToNumberFunction(wcstoull, L"42abc", 10);
}
TEST_CASE("std::to_chars")
{
CheckStdToChars(/* value */ static_cast<int>(42), /* base */ 8);
CheckStdToChars(/* value */ static_cast<unsigned int>(42), /* base */ 8);
CheckStdToChars(/* value */ static_cast<int8_t>(42), /* base */ 8);
CheckStdToChars(/* value */ static_cast<uint8_t>(42), /* base */ 8);
CheckStdToChars(/* value */ static_cast<int16_t>(42), /* base */ 8);
CheckStdToChars(/* value */ static_cast<uint16_t>(42), /* base */ 8);
CheckStdToChars(/* value */ static_cast<int32_t>(42), /* base */ 8);
CheckStdToChars(/* value */ static_cast<uint32_t>(42), /* base */ 8);
CheckStdToChars(/* value */ static_cast<int64_t>(42), /* base */ 8);
CheckStdToChars(/* value */ static_cast<uint64_t>(42), /* base */ 8);
CheckStdToChars(/* value */ static_cast<float>(42.24f));
CheckStdToChars(/* value */ static_cast<float>(42.24f), /* fmt */ std::chars_format::hex);
CheckStdToChars(/* value */ static_cast<float>(42.24f), /* fmt */ std::chars_format::hex, /* precision */ 3);
CheckStdToChars(/* value */ static_cast<double>(42.24));
CheckStdToChars(/* value */ static_cast<double>(42.24), /* fmt */ std::chars_format::hex);
CheckStdToChars(/* value */ static_cast<double>(42.24), /* fmt */ std::chars_format::hex, /* precision */ 3);
CheckStdToChars(/* value */ static_cast<long double>(42.24));
CheckStdToChars(/* value */ static_cast<long double>(42.24), /* fmt */ std::chars_format::hex);
CheckStdToChars(/* value */ static_cast<long double>(42.24), /* fmt */ std::chars_format::hex, /* precision */ 3);
}
#if PLATFORM_WINDOWS
TEST_CASE("CString._wcstoi64")
{
CheckStringToNumberFunction(_wcstoi64, L"42abc", 10);
}
TEST_CASE("CString._wcstoui64")
{
CheckStringToNumberFunction(_wcstoui64, L"42abc", 10);
}
TEST_CASE("CString._wcstod_l")
{
CheckStringToNumberFunction(_wcstod_l, L"42.5abc", _get_current_locale());
}
TEST_CASE("CString._wcstof_l")
{
CheckStringToNumberFunction(_wcstof_l, L"42.5abc", _get_current_locale());
}
TEST_CASE("CString._wcstold_l")
{
CheckStringToNumberFunction(_wcstold_l, L"42.5abc", _get_current_locale());
}
TEST_CASE("CString._wcstol_l")
{
CheckStringToNumberFunction(_wcstol_l, L"42abc", 10, _get_current_locale());
}
TEST_CASE("CString._wcstoll_l")
{
CheckStringToNumberFunction(_wcstoll_l, L"42abc", 10, _get_current_locale());
}
TEST_CASE("CString._wcstoul_l")
{
CheckStringToNumberFunction(_wcstoul_l, L"42abc", 10, _get_current_locale());
}
TEST_CASE("CString._wcstoull_l")
{
CheckStringToNumberFunction(_wcstoull_l, L"42abc", 10, _get_current_locale());
}
TEST_CASE("CString._wcstoi64_l")
{
CheckStringToNumberFunction(_wcstoi64_l, L"42abc", 10, _get_current_locale());
}
TEST_CASE("CString._wcstoui64_l")
{
CheckStringToNumberFunction(_wcstoui64_l, L"42abc", 10, _get_current_locale());
}
#endif
TEST_CASE("CString.iswupper")
{
CheckWCharFunction(iswupper, L'X');
CheckWCharFunction(iswupper, L'x');
}
TEST_CASE("CString.iswlower")
{
CheckWCharFunction(iswlower, L'X');
CheckWCharFunction(iswlower, L'x');
}
TEST_CASE("CString.iswalpha")
{
CheckWCharFunction(iswalpha, L'X');
CheckWCharFunction(iswalpha, L'5');
}
TEST_CASE("CString.iswgraph")
{
CheckWCharFunction(iswgraph, L'X');
CheckWCharFunction(iswgraph, L'x');
}
TEST_CASE("CString.iswprint")
{
CheckWCharFunction(iswprint, L'X');
CheckWCharFunction(iswprint, L'x');
}
TEST_CASE("CString.iswpunct")
{
CheckWCharFunction(iswpunct, L'X');
CheckWCharFunction(iswpunct, L'^');
}
TEST_CASE("CString.iswalnum")
{
CheckWCharFunction(iswalnum, L'X');
CheckWCharFunction(iswalnum, L'6');
CheckWCharFunction(iswalnum, L' ');
}
TEST_CASE("CString.iswdigit")
{
CheckWCharFunction(iswdigit, L'X');
CheckWCharFunction(iswdigit, L'5');
}
TEST_CASE("CString.iswxdigit")
{
CheckWCharFunction(iswxdigit, L'X');
CheckWCharFunction(iswxdigit, L'q');
}
TEST_CASE("CString.iswspace")
{
CheckWCharFunction(iswspace, L'X');
CheckWCharFunction(iswspace, L' ');
}
TEST_CASE("CString.iswcntrl")
{
CheckWCharFunction(iswcntrl, L'X');
CheckWCharFunction(iswcntrl, L'\u2028');
}
TEST_CASE("CString.snprintf")
{
static constexpr size_t BufferSize = 64;
char Buffer[BufferSize] = "_____________________";
SECTION("Fits in buffer")
{
SECTION("With Abort")
{
int Count = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Count = snprintf(Buffer, BufferSize, "cat says '%s'!", "meow");
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(0 == Count);
REQUIRE("_____________________" == std::string_view(Buffer));
}
SECTION("With Commit")
{
int Count = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&]
{
Count = snprintf(Buffer, BufferSize, "cat says '%s'!", "meow");
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(16 == Count);
REQUIRE("cat says 'meow'!" == std::string_view(Buffer));
}
}
SECTION("Buffer too small")
{
CLANG_BEGIN_DISABLE_WARN_FORMAT_TRUNCATED
SECTION("With Abort")
{
int Count = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Count = snprintf(Buffer, 8, "cat says '%s'!", "meow");
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(0 == Count);
REQUIRE("_____________________" == std::string_view(Buffer));
}
SECTION("With Commit")
{
int Count = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&]
{
Count = snprintf(Buffer, 8, "cat says '%s'!", "meow");
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(16 == Count);
REQUIRE("cat say" == std::string_view(Buffer));
}
CLANG_END_DISABLE_WARN_FORMAT_TRUNCATED
}
SECTION("Null buffer")
{
SECTION("With Abort")
{
int Count = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Count = snprintf(nullptr, 0, "cat says '%s'!", "meow");
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(0 == Count);
}
SECTION("With Commit")
{
int Count = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&]
{
Count = snprintf(nullptr, 0, "cat says '%s'!", "meow");
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(16 == Count);
}
}
SECTION("PercentN")
{
FCaptureWarningContext WarningContext;
int Count = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
snprintf(Buffer, 8, "%n", &Count);
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByLanguage == Result);
REQUIRE(0 == Count);
REQUIRE("_____________________" == std::string_view(Buffer));
REQUIRE_THAT(WarningContext.GetWarnings(),
Catch::Matchers::VectorContains(FString(kPercentNWarning)));
}
SECTION("PercentPercentN")
{
SECTION("With Abort")
{
int Count = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Count = snprintf(Buffer, 8, "%%n");
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(0 == Count);
REQUIRE("_____________________" == std::string_view(Buffer));
}
SECTION("With Commit")
{
int Count = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&]
{
Count = snprintf(Buffer, 8, "%%n");
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(2 == Count);
REQUIRE("%n" == std::string_view(Buffer));
}
}
}
TEST_CASE("CString.swprintf")
{
static constexpr size_t BufferSize = 64;
wchar_t Buffer[BufferSize] = L"_____________________";
SECTION("Fits in buffer")
{
SECTION("With Abort")
{
int Count = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Count = swprintf(Buffer, BufferSize, L"cat says '%ls'!", L"meow");
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(0 == Count);
REQUIRE(std::wstring_view(L"_____________________") == std::wstring_view(Buffer));
}
SECTION("With Commit")
{
int Count = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&]
{
Count = swprintf(Buffer, BufferSize, L"cat says '%ls'!", L"meow");
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(16 == Count);
REQUIRE(std::wstring_view(L"cat says 'meow'!") == std::wstring_view(Buffer));
}
}
SECTION("Buffer too small")
{
CLANG_BEGIN_DISABLE_WARN_FORMAT_TRUNCATED
SECTION("With Abort")
{
int Count = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Count = swprintf(Buffer, 8, L"cat says '%ls'!", L"meow");
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(0 == Count);
REQUIRE(std::wstring_view(L"_____________________") == std::wstring_view(Buffer));
}
SECTION("With Commit")
{
int Count = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&]
{
Count = swprintf(Buffer, 8, L"cat says '%ls'!", L"meow");
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(0 > Count);
REQUIRE(std::wstring_view(L"cat say") == std::wstring_view(Buffer, 7));
}
CLANG_END_DISABLE_WARN_FORMAT_TRUNCATED
}
SECTION("Null buffer")
{
SECTION("With Abort")
{
int Count = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Count = swprintf(nullptr, 0, L"cat says '%ls'!", L"meow");
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(0 == Count);
}
SECTION("With Commit")
{
int Count = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&]
{
Count = swprintf(nullptr, 0, L"cat says '%ls'!", L"meow");
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(((0 > Count) || (16 == Count)));
}
}
SECTION("PercentN")
{
FCaptureWarningContext WarningContext;
int Count = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
swprintf(Buffer, 8, L"%n", &Count);
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByLanguage == Result);
REQUIRE(0 == Count);
REQUIRE(std::wstring_view(L"_____________________") == std::wstring_view(Buffer));
REQUIRE_THAT(WarningContext.GetWarnings(),
Catch::Matchers::VectorContains(FString(kPercentNWarning)));
}
SECTION("PercentPercentN")
{
SECTION("With Abort")
{
int Count = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Count = swprintf(Buffer, 8, L"%%n");
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(0 == Count);
REQUIRE(std::wstring_view(L"_____________________") == std::wstring_view(Buffer));
}
SECTION("With Commit")
{
int Count = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&]
{
Count = swprintf(Buffer, 8, L"%%n");
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(2 == Count);
REQUIRE(std::wstring_view(L"%n") == std::wstring_view(Buffer));
}
}
}
TEST_CASE("CString.printf")
{
SECTION("With Abort")
{
int Count = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Count = printf("AutoRTFM printf test: %%n\n");
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(0 == Count);
}
SECTION("With Commit")
{
int Count = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::Commit([&]
{
Count = printf("AutoRTFM printf test: %%n\n");
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(25 == Count);
}
SECTION("PercentN")
{
FCaptureWarningContext WarningContext;
int Count = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
printf("%n", &Count);
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByLanguage == Result);
REQUIRE(0 == Count);
REQUIRE_THAT(WarningContext.GetWarnings(),
Catch::Matchers::VectorContains(FString(kPercentNWarning)));
}
}
TEST_CASE("CString.wprintf")
{
#if PLATFORM_WINDOWS // wprintf() can error on linux
SECTION("With Abort")
{
int Count = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Count = wprintf(L"AutoRTFM wprintf test: %%n\n");
AutoRTFM::AbortTransaction();
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByRequest == Result);
REQUIRE(0 == Count);
}
SECTION("With Commit")
{
int Count = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
Count = wprintf(L"AutoRTFM wprintf test: %%n\n");
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::Committed == Result);
REQUIRE(26 == Count);
}
#endif // PLATFORM_WINDOWS
SECTION("PercentN")
{
FCaptureWarningContext WarningContext;
int Count = 0;
CLANG_BEGIN_DISABLE_OPTIMIZATIONS
AutoRTFM::ETransactionResult Result = AutoRTFM::Transact([&]
{
wprintf(L"%n", &Count);
});
CLANG_END_DISABLE_OPTIMIZATIONS
REQUIRE(AutoRTFM::ETransactionResult::AbortedByLanguage == Result);
REQUIRE(0 == Count);
REQUIRE_THAT(WarningContext.GetWarnings(),
Catch::Matchers::VectorContains(FString(kPercentNWarning)));
}
}
#undef CLANG_BEGIN_DISABLE_OPTIMIZATIONS
#undef CLANG_END_DISABLE_OPTIMIZATIONS
#undef MSVC_BEGIN_DISABLE_WARN_UNSAFE_FUNCTION
#undef MSVC_END_DISABLE_UNSAFE_FN