Merge branch 'Zbyl-string-functions-semantics'
This commit is contained in:
@@ -226,7 +226,9 @@ bool String::IsANSI() const
|
||||
|
||||
bool String::StartsWith(const StringView& prefix, StringSearchCase searchCase) const
|
||||
{
|
||||
if (prefix.IsEmpty() || prefix.Length() > Length())
|
||||
if (prefix.IsEmpty())
|
||||
return true;
|
||||
if (prefix.Length() > Length())
|
||||
return false;
|
||||
if (searchCase == StringSearchCase::IgnoreCase)
|
||||
return !StringUtils::CompareIgnoreCase(this->GetText(), *prefix, prefix.Length());
|
||||
@@ -235,7 +237,9 @@ bool String::StartsWith(const StringView& prefix, StringSearchCase searchCase) c
|
||||
|
||||
bool String::EndsWith(const StringView& suffix, StringSearchCase searchCase) const
|
||||
{
|
||||
if (suffix.IsEmpty() || suffix.Length() > Length())
|
||||
if (suffix.IsEmpty())
|
||||
return true;
|
||||
if (suffix.Length() > Length())
|
||||
return false;
|
||||
if (searchCase == StringSearchCase::IgnoreCase)
|
||||
return !StringUtils::CompareIgnoreCase(&(*this)[Length() - suffix.Length()], *suffix);
|
||||
|
||||
@@ -65,10 +65,11 @@ public:
|
||||
|
||||
/// <summary>
|
||||
/// Lexicographically tests how this string compares to the other given string.
|
||||
/// In case sensitive mode 'A' is less than 'a'.
|
||||
/// </summary>
|
||||
/// <param name="str">The another string test against.</param>
|
||||
/// <param name="searchCase">The case sensitivity mode.</param>
|
||||
/// <returns>0 if equal, -1 if less than, 1 if greater than.</returns>
|
||||
/// <returns>0 if equal, negative number if less than, positive number if greater than.</returns>
|
||||
int32 Compare(const StringBase& str, StringSearchCase searchCase = StringSearchCase::CaseSensitive) const
|
||||
{
|
||||
if (searchCase == StringSearchCase::CaseSensitive)
|
||||
@@ -352,7 +353,7 @@ public:
|
||||
bool StartsWith(T c, StringSearchCase searchCase = StringSearchCase::CaseSensitive) const
|
||||
{
|
||||
const int32 length = Length();
|
||||
if (searchCase == StringSearchCase::IgnoreCase)
|
||||
if (searchCase == StringSearchCase::CaseSensitive)
|
||||
return length > 0 && _data[0] == c;
|
||||
return length > 0 && StringUtils::ToLower(_data[0]) == StringUtils::ToLower(c);
|
||||
}
|
||||
@@ -360,14 +361,16 @@ public:
|
||||
bool EndsWith(T c, StringSearchCase searchCase = StringSearchCase::CaseSensitive) const
|
||||
{
|
||||
const int32 length = Length();
|
||||
if (searchCase == StringSearchCase::IgnoreCase)
|
||||
if (searchCase == StringSearchCase::CaseSensitive)
|
||||
return length > 0 && _data[length - 1] == c;
|
||||
return length > 0 && StringUtils::ToLower(_data[length - 1]) == StringUtils::ToLower(c);
|
||||
}
|
||||
|
||||
bool StartsWith(const StringBase& prefix, StringSearchCase searchCase = StringSearchCase::CaseSensitive) const
|
||||
{
|
||||
if (prefix.IsEmpty() || Length() < prefix.Length())
|
||||
if (prefix.IsEmpty())
|
||||
return true;
|
||||
if (Length() < prefix.Length())
|
||||
return false;
|
||||
if (searchCase == StringSearchCase::IgnoreCase)
|
||||
return StringUtils::CompareIgnoreCase(this->GetText(), *prefix, prefix.Length()) == 0;
|
||||
@@ -376,7 +379,9 @@ public:
|
||||
|
||||
bool EndsWith(const StringBase& suffix, StringSearchCase searchCase = StringSearchCase::CaseSensitive) const
|
||||
{
|
||||
if (suffix.IsEmpty() || Length() < suffix.Length())
|
||||
if (suffix.IsEmpty())
|
||||
return true;
|
||||
if (Length() < suffix.Length())
|
||||
return false;
|
||||
if (searchCase == StringSearchCase::IgnoreCase)
|
||||
return StringUtils::CompareIgnoreCase(&(*this)[Length() - suffix.Length()], *suffix) == 0;
|
||||
@@ -429,22 +434,19 @@ public:
|
||||
/// <summary>
|
||||
/// Replaces all occurences of searchText within current string with replacementText.
|
||||
/// </summary>
|
||||
/// <param name="searchText">String to search for. If empty or null no replacements are done.</param>
|
||||
/// <param name="searchTextLength">Length of searchText.</param>
|
||||
/// <param name="searchText">String to search for.</param>
|
||||
/// <param name="searchTextLength">Length of searchText. Must be greater than zero.</param>
|
||||
/// <param name="replacementText">String to replace with. Null is treated as empty string.</param>
|
||||
/// <param name="replacementTextLength">Length of replacementText.</param>
|
||||
/// <returns>Number of replacements made. (In case-sensitive mode if search text and replacement text are equal no replacements are done, and zero is returned.)</returns>
|
||||
/// <returns>Number of replacements made (in other words number of occurences of searchText).</returns>
|
||||
int32 Replace(const T* searchText, int32 searchTextLength, const T* replacementText, int32 replacementTextLength, StringSearchCase searchCase = StringSearchCase::CaseSensitive)
|
||||
{
|
||||
if (!HasChars())
|
||||
return 0;
|
||||
|
||||
if (searchTextLength == 0)
|
||||
return 0;
|
||||
|
||||
// If we are doing case sensitive search and replacement text is equal to search text, we do nothing.
|
||||
if ((searchCase == StringSearchCase::CaseSensitive) && (searchTextLength == replacementTextLength) && (StringUtils::Compare(searchText, replacementText) == 0))
|
||||
{
|
||||
ASSERT(false); // Empty search text never makes sense, and is always sign of a bug in calling code.
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -54,21 +54,23 @@ public:
|
||||
|
||||
/// <summary>
|
||||
/// Lexicographically tests how this string compares to the other given string.
|
||||
/// In case sensitive mode 'A' is less than 'a'.
|
||||
/// </summary>
|
||||
/// <param name="str">The another string test against.</param>
|
||||
/// <param name="searchCase">The case sensitivity mode.</param>
|
||||
/// <returns>0 if equal, -1 if less than, 1 if greater than.</returns>
|
||||
/// <returns>0 if equal, negative number if less than, positive number if greater than.</returns>
|
||||
int32 Compare(const StringViewBase& str, StringSearchCase searchCase = StringSearchCase::CaseSensitive) const
|
||||
{
|
||||
const int32 lengthDiff = Length() - str.Length();
|
||||
if (lengthDiff != 0)
|
||||
return lengthDiff;
|
||||
if (Length() == 0)
|
||||
const bool thisIsShorter = Length() < str.Length();
|
||||
const int32 minLength = thisIsShorter ? Length() : str.Length();
|
||||
const int32 prefixCompare = (searchCase == StringSearchCase::CaseSensitive)
|
||||
? StringUtils::Compare(this->GetNonTerminatedText(), str.GetNonTerminatedText(), minLength)
|
||||
: StringUtils::CompareIgnoreCase(this->GetNonTerminatedText(), str.GetNonTerminatedText(), minLength);
|
||||
if (prefixCompare != 0)
|
||||
return prefixCompare;
|
||||
if (Length() == str.Length())
|
||||
return 0;
|
||||
// We know here that both this StringView and str are not empty, and therefore Get() below are valid.
|
||||
if (searchCase == StringSearchCase::CaseSensitive)
|
||||
return StringUtils::Compare(this->Get(), str.Get(), Length());
|
||||
return StringUtils::CompareIgnoreCase(this->Get(), str.Get(), Length());
|
||||
return thisIsShorter ? -1 : 1;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
334
Source/Engine/TestsMain/StringTests.cpp
Normal file
334
Source/Engine/TestsMain/StringTests.cpp
Normal file
@@ -0,0 +1,334 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
|
||||
#include "Engine/Core/Types/String.h"
|
||||
#include "Engine/Core/Types/StringView.h"
|
||||
|
||||
#include <ThirdParty/catch2/catch.hpp>
|
||||
|
||||
|
||||
TEST_CASE("String Replace works") {
|
||||
SECTION("Char, case sensitive") {
|
||||
String str("hello HELLO");
|
||||
CHECK(str.Replace('l', 'x', StringSearchCase::CaseSensitive) == 2);
|
||||
CHECK(str == String("hexxo HELLO"));
|
||||
}
|
||||
|
||||
SECTION("Char, ignore case") {
|
||||
String str("hello HELLO");
|
||||
CHECK(str.Replace('l', 'x', StringSearchCase::IgnoreCase) == 4);
|
||||
CHECK(str == String("hexxo HExxO"));
|
||||
}
|
||||
|
||||
SECTION("case sensitive") {
|
||||
String str("hello HELLO this is me saying hello");
|
||||
CHECK(str.Replace(TEXT("hello"), TEXT("hi"), StringSearchCase::CaseSensitive) == 2);
|
||||
CHECK(str == String("hi HELLO this is me saying hi"));
|
||||
}
|
||||
|
||||
SECTION("ignore case") {
|
||||
String str("hello HELLO this is me saying hello");
|
||||
CHECK(str.Replace(TEXT("hello"), TEXT("hi"), StringSearchCase::IgnoreCase) == 3);
|
||||
CHECK(str == String("hi hi this is me saying hi"));
|
||||
}
|
||||
|
||||
SECTION("case sensitive, search and replace texts identical") {
|
||||
String str("hello HELLO this is me saying hello");
|
||||
CHECK(str.Replace(TEXT("hello"), TEXT("hello"), StringSearchCase::CaseSensitive) == 2);
|
||||
CHECK(str == String("hello HELLO this is me saying hello"));
|
||||
}
|
||||
|
||||
SECTION("ignore case, search and replace texts identical") {
|
||||
String str("hello HELLO this is me saying hello");
|
||||
CHECK(str.Replace(TEXT("hello"), TEXT("hello"), StringSearchCase::IgnoreCase) == 3);
|
||||
CHECK(str == String("hello hello this is me saying hello"));
|
||||
}
|
||||
|
||||
SECTION("case sensitive, replace text empty") {
|
||||
String str("hello HELLO this is me saying hello");
|
||||
CHECK(str.Replace(TEXT("hello"), TEXT(""), StringSearchCase::CaseSensitive) == 2);
|
||||
CHECK(str == String(" HELLO this is me saying "));
|
||||
}
|
||||
|
||||
SECTION("ignore case, replace text empty") {
|
||||
String str("hello HELLO this is me saying hello");
|
||||
CHECK(str.Replace(TEXT("hello"), TEXT(""), StringSearchCase::IgnoreCase) == 3);
|
||||
CHECK(str == String(" this is me saying "));
|
||||
}
|
||||
|
||||
SECTION("no finds") {
|
||||
String str("hello HELLO this is me saying hello");
|
||||
CHECK(str.Replace(TEXT("bye"), TEXT("hi"), StringSearchCase::CaseSensitive) == 0);
|
||||
CHECK(str.Replace(TEXT("bye"), TEXT("hi"), StringSearchCase::IgnoreCase) == 0);
|
||||
CHECK(str == String("hello HELLO this is me saying hello"));
|
||||
}
|
||||
|
||||
SECTION("empty input") {
|
||||
String str("");
|
||||
CHECK(str.Replace(TEXT("bye"), TEXT("hi"), StringSearchCase::CaseSensitive) == 0);
|
||||
CHECK(str.Replace(TEXT("bye"), TEXT("hi"), StringSearchCase::IgnoreCase) == 0);
|
||||
CHECK(str == String(""));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("String Starts/EndsWith works") {
|
||||
SECTION("StartsWith, case sensitive") {
|
||||
SECTION("Char") {
|
||||
CHECK(String("").StartsWith('h', StringSearchCase::CaseSensitive) == false);
|
||||
CHECK(String("hello HELLO").StartsWith('h', StringSearchCase::CaseSensitive) == true);
|
||||
CHECK(String("hello HELLO").StartsWith('H', StringSearchCase::CaseSensitive) == false);
|
||||
}
|
||||
|
||||
SECTION("String") {
|
||||
CHECK(String("hello HELLO").StartsWith(String(TEXT("hello")), StringSearchCase::CaseSensitive) == true);
|
||||
CHECK(String("hello HELLO").StartsWith(String(TEXT("HELLO")), StringSearchCase::CaseSensitive) == false);
|
||||
CHECK(String("hello HELLO").StartsWith(String(TEXT("")), StringSearchCase::CaseSensitive) == true);
|
||||
CHECK(String("hello HELLO").StartsWith(String(TEXT("xxx")), StringSearchCase::CaseSensitive) == false);
|
||||
|
||||
CHECK(String("").StartsWith(String(TEXT("x")), StringSearchCase::CaseSensitive) == false);
|
||||
CHECK(String("hello HELLO").StartsWith(String(TEXT("hello HELLOx")), StringSearchCase::CaseSensitive) == false);
|
||||
CHECK(String("hello HELLO").StartsWith(String(TEXT("xhello HELLO")), StringSearchCase::CaseSensitive) == false);
|
||||
}
|
||||
|
||||
SECTION("StringView") {
|
||||
CHECK(String("hello HELLO").StartsWith(StringView(TEXT("hello")), StringSearchCase::CaseSensitive) == true);
|
||||
CHECK(String("hello HELLO").StartsWith(StringView(TEXT("HELLO")), StringSearchCase::CaseSensitive) == false);
|
||||
CHECK(String("hello HELLO").StartsWith(StringView(), StringSearchCase::CaseSensitive) == true);
|
||||
CHECK(String("hello HELLO").StartsWith(StringView(TEXT("")), StringSearchCase::CaseSensitive) == true);
|
||||
CHECK(String("hello HELLO").StartsWith(StringView(TEXT("xxx")), StringSearchCase::CaseSensitive) == false);
|
||||
|
||||
CHECK(String("").StartsWith(StringView(TEXT("x")), StringSearchCase::CaseSensitive) == false);
|
||||
CHECK(String("hello HELLO").StartsWith(StringView(TEXT("hello HELLOx")), StringSearchCase::CaseSensitive) == false);
|
||||
CHECK(String("hello HELLO").StartsWith(StringView(TEXT("xhello HELLO")), StringSearchCase::CaseSensitive) == false);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("StartsWith, ignore case") {
|
||||
SECTION("Char") {
|
||||
CHECK(String("").StartsWith('h', StringSearchCase::IgnoreCase) == false);
|
||||
CHECK(String("hello HELLO").StartsWith('h', StringSearchCase::IgnoreCase) == true);
|
||||
CHECK(String("hello HELLO").StartsWith('H', StringSearchCase::IgnoreCase) == true);
|
||||
}
|
||||
|
||||
SECTION("String") {
|
||||
CHECK(String("hello HELLO").StartsWith(String(TEXT("hello")), StringSearchCase::IgnoreCase) == true);
|
||||
CHECK(String("hello HELLO").StartsWith(String(TEXT("HELLO")), StringSearchCase::IgnoreCase) == true);
|
||||
CHECK(String("hello HELLO").StartsWith(String(TEXT("")), StringSearchCase::IgnoreCase) == true);
|
||||
CHECK(String("hello HELLO").StartsWith(String(TEXT("xxx")), StringSearchCase::IgnoreCase) == false);
|
||||
|
||||
CHECK(String("").StartsWith(String(TEXT("x")), StringSearchCase::IgnoreCase) == false);
|
||||
CHECK(String("hello HELLO").StartsWith(String(TEXT("hello HELLOx")), StringSearchCase::IgnoreCase) == false);
|
||||
CHECK(String("hello HELLO").StartsWith(String(TEXT("xhello HELLO")), StringSearchCase::IgnoreCase) == false);
|
||||
}
|
||||
|
||||
SECTION("StringView") {
|
||||
CHECK(String("hello HELLO").StartsWith(StringView(TEXT("hello")), StringSearchCase::IgnoreCase) == true);
|
||||
CHECK(String("hello HELLO").StartsWith(StringView(TEXT("HELLO")), StringSearchCase::IgnoreCase) == true);
|
||||
CHECK(String("hello HELLO").StartsWith(StringView(), StringSearchCase::IgnoreCase) == true);
|
||||
CHECK(String("hello HELLO").StartsWith(StringView(TEXT("")), StringSearchCase::IgnoreCase) == true);
|
||||
CHECK(String("hello HELLO").StartsWith(StringView(TEXT("xxx")), StringSearchCase::IgnoreCase) == false);
|
||||
|
||||
CHECK(String("").StartsWith(StringView(TEXT("x")), StringSearchCase::IgnoreCase) == false);
|
||||
CHECK(String("hello HELLO").StartsWith(StringView(TEXT("hello HELLOx")), StringSearchCase::IgnoreCase) == false);
|
||||
CHECK(String("hello HELLO").StartsWith(StringView(TEXT("xhello HELLO")), StringSearchCase::IgnoreCase) == false);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("EndsWith, case sensitive") {
|
||||
SECTION("Char") {
|
||||
CHECK(String("").EndsWith('h', StringSearchCase::CaseSensitive) == false);
|
||||
CHECK(String("hello HELLO").EndsWith('O', StringSearchCase::CaseSensitive) == true);
|
||||
CHECK(String("hello HELLO").EndsWith('o', StringSearchCase::CaseSensitive) == false);
|
||||
}
|
||||
|
||||
SECTION("String") {
|
||||
CHECK(String("hello HELLO").EndsWith(String(TEXT("HELLO")), StringSearchCase::CaseSensitive) == true);
|
||||
CHECK(String("hello HELLO").EndsWith(String(TEXT("hello")), StringSearchCase::CaseSensitive) == false);
|
||||
CHECK(String("hello HELLO").EndsWith(String(TEXT("")), StringSearchCase::CaseSensitive) == true);
|
||||
CHECK(String("hello HELLO").EndsWith(String(TEXT("xxx")), StringSearchCase::CaseSensitive) == false);
|
||||
|
||||
CHECK(String("").EndsWith(String(TEXT("x")), StringSearchCase::CaseSensitive) == false);
|
||||
CHECK(String("hello HELLO").EndsWith(String(TEXT("hello HELLOx")), StringSearchCase::CaseSensitive) == false);
|
||||
CHECK(String("hello HELLO").EndsWith(String(TEXT("xhello HELLO")), StringSearchCase::CaseSensitive) == false);
|
||||
}
|
||||
|
||||
SECTION("StringView") {
|
||||
CHECK(String("hello HELLO").EndsWith(StringView(TEXT("HELLO")), StringSearchCase::CaseSensitive) == true);
|
||||
CHECK(String("hello HELLO").EndsWith(StringView(TEXT("hello")), StringSearchCase::CaseSensitive) == false);
|
||||
CHECK(String("hello HELLO").EndsWith(StringView(), StringSearchCase::CaseSensitive) == true);
|
||||
CHECK(String("hello HELLO").EndsWith(StringView(TEXT("")), StringSearchCase::CaseSensitive) == true);
|
||||
CHECK(String("hello HELLO").EndsWith(StringView(TEXT("xxx")), StringSearchCase::CaseSensitive) == false);
|
||||
|
||||
CHECK(String("").EndsWith(StringView(TEXT("x")), StringSearchCase::CaseSensitive) == false);
|
||||
CHECK(String("hello HELLO").EndsWith(StringView(TEXT("hello HELLOx")), StringSearchCase::CaseSensitive) == false);
|
||||
CHECK(String("hello HELLO").EndsWith(StringView(TEXT("xhello HELLO")), StringSearchCase::CaseSensitive) == false);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("EndsWith, ignore case") {
|
||||
SECTION("Char") {
|
||||
CHECK(String("").EndsWith('h', StringSearchCase::IgnoreCase) == false);
|
||||
CHECK(String("hello HELLO").EndsWith('O', StringSearchCase::IgnoreCase) == true);
|
||||
CHECK(String("hello HELLO").EndsWith('o', StringSearchCase::IgnoreCase) == true);
|
||||
}
|
||||
|
||||
SECTION("String") {
|
||||
CHECK(String("hello HELLO").EndsWith(String(TEXT("HELLO")), StringSearchCase::IgnoreCase) == true);
|
||||
CHECK(String("hello HELLO").EndsWith(String(TEXT("hello")), StringSearchCase::IgnoreCase) == true);
|
||||
CHECK(String("hello HELLO").EndsWith(String(TEXT("")), StringSearchCase::IgnoreCase) == true);
|
||||
CHECK(String("hello HELLO").EndsWith(String(TEXT("xxx")), StringSearchCase::IgnoreCase) == false);
|
||||
|
||||
CHECK(String("").EndsWith(String(TEXT("x")), StringSearchCase::IgnoreCase) == false);
|
||||
CHECK(String("hello HELLO").EndsWith(String(TEXT("hello HELLOx")), StringSearchCase::IgnoreCase) == false);
|
||||
CHECK(String("hello HELLO").EndsWith(String(TEXT("xhello HELLO")), StringSearchCase::IgnoreCase) == false);
|
||||
}
|
||||
|
||||
SECTION("StringView") {
|
||||
CHECK(String("hello HELLO").EndsWith(StringView(TEXT("HELLO")), StringSearchCase::IgnoreCase) == true);
|
||||
CHECK(String("hello HELLO").EndsWith(StringView(TEXT("hello")), StringSearchCase::IgnoreCase) == true);
|
||||
CHECK(String("hello HELLO").EndsWith(StringView(), StringSearchCase::IgnoreCase) == true);
|
||||
CHECK(String("hello HELLO").EndsWith(StringView(TEXT("")), StringSearchCase::IgnoreCase) == true);
|
||||
CHECK(String("hello HELLO").EndsWith(StringView(TEXT("xxx")), StringSearchCase::IgnoreCase) == false);
|
||||
|
||||
CHECK(String("").EndsWith(StringView(TEXT("x")), StringSearchCase::IgnoreCase) == false);
|
||||
CHECK(String("hello HELLO").EndsWith(StringView(TEXT("hello HELLOx")), StringSearchCase::IgnoreCase) == false);
|
||||
CHECK(String("hello HELLO").EndsWith(StringView(TEXT("xhello HELLO")), StringSearchCase::IgnoreCase) == false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("String Compare works") {
|
||||
SECTION("String") {
|
||||
SECTION("case sensitive") {
|
||||
// Empty strings
|
||||
CHECK(String("").Compare(String(TEXT("")), StringSearchCase::CaseSensitive) == 0);
|
||||
CHECK(String("").Compare(String(TEXT("xxx")), StringSearchCase::CaseSensitive) < 0);
|
||||
CHECK(String("xxx").Compare(String(TEXT("")), StringSearchCase::CaseSensitive) > 0);
|
||||
|
||||
// Equal lengths, difference at end
|
||||
CHECK(String("xxx").Compare(String(TEXT("xxx")), StringSearchCase::CaseSensitive) == 0);
|
||||
CHECK(String("abc").Compare(String(TEXT("abd")), StringSearchCase::CaseSensitive) < 0);
|
||||
CHECK(String("abd").Compare(String(TEXT("abc")), StringSearchCase::CaseSensitive) > 0);
|
||||
|
||||
// Equal lengths, difference in the middle
|
||||
CHECK(String("abcx").Compare(String(TEXT("abdx")), StringSearchCase::CaseSensitive) < 0);
|
||||
CHECK(String("abdx").Compare(String(TEXT("abcx")), StringSearchCase::CaseSensitive) > 0);
|
||||
|
||||
// Different lengths, same prefix
|
||||
CHECK(String("abcxx").Compare(String(TEXT("abc")), StringSearchCase::CaseSensitive) > 0);
|
||||
CHECK(String("abc").Compare(String(TEXT("abcxx")), StringSearchCase::CaseSensitive) < 0);
|
||||
|
||||
// Different lengths, different prefix
|
||||
CHECK(String("abcx").Compare(String(TEXT("abd")), StringSearchCase::CaseSensitive) < 0);
|
||||
CHECK(String("abd").Compare(String(TEXT("abcx")), StringSearchCase::CaseSensitive) > 0);
|
||||
CHECK(String("abc").Compare(String(TEXT("abdx")), StringSearchCase::CaseSensitive) < 0);
|
||||
CHECK(String("abdx").Compare(String(TEXT("abc")), StringSearchCase::CaseSensitive) > 0);
|
||||
|
||||
// Case differences
|
||||
CHECK(String("a").Compare(String(TEXT("A")), StringSearchCase::CaseSensitive) > 0);
|
||||
CHECK(String("A").Compare(String(TEXT("a")), StringSearchCase::CaseSensitive) < 0);
|
||||
}
|
||||
|
||||
SECTION("ignore case") {
|
||||
// Empty strings
|
||||
CHECK(String("").Compare(String(TEXT("")), StringSearchCase::IgnoreCase) == 0);
|
||||
CHECK(String("").Compare(String(TEXT("xxx")), StringSearchCase::IgnoreCase) < 0);
|
||||
CHECK(String("xxx").Compare(String(TEXT("")), StringSearchCase::IgnoreCase) > 0);
|
||||
|
||||
// Equal lengths, difference at end
|
||||
CHECK(String("xxx").Compare(String(TEXT("xxx")), StringSearchCase::IgnoreCase) == 0);
|
||||
CHECK(String("abc").Compare(String(TEXT("abd")), StringSearchCase::IgnoreCase) < 0);
|
||||
CHECK(String("abd").Compare(String(TEXT("abc")), StringSearchCase::IgnoreCase) > 0);
|
||||
|
||||
// Equal lengths, difference in the middle
|
||||
CHECK(String("abcx").Compare(String(TEXT("abdx")), StringSearchCase::IgnoreCase) < 0);
|
||||
CHECK(String("abdx").Compare(String(TEXT("abcx")), StringSearchCase::IgnoreCase) > 0);
|
||||
|
||||
// Different lengths, same prefix
|
||||
CHECK(String("abcxx").Compare(String(TEXT("abc")), StringSearchCase::IgnoreCase) > 0);
|
||||
CHECK(String("abc").Compare(String(TEXT("abcxx")), StringSearchCase::IgnoreCase) < 0);
|
||||
|
||||
// Different lengths, different prefix
|
||||
CHECK(String("abcx").Compare(String(TEXT("abd")), StringSearchCase::IgnoreCase) < 0);
|
||||
CHECK(String("abd").Compare(String(TEXT("abcx")), StringSearchCase::IgnoreCase) > 0);
|
||||
CHECK(String("abc").Compare(String(TEXT("abdx")), StringSearchCase::IgnoreCase) < 0);
|
||||
CHECK(String("abdx").Compare(String(TEXT("abc")), StringSearchCase::IgnoreCase) > 0);
|
||||
|
||||
// Case differences
|
||||
CHECK(String("a").Compare(String(TEXT("A")), StringSearchCase::IgnoreCase) == 0);
|
||||
CHECK(String("A").Compare(String(TEXT("a")), StringSearchCase::IgnoreCase) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("StringView") {
|
||||
SECTION("case sensitive") {
|
||||
// Null string views
|
||||
CHECK(StringView().Compare(StringView(), StringSearchCase::CaseSensitive) == 0);
|
||||
CHECK(StringView().Compare(StringView(TEXT("xxx")), StringSearchCase::CaseSensitive) < 0);
|
||||
CHECK(StringView(TEXT("xxx")).Compare(StringView(), StringSearchCase::CaseSensitive) > 0);
|
||||
|
||||
// Empty strings
|
||||
CHECK(StringView(TEXT("")).Compare(StringView(TEXT("")), StringSearchCase::CaseSensitive) == 0);
|
||||
CHECK(StringView(TEXT("")).Compare(StringView(TEXT("xxx")), StringSearchCase::CaseSensitive) < 0);
|
||||
CHECK(StringView(TEXT("xxx")).Compare(StringView(TEXT("")), StringSearchCase::CaseSensitive) > 0);
|
||||
|
||||
// Equal lengths, difference at end
|
||||
CHECK(StringView(TEXT("xxx")).Compare(StringView(TEXT("xxx")), StringSearchCase::CaseSensitive) == 0);
|
||||
CHECK(StringView(TEXT("abc")).Compare(StringView(TEXT("abd")), StringSearchCase::CaseSensitive) < 0);
|
||||
CHECK(StringView(TEXT("abd")).Compare(StringView(TEXT("abc")), StringSearchCase::CaseSensitive) > 0);
|
||||
|
||||
// Equal lengths, difference in the middle
|
||||
CHECK(StringView(TEXT("abcx")).Compare(StringView(TEXT("abdx")), StringSearchCase::CaseSensitive) < 0);
|
||||
CHECK(StringView(TEXT("abdx")).Compare(StringView(TEXT("abcx")), StringSearchCase::CaseSensitive) > 0);
|
||||
|
||||
// Different lengths, same prefix
|
||||
CHECK(StringView(TEXT("abcxx")).Compare(StringView(TEXT("abc")), StringSearchCase::CaseSensitive) > 0);
|
||||
CHECK(StringView(TEXT("abc")).Compare(StringView(TEXT("abcxx")), StringSearchCase::CaseSensitive) < 0);
|
||||
|
||||
// Different lengths, different prefix
|
||||
CHECK(StringView(TEXT("abcx")).Compare(StringView(TEXT("abd")), StringSearchCase::CaseSensitive) < 0);
|
||||
CHECK(StringView(TEXT("abd")).Compare(StringView(TEXT("abcx")), StringSearchCase::CaseSensitive) > 0);
|
||||
CHECK(StringView(TEXT("abc")).Compare(StringView(TEXT("abdx")), StringSearchCase::CaseSensitive) < 0);
|
||||
CHECK(StringView(TEXT("abdx")).Compare(StringView(TEXT("abc")), StringSearchCase::CaseSensitive) > 0);
|
||||
|
||||
// Case differences
|
||||
CHECK(StringView(TEXT("a")).Compare(StringView(TEXT("A")), StringSearchCase::CaseSensitive) > 0);
|
||||
CHECK(StringView(TEXT("A")).Compare(StringView(TEXT("a")), StringSearchCase::CaseSensitive) < 0);
|
||||
}
|
||||
|
||||
SECTION("ignore case") {
|
||||
//Null string views
|
||||
CHECK(StringView().Compare(StringView(), StringSearchCase::IgnoreCase) == 0);
|
||||
CHECK(StringView().Compare(StringView(TEXT("xxx")), StringSearchCase::IgnoreCase) < 0);
|
||||
CHECK(StringView(TEXT("xxx")).Compare(StringView(), StringSearchCase::IgnoreCase) > 0);
|
||||
|
||||
// Empty strings
|
||||
CHECK(StringView(TEXT("")).Compare(StringView(TEXT("")), StringSearchCase::IgnoreCase) == 0);
|
||||
CHECK(StringView(TEXT("")).Compare(StringView(TEXT("xxx")), StringSearchCase::IgnoreCase) < 0);
|
||||
CHECK(StringView(TEXT("xxx")).Compare(StringView(TEXT("")), StringSearchCase::IgnoreCase) > 0);
|
||||
|
||||
// Equal lengths, difference at end
|
||||
CHECK(StringView(TEXT("xxx")).Compare(StringView(TEXT("xxx")), StringSearchCase::IgnoreCase) == 0);
|
||||
CHECK(StringView(TEXT("abc")).Compare(StringView(TEXT("abd")), StringSearchCase::IgnoreCase) < 0);
|
||||
CHECK(StringView(TEXT("abd")).Compare(StringView(TEXT("abc")), StringSearchCase::IgnoreCase) > 0);
|
||||
|
||||
// Equal lengths, difference in the middle
|
||||
CHECK(StringView(TEXT("abcx")).Compare(StringView(TEXT("abdx")), StringSearchCase::IgnoreCase) < 0);
|
||||
CHECK(StringView(TEXT("abdx")).Compare(StringView(TEXT("abcx")), StringSearchCase::IgnoreCase) > 0);
|
||||
|
||||
// Different lengths, same prefix
|
||||
CHECK(StringView(TEXT("abcxx")).Compare(StringView(TEXT("abc")), StringSearchCase::IgnoreCase) > 0);
|
||||
CHECK(StringView(TEXT("abc")).Compare(StringView(TEXT("abcxx")), StringSearchCase::IgnoreCase) < 0);
|
||||
|
||||
// Different lengths, different prefix
|
||||
CHECK(StringView(TEXT("abcx")).Compare(StringView(TEXT("abd")), StringSearchCase::IgnoreCase) < 0);
|
||||
CHECK(StringView(TEXT("abd")).Compare(StringView(TEXT("abcx")), StringSearchCase::IgnoreCase) > 0);
|
||||
CHECK(StringView(TEXT("abc")).Compare(StringView(TEXT("abdx")), StringSearchCase::IgnoreCase) < 0);
|
||||
CHECK(StringView(TEXT("abdx")).Compare(StringView(TEXT("abc")), StringSearchCase::IgnoreCase) > 0);
|
||||
|
||||
// Case differences
|
||||
CHECK(StringView(TEXT("a")).Compare(StringView(TEXT("A")), StringSearchCase::IgnoreCase) == 0);
|
||||
CHECK(StringView(TEXT("A")).Compare(StringView(TEXT("a")), StringSearchCase::IgnoreCase) == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
17
Source/Engine/TestsMain/TestsMain.Build.cs
Normal file
17
Source/Engine/TestsMain/TestsMain.Build.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Flax.Build;
|
||||
using Flax.Build.NativeCpp;
|
||||
|
||||
/// <summary>
|
||||
/// Application startup module.
|
||||
/// </summary>
|
||||
public class TestsMain : EngineModule
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void GetFilesToDeploy(List<string> files)
|
||||
{
|
||||
}
|
||||
}
|
||||
14
Source/Engine/TestsMain/TestsMain.cpp
Normal file
14
Source/Engine/TestsMain/TestsMain.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if PLATFORM_WINDOWS || PLATFORM_LINUX
|
||||
|
||||
#define CATCH_CONFIG_RUNNER
|
||||
#include <ThirdParty/catch2/catch.hpp>
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int result = Catch::Session().run(argc, argv);
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
63
Source/FlaxNativeTests.Build.cs
Normal file
63
Source/FlaxNativeTests.Build.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Flax.Build;
|
||||
using Flax.Build.NativeCpp;
|
||||
|
||||
/// <summary>
|
||||
/// Target that builds standalone, native tests.
|
||||
/// </summary>
|
||||
public class FlaxNativeTestsTarget : EngineTarget
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
|
||||
// Initialize
|
||||
OutputName = "FlaxNativeTests";
|
||||
ConfigurationName = "Tests";
|
||||
|
||||
// TODO: All platforms would benefit from the tests.
|
||||
Platforms = new[]
|
||||
{
|
||||
TargetPlatform.Windows,
|
||||
TargetPlatform.Linux,
|
||||
};
|
||||
Architectures = new[]
|
||||
{
|
||||
TargetArchitecture.x64,
|
||||
TargetArchitecture.x86,
|
||||
};
|
||||
|
||||
Modules.Remove("Main");
|
||||
Modules.Add("TestsMain");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void SetupTargetEnvironment(BuildOptions options)
|
||||
{
|
||||
base.SetupTargetEnvironment(options);
|
||||
|
||||
options.LinkEnv.LinkAsConsoleProgram = true;
|
||||
|
||||
// Setup output folder for Test binaries
|
||||
var platformName = options.Platform.Target.ToString();
|
||||
var architectureName = options.Architecture.ToString();
|
||||
var configurationName = options.Configuration.ToString();
|
||||
options.OutputFolder = Path.Combine(options.WorkingDirectory, "Binaries", "Tests", platformName, architectureName, configurationName);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Target SelectReferencedTarget(ProjectInfo project, Target[] projectTargets)
|
||||
{
|
||||
var testTargetName = "FlaxNativeTests"; // Should this be added to .flaxproj, similarly as "GameTarget" and "EditorTarget"?
|
||||
var result = projectTargets.FirstOrDefault(x => x.Name == testTargetName);
|
||||
if (result == null)
|
||||
// Apparently .NET compiler that is used for building .Build.cs files is different that one used for building Flax.Build itself. String interpolation ($) does't work here.
|
||||
throw new Exception(string.Format("Invalid or missing test target {0} specified in project {1} (referenced by project {2}).", testTargetName, project.Name, Project.Name));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
23
Source/ThirdParty/catch2/LICENSE.txt
vendored
Normal file
23
Source/ThirdParty/catch2/LICENSE.txt
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
17959
Source/ThirdParty/catch2/catch.hpp
vendored
Normal file
17959
Source/ThirdParty/catch2/catch.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
20
Source/ThirdParty/catch2/catch2.Build.cs
vendored
Normal file
20
Source/ThirdParty/catch2/catch2.Build.cs
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Flax.Build;
|
||||
|
||||
/// <summary>
|
||||
/// https://github.com/catchorg/Catch2
|
||||
/// </summary>
|
||||
public class catch2 : HeaderOnlyModule
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
|
||||
LicenseType = LicenseTypes.BoostSoftwareLicense;
|
||||
LicenseFilePath = "LICENSE.txt";
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -76,6 +76,11 @@ namespace Flax.Build.NativeCpp
|
||||
/// </summary>
|
||||
public bool GenerateWindowsMetadata = false;
|
||||
|
||||
/// <summary>
|
||||
/// Use CONSOLE subsystem on Windows instead of the WINDOWS one.
|
||||
/// </summary>
|
||||
public bool LinkAsConsoleProgram = false;
|
||||
|
||||
/// <summary>
|
||||
/// Enables documentation generation.
|
||||
/// </summary>
|
||||
@@ -114,6 +119,7 @@ namespace Flax.Build.NativeCpp
|
||||
LinkTimeCodeGeneration = LinkTimeCodeGeneration,
|
||||
UseIncrementalLinking = UseIncrementalLinking,
|
||||
GenerateWindowsMetadata = GenerateWindowsMetadata,
|
||||
LinkAsConsoleProgram = LinkAsConsoleProgram,
|
||||
GenerateDocumentation = GenerateDocumentation
|
||||
};
|
||||
foreach (var e in InputFiles)
|
||||
|
||||
@@ -675,7 +675,14 @@ namespace Flax.Build.Platforms
|
||||
}
|
||||
|
||||
// Specify subsystem
|
||||
args.Add("/SUBSYSTEM:WINDOWS");
|
||||
if (linkEnvironment.LinkAsConsoleProgram)
|
||||
{
|
||||
args.Add("/SUBSYSTEM:CONSOLE");
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Add("/SUBSYSTEM:WINDOWS");
|
||||
}
|
||||
|
||||
// Generate Windows Metadata
|
||||
if (linkEnvironment.GenerateWindowsMetadata)
|
||||
|
||||
Reference in New Issue
Block a user