Files
test/source/blender/blenlib/tests/BLI_string_test.cc
Campbell Barton e955c94ed3 License Headers: Set copyright to "Blender Authors", add AUTHORS
Listing the "Blender Foundation" as copyright holder implied the Blender
Foundation holds copyright to files which may include work from many
developers.

While keeping copyright on headers makes sense for isolated libraries,
Blender's own code may be refactored or moved between files in a way
that makes the per file copyright holders less meaningful.

Copyright references to the "Blender Foundation" have been replaced with
"Blender Authors", with the exception of `./extern/` since these this
contains libraries which are more isolated, any changed to license
headers there can be handled on a case-by-case basis.

Some directories in `./intern/` have also been excluded:

- `./intern/cycles/` it's own `AUTHORS` file is planned.
- `./intern/opensubdiv/`.

An "AUTHORS" file has been added, using the chromium projects authors
file as a template.

Design task: #110784

Ref !110783.
2023-08-16 00:20:26 +10:00

1479 lines
44 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: Apache-2.0 */
#include "testing/testing.h"
#include <array>
#include <initializer_list>
#include <ostream> /* NOLINT */
#include <string>
#include <utility>
#include <vector>
#include "MEM_guardedalloc.h"
#include "BLI_string.h"
#include "BLI_string_utf8.h"
#include "BLI_string_utils.h"
#include "BLI_utildefines.h"
using std::initializer_list;
using std::pair;
using std::string;
using std::vector;
/* -------------------------------------------------------------------- */
/** \name String Copy (UTF8)
* \{ */
TEST(string, StrCopyUTF8_ASCII)
{
#define STRNCPY_UTF8_ASCII(...) \
{ \
const char src[] = {__VA_ARGS__, 0}; \
char dst[sizeof(src)]; \
memset(dst, 0xff, sizeof(dst)); \
STRNCPY_UTF8(dst, src); \
EXPECT_EQ(strlen(dst), sizeof(dst) - 1); \
EXPECT_STREQ(dst, src); \
}
STRNCPY_UTF8_ASCII('a');
STRNCPY_UTF8_ASCII('a', 'b', 'c');
#undef STRNCPY_UTF8_ASCII
}
TEST(string, StrCopyUTF8_ASCII_Truncate)
{
#define STRNCPY_UTF8_ASCII_TRUNCATE(maxncpy, ...) \
{ \
char src[] = {__VA_ARGS__}; \
char dst[sizeof(src)]; \
memset(dst, 0xff, sizeof(dst)); \
BLI_strncpy_utf8(dst, src, maxncpy); \
int len_expect = MIN2(sizeof(src), maxncpy) - 1; \
src[len_expect] = '\0'; /* To be able to use `EXPECT_STREQ`. */ \
EXPECT_EQ(strlen(dst), len_expect); \
EXPECT_STREQ(dst, src); \
}
STRNCPY_UTF8_ASCII_TRUNCATE(1, '\0');
STRNCPY_UTF8_ASCII_TRUNCATE(3, 'A', 'A', 'A', 'A');
#undef STRNCPY_UTF8_ASCII_TRUNCATE
}
TEST(string, StrCopyUTF8_TruncateEncoding)
{
/* Ensure copying one byte less than the code-point results in it being ignored entirely. */
#define STRNCPY_UTF8_TRUNCATE(byte_size, ...) \
{ \
const char src[] = {__VA_ARGS__, 0}; \
EXPECT_EQ(BLI_str_utf8_size(src), byte_size); \
char dst[sizeof(src)]; \
memset(dst, 0xff, sizeof(dst)); \
STRNCPY_UTF8(dst, src); \
EXPECT_EQ(strlen(dst), sizeof(dst) - 1); \
EXPECT_STREQ(dst, src); \
BLI_strncpy_utf8(dst, src, sizeof(dst) - 1); \
EXPECT_STREQ(dst, ""); \
}
STRNCPY_UTF8_TRUNCATE(6, 252, 1, 1, 1, 1, 1);
STRNCPY_UTF8_TRUNCATE(5, 248, 1, 1, 1, 1);
STRNCPY_UTF8_TRUNCATE(4, 240, 1, 1, 1);
STRNCPY_UTF8_TRUNCATE(3, 224, 1, 1);
STRNCPY_UTF8_TRUNCATE(2, 192, 1);
STRNCPY_UTF8_TRUNCATE(1, 96);
#undef STRNCPY_UTF8_TRUNCATE
}
TEST(string, StrCopyUTF8_TerminateEncodingEarly)
{
/* A UTF8 sequence that has a null byte before the sequence ends.
* Ensure the UTF8 sequence does not step over the null byte. */
#define STRNCPY_UTF8_TERMINATE_EARLY(byte_size, ...) \
{ \
char src[] = {__VA_ARGS__, 0}; \
EXPECT_EQ(BLI_str_utf8_size(src), byte_size); \
char dst[sizeof(src)]; \
memset(dst, 0xff, sizeof(dst)); \
STRNCPY_UTF8(dst, src); \
EXPECT_EQ(strlen(dst), sizeof(dst) - 1); \
EXPECT_STREQ(dst, src); \
for (int i = sizeof(dst) - 1; i > 1; i--) { \
src[i] = '\0'; \
memset(dst, 0xff, sizeof(dst)); \
const int dst_copied = STRNCPY_UTF8_RLEN(dst, src); \
EXPECT_STREQ(dst, src); \
EXPECT_EQ(strlen(dst), i); \
EXPECT_EQ(dst_copied, i); \
} \
}
STRNCPY_UTF8_TERMINATE_EARLY(6, 252, 1, 1, 1, 1, 1);
STRNCPY_UTF8_TERMINATE_EARLY(5, 248, 1, 1, 1, 1);
STRNCPY_UTF8_TERMINATE_EARLY(4, 240, 1, 1, 1);
STRNCPY_UTF8_TERMINATE_EARLY(3, 224, 1, 1);
STRNCPY_UTF8_TERMINATE_EARLY(2, 192, 1);
STRNCPY_UTF8_TERMINATE_EARLY(1, 96);
#undef STRNCPY_UTF8_TERMINATE_EARLY
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name String Concatenate
* \{ */
TEST(string, StrCat)
{
#define STR_N_CAT(dst_init, dst_size, src, result_expect) \
{ \
char dst[dst_size + 1] = dst_init; \
dst[dst_size] = 0xff; \
BLI_strncat(dst, src, dst_size); \
EXPECT_STREQ(dst, result_expect); \
EXPECT_EQ(dst[dst_size], 0xff); \
}
STR_N_CAT("", 1, "", "");
STR_N_CAT("", 1, "Y", "");
STR_N_CAT("", 2, "Y", "Y");
STR_N_CAT("", 2, "YZ", "Y");
STR_N_CAT("X", 2, "YZ", "X");
STR_N_CAT("ABC", 4, "XYZ", "ABC");
STR_N_CAT("ABC", 7, "XYZ", "ABCXYZ");
#undef STR_N_CAT
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name String Replace
* \{ */
TEST(string, StrReplaceRange)
{
#define STR_REPLACE_RANGE(src, size, beg, end, dst, result_expect) \
{ \
char string[size] = src; \
BLI_string_replace_range(string, sizeof(string), beg, end, dst); \
EXPECT_STREQ(string, result_expect); \
}
STR_REPLACE_RANGE("a ", 5, 2, 2, "b!", "a b!");
STR_REPLACE_RANGE("a ", 4, 2, 2, "b!", "a b");
STR_REPLACE_RANGE("a ", 5, 1, 2, "b!", "ab!");
STR_REPLACE_RANGE("XYZ", 5, 1, 1, "A", "XAYZ");
STR_REPLACE_RANGE("XYZ", 5, 1, 1, "AB", "XABY");
STR_REPLACE_RANGE("XYZ", 5, 1, 1, "ABC", "XABC");
/* Add at the end when there is no room (no-op). */
STR_REPLACE_RANGE("XYZA", 5, 4, 4, "?", "XYZA");
/* Add at the start, replace all contents. */
STR_REPLACE_RANGE("XYZ", 4, 0, 0, "ABC", "ABC");
STR_REPLACE_RANGE("XYZ", 7, 0, 0, "ABC", "ABCXYZ");
/* Only remove. */
STR_REPLACE_RANGE("XYZ", 4, 1, 3, "", "X");
STR_REPLACE_RANGE("XYZ", 4, 0, 2, "", "Z");
STR_REPLACE_RANGE("XYZ", 4, 0, 3, "", "");
/* Only Add. */
STR_REPLACE_RANGE("", 4, 0, 0, "XYZ", "XYZ");
STR_REPLACE_RANGE("", 4, 0, 0, "XYZ?", "XYZ");
/* Do nothing. */
STR_REPLACE_RANGE("", 1, 0, 0, "?", "");
STR_REPLACE_RANGE("", 1, 0, 0, "", "");
#undef STR_REPLACE_RANGE
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name String Partition
* \{ */
/* BLI_str_partition */
TEST(string, StrPartition)
{
const char delim[] = {'-', '.', '_', '~', '\\', '\0'};
const char *sep, *suf;
size_t pre_len;
{
const char *str = "mat.e-r_ial";
/* "mat.e-r_ial" -> "mat", '.', "e-r_ial", 3 */
pre_len = BLI_str_partition(str, delim, &sep, &suf);
EXPECT_EQ(pre_len, 3);
EXPECT_EQ(&str[3], sep);
EXPECT_STREQ("e-r_ial", suf);
}
/* Corner cases. */
{
const char *str = ".mate-rial--";
/* ".mate-rial--" -> "", '.', "mate-rial--", 0 */
pre_len = BLI_str_partition(str, delim, &sep, &suf);
EXPECT_EQ(pre_len, 0);
EXPECT_EQ(&str[0], sep);
EXPECT_STREQ("mate-rial--", suf);
}
{
const char *str = ".__.--_";
/* ".__.--_" -> "", '.', "__.--_", 0 */
pre_len = BLI_str_partition(str, delim, &sep, &suf);
EXPECT_EQ(pre_len, 0);
EXPECT_EQ(&str[0], sep);
EXPECT_STREQ("__.--_", suf);
}
{
const char *str = "";
/* "" -> "", nullptr, nullptr, 0 */
pre_len = BLI_str_partition(str, delim, &sep, &suf);
EXPECT_EQ(pre_len, 0);
EXPECT_EQ(sep, (void *)nullptr);
EXPECT_EQ(suf, (void *)nullptr);
}
{
const char *str = "material";
/* "material" -> "material", nullptr, nullptr, 8 */
pre_len = BLI_str_partition(str, delim, &sep, &suf);
EXPECT_EQ(pre_len, 8);
EXPECT_EQ(sep, (void *)nullptr);
EXPECT_EQ(suf, (void *)nullptr);
}
}
/* BLI_str_rpartition */
TEST(string, StrRPartition)
{
const char delim[] = {'-', '.', '_', '~', '\\', '\0'};
const char *sep, *suf;
size_t pre_len;
{
const char *str = "mat.e-r_ial";
/* "mat.e-r_ial" -> "mat.e-r", '_', "ial", 7 */
pre_len = BLI_str_rpartition(str, delim, &sep, &suf);
EXPECT_EQ(pre_len, 7);
EXPECT_EQ(&str[7], sep);
EXPECT_STREQ("ial", suf);
}
/* Corner cases. */
{
const char *str = ".mate-rial--";
/* ".mate-rial--" -> ".mate-rial-", '-', "", 11 */
pre_len = BLI_str_rpartition(str, delim, &sep, &suf);
EXPECT_EQ(pre_len, 11);
EXPECT_EQ(&str[11], sep);
EXPECT_STREQ("", suf);
}
{
const char *str = ".__.--_";
/* ".__.--_" -> ".__.--", '_', "", 6 */
pre_len = BLI_str_rpartition(str, delim, &sep, &suf);
EXPECT_EQ(pre_len, 6);
EXPECT_EQ(&str[6], sep);
EXPECT_STREQ("", suf);
}
{
const char *str = "";
/* "" -> "", nullptr, nullptr, 0 */
pre_len = BLI_str_rpartition(str, delim, &sep, &suf);
EXPECT_EQ(pre_len, 0);
EXPECT_EQ(sep, (void *)nullptr);
EXPECT_EQ(suf, (void *)nullptr);
}
{
const char *str = "material";
/* "material" -> "material", nullptr, nullptr, 8 */
pre_len = BLI_str_rpartition(str, delim, &sep, &suf);
EXPECT_EQ(pre_len, 8);
EXPECT_EQ(sep, (void *)nullptr);
EXPECT_EQ(suf, (void *)nullptr);
}
}
/* BLI_str_partition_ex */
TEST(string, StrPartitionEx)
{
const char delim[] = {'-', '.', '_', '~', '\\', '\0'};
const char *sep, *suf;
size_t pre_len;
/* Only considering 'from_right' cases here. */
{
const char *str = "mat.e-r_ia.l";
/* "mat.e-r_ia.l" over "mat.e-r" -> "mat.e", '.', "r_ia.l", 3 */
pre_len = BLI_str_partition_ex(str, str + 6, delim, &sep, &suf, true);
EXPECT_EQ(pre_len, 5);
EXPECT_EQ(&str[5], sep);
EXPECT_STREQ("r_ia.l", suf);
}
/* Corner cases. */
{
const char *str = "mate.rial";
/* "mate.rial" over "mate" -> "mate.rial", nullptr, nullptr, 4 */
pre_len = BLI_str_partition_ex(str, str + 4, delim, &sep, &suf, true);
EXPECT_EQ(pre_len, 4);
EXPECT_EQ(sep, (void *)nullptr);
EXPECT_EQ(suf, (void *)nullptr);
}
}
/* BLI_str_partition_utf8 */
TEST(string, StrPartitionUtf8)
{
const uint delim[] = {'-', '.', '_', 0x00F1 /* n tilde */, 0x262F /* ying-yang */, '\0'};
const char *sep, *suf;
size_t pre_len;
{
const char *str = "ma\xc3\xb1te-r\xe2\x98\xafial";
/* "ma\xc3\xb1te-r\xe2\x98\xafial" -> "ma", '\xc3\xb1', "te-r\xe2\x98\xafial", 2 */
pre_len = BLI_str_partition_utf8(str, delim, &sep, &suf);
EXPECT_EQ(pre_len, 2);
EXPECT_EQ(&str[2], sep);
EXPECT_STREQ("te-r\xe2\x98\xafial", suf);
}
/* Corner cases. */
{
const char *str = "\xe2\x98\xafmate-rial-\xc3\xb1";
/* "\xe2\x98\xafmate-rial-\xc3\xb1" -> "", '\xe2\x98\xaf', "mate-rial-\xc3\xb1", 0 */
pre_len = BLI_str_partition_utf8(str, delim, &sep, &suf);
EXPECT_EQ(pre_len, 0);
EXPECT_EQ(&str[0], sep);
EXPECT_STREQ("mate-rial-\xc3\xb1", suf);
}
{
const char *str = "\xe2\x98\xaf.\xc3\xb1_.--\xc3\xb1";
/* "\xe2\x98\xaf.\xc3\xb1_.--\xc3\xb1" -> "", '\xe2\x98\xaf', ".\xc3\xb1_.--\xc3\xb1", 0 */
pre_len = BLI_str_partition_utf8(str, delim, &sep, &suf);
EXPECT_EQ(pre_len, 0);
EXPECT_EQ(&str[0], sep);
EXPECT_STREQ(".\xc3\xb1_.--\xc3\xb1", suf);
}
{
const char *str = "";
/* "" -> "", nullptr, nullptr, 0 */
pre_len = BLI_str_partition_utf8(str, delim, &sep, &suf);
EXPECT_EQ(pre_len, 0);
EXPECT_EQ(sep, (void *)nullptr);
EXPECT_EQ(suf, (void *)nullptr);
}
{
const char *str = "material";
/* "material" -> "material", nullptr, nullptr, 8 */
pre_len = BLI_str_partition_utf8(str, delim, &sep, &suf);
EXPECT_EQ(pre_len, 8);
EXPECT_EQ(sep, (void *)nullptr);
EXPECT_EQ(suf, (void *)nullptr);
}
}
/* BLI_str_rpartition_utf8 */
TEST(string, StrRPartitionUtf8)
{
const uint delim[] = {'-', '.', '_', 0x00F1 /* n tilde */, 0x262F /* ying-yang */, '\0'};
const char *sep, *suf;
size_t pre_len;
{
const char *str = "ma\xc3\xb1te-r\xe2\x98\xafial";
/* "ma\xc3\xb1te-r\xe2\x98\xafial" -> "mat\xc3\xb1te-r", '\xe2\x98\xaf', "ial", 8 */
pre_len = BLI_str_rpartition_utf8(str, delim, &sep, &suf);
EXPECT_EQ(pre_len, 8);
EXPECT_EQ(&str[8], sep);
EXPECT_STREQ("ial", suf);
}
/* Corner cases. */
{
const char *str = "\xe2\x98\xafmate-rial-\xc3\xb1";
/* "\xe2\x98\xafmate-rial-\xc3\xb1" -> "\xe2\x98\xafmate-rial-", '\xc3\xb1', "", 13 */
pre_len = BLI_str_rpartition_utf8(str, delim, &sep, &suf);
EXPECT_EQ(pre_len, 13);
EXPECT_EQ(&str[13], sep);
EXPECT_STREQ("", suf);
}
{
const char *str = "\xe2\x98\xaf.\xc3\xb1_.--\xc3\xb1";
/* "\xe2\x98\xaf.\xc3\xb1_.--\xc3\xb1" -> "\xe2\x98\xaf.\xc3\xb1_.--", '\xc3\xb1', "", 10 */
pre_len = BLI_str_rpartition_utf8(str, delim, &sep, &suf);
EXPECT_EQ(pre_len, 10);
EXPECT_EQ(&str[10], sep);
EXPECT_STREQ("", suf);
}
{
const char *str = "";
/* "" -> "", nullptr, nullptr, 0 */
pre_len = BLI_str_rpartition_utf8(str, delim, &sep, &suf);
EXPECT_EQ(pre_len, 0);
EXPECT_EQ(sep, (void *)nullptr);
EXPECT_EQ(suf, (void *)nullptr);
}
{
const char *str = "material";
/* "material" -> "material", nullptr, nullptr, 8 */
pre_len = BLI_str_rpartition_utf8(str, delim, &sep, &suf);
EXPECT_EQ(pre_len, 8);
EXPECT_EQ(sep, (void *)nullptr);
EXPECT_EQ(suf, (void *)nullptr);
}
}
/* BLI_str_partition_ex_utf8 */
TEST(string, StrPartitionExUtf8)
{
const uint delim[] = {'-', '.', '_', 0x00F1 /* n tilde */, 0x262F /* ying-yang */, '\0'};
const char *sep, *suf;
size_t pre_len;
/* Only considering 'from_right' cases here. */
{
const char *str = "ma\xc3\xb1te-r\xe2\x98\xafial";
/* "ma\xc3\xb1te-r\xe2\x98\xafial" over
* "ma\xc3\xb1te" -> "ma", '\xc3\xb1', "te-r\xe2\x98\xafial", 2 */
pre_len = BLI_str_partition_ex_utf8(str, str + 6, delim, &sep, &suf, true);
EXPECT_EQ(pre_len, 2);
EXPECT_EQ(&str[2], sep);
EXPECT_STREQ("te-r\xe2\x98\xafial", suf);
}
/* Corner cases. */
{
const char *str = "mate\xe2\x98\xafrial";
/* "mate\xe2\x98\xafrial" over "mate" -> "mate\xe2\x98\xafrial", nullptr, nullptr, 4 */
pre_len = BLI_str_partition_ex_utf8(str, str + 4, delim, &sep, &suf, true);
EXPECT_EQ(pre_len, 4);
EXPECT_EQ(sep, (void *)nullptr);
EXPECT_EQ(suf, (void *)nullptr);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name String Format Integer (Grouped)
* \{ */
/* BLI_str_format_int_grouped */
TEST(string, StrFormatIntGrouped)
{
char number_str[BLI_STR_FORMAT_INT32_GROUPED_SIZE];
int number;
BLI_str_format_int_grouped(number_str, number = 0);
EXPECT_STREQ("0", number_str);
BLI_str_format_int_grouped(number_str, number = 1);
EXPECT_STREQ("1", number_str);
BLI_str_format_int_grouped(number_str, number = -1);
EXPECT_STREQ("-1", number_str);
BLI_str_format_int_grouped(number_str, number = 1000);
EXPECT_STREQ("1,000", number_str);
BLI_str_format_int_grouped(number_str, number = -1000);
EXPECT_STREQ("-1,000", number_str);
BLI_str_format_int_grouped(number_str, number = 999);
EXPECT_STREQ("999", number_str);
BLI_str_format_int_grouped(number_str, number = -999);
EXPECT_STREQ("-999", number_str);
BLI_str_format_int_grouped(number_str, number = 2147483647);
EXPECT_STREQ("2,147,483,647", number_str);
BLI_str_format_int_grouped(number_str, number = -2147483648);
EXPECT_STREQ("-2,147,483,648", number_str);
/* Ensure the limit is correct. */
EXPECT_EQ(sizeof(number_str), strlen(number_str) + 1);
}
/* BLI_str_format_uint64_grouped */
TEST(string, StrFormatUint64Grouped)
{
char number_str[BLI_STR_FORMAT_UINT64_GROUPED_SIZE];
uint64_t number;
BLI_str_format_uint64_grouped(number_str, number = 0);
EXPECT_STREQ("0", number_str);
BLI_str_format_uint64_grouped(number_str, number = 1);
EXPECT_STREQ("1", number_str);
BLI_str_format_uint64_grouped(number_str, number = 999);
EXPECT_STREQ("999", number_str);
BLI_str_format_uint64_grouped(number_str, number = 1000);
EXPECT_STREQ("1,000", number_str);
BLI_str_format_uint64_grouped(number_str, number = 18446744073709551615u);
EXPECT_STREQ("18,446,744,073,709,551,615", number_str);
/* Ensure the limit is correct. */
EXPECT_EQ(sizeof(number_str), strlen(number_str) + 1);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name String Format Byte Units
* \{ */
/* BLI_str_format_byte_unit */
TEST(string, StrFormatByteUnits)
{
char size_str[BLI_STR_FORMAT_INT64_BYTE_UNIT_SIZE];
long long int size;
/* Base 10 */
BLI_str_format_byte_unit(size_str, size = 0, true);
EXPECT_STREQ("0 B", size_str);
BLI_str_format_byte_unit(size_str, size = -0, true);
EXPECT_STREQ("0 B", size_str);
BLI_str_format_byte_unit(size_str, size = 1, true);
EXPECT_STREQ("1 B", size_str);
BLI_str_format_byte_unit(size_str, size = -1, true);
EXPECT_STREQ("-1 B", size_str);
BLI_str_format_byte_unit(size_str, size = 1000, true);
EXPECT_STREQ("1 KB", size_str);
BLI_str_format_byte_unit(size_str, size = -1000, true);
EXPECT_STREQ("-1 KB", size_str);
BLI_str_format_byte_unit(size_str, size = 1024, true);
EXPECT_STREQ("1 KB", size_str);
BLI_str_format_byte_unit(size_str, size = -1024, true);
EXPECT_STREQ("-1 KB", size_str);
/* LLONG_MAX - largest possible value */
BLI_str_format_byte_unit(size_str, size = 9223372036854775807, true);
EXPECT_STREQ("9223.372 PB", size_str);
BLI_str_format_byte_unit(size_str, size = -9223372036854775807, true);
EXPECT_STREQ("-9223.372 PB", size_str);
/* Base 2 */
BLI_str_format_byte_unit(size_str, size = 0, false);
EXPECT_STREQ("0 B", size_str);
BLI_str_format_byte_unit(size_str, size = -0, false);
EXPECT_STREQ("0 B", size_str);
BLI_str_format_byte_unit(size_str, size = 1, false);
EXPECT_STREQ("1 B", size_str);
BLI_str_format_byte_unit(size_str, size = -1, false);
EXPECT_STREQ("-1 B", size_str);
BLI_str_format_byte_unit(size_str, size = 1000, false);
EXPECT_STREQ("1000 B", size_str);
BLI_str_format_byte_unit(size_str, size = -1000, false);
EXPECT_STREQ("-1000 B", size_str);
BLI_str_format_byte_unit(size_str, size = 1024, false);
EXPECT_STREQ("1 KiB", size_str);
BLI_str_format_byte_unit(size_str, size = -1024, false);
EXPECT_STREQ("-1 KiB", size_str);
/* LLONG_MAX - largest possible value */
BLI_str_format_byte_unit(size_str, size = 9223372036854775807, false);
EXPECT_STREQ("8192.0 PiB", size_str);
BLI_str_format_byte_unit(size_str, size = -9223372036854775807, false);
EXPECT_STREQ("-8192.0 PiB", size_str);
/* Test maximum string length. */
BLI_str_format_byte_unit(size_str, size = -9223200000000000000, false);
EXPECT_STREQ("-8191.8472 PiB", size_str);
/* Ensure the limit is correct. */
EXPECT_EQ(sizeof(size_str), strlen(size_str) + 1);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name String Format Decimal Units
* \{ */
/* BLI_str_format_decimal_unit */
TEST(string, StrFormatDecimalUnits)
{
char size_str[BLI_STR_FORMAT_INT32_DECIMAL_UNIT_SIZE];
int size;
BLI_str_format_decimal_unit(size_str, size = 0);
EXPECT_STREQ("0", size_str);
BLI_str_format_decimal_unit(size_str, size = 1);
EXPECT_STREQ("1", size_str);
BLI_str_format_decimal_unit(size_str, size = 10);
EXPECT_STREQ("10", size_str);
BLI_str_format_decimal_unit(size_str, size = 15);
EXPECT_STREQ("15", size_str);
BLI_str_format_decimal_unit(size_str, size = 100);
EXPECT_STREQ("100", size_str);
BLI_str_format_decimal_unit(size_str, size = 155);
EXPECT_STREQ("155", size_str);
BLI_str_format_decimal_unit(size_str, size = 1000);
EXPECT_STREQ("1.0K", size_str);
BLI_str_format_decimal_unit(size_str, size = 1555);
EXPECT_STREQ("1.6K", size_str);
BLI_str_format_decimal_unit(size_str, size = 10000);
EXPECT_STREQ("10.0K", size_str);
BLI_str_format_decimal_unit(size_str, size = 15555);
EXPECT_STREQ("15.6K", size_str);
BLI_str_format_decimal_unit(size_str, size = 100000);
EXPECT_STREQ("100K", size_str);
BLI_str_format_decimal_unit(size_str, size = 100000);
EXPECT_STREQ("100K", size_str);
BLI_str_format_decimal_unit(size_str, size = 155555);
EXPECT_STREQ("156K", size_str);
BLI_str_format_decimal_unit(size_str, size = 1000000);
EXPECT_STREQ("1.0M", size_str);
BLI_str_format_decimal_unit(size_str, size = 1555555);
EXPECT_STREQ("1.6M", size_str);
BLI_str_format_decimal_unit(size_str, size = 10000000);
EXPECT_STREQ("10.0M", size_str);
BLI_str_format_decimal_unit(size_str, size = 15555555);
EXPECT_STREQ("15.6M", size_str);
BLI_str_format_decimal_unit(size_str, size = 100000000);
EXPECT_STREQ("100M", size_str);
BLI_str_format_decimal_unit(size_str, size = 155555555);
EXPECT_STREQ("156M", size_str);
BLI_str_format_decimal_unit(size_str, size = 1000000000);
EXPECT_STREQ("1.0B", size_str);
/* Largest possible value. */
BLI_str_format_decimal_unit(size_str, size = INT32_MAX);
EXPECT_STREQ("2.1B", size_str);
BLI_str_format_decimal_unit(size_str, size = -0);
EXPECT_STREQ("0", size_str);
BLI_str_format_decimal_unit(size_str, size = -1);
EXPECT_STREQ("-1", size_str);
BLI_str_format_decimal_unit(size_str, size = -10);
EXPECT_STREQ("-10", size_str);
BLI_str_format_decimal_unit(size_str, size = -15);
EXPECT_STREQ("-15", size_str);
BLI_str_format_decimal_unit(size_str, size = -100);
EXPECT_STREQ("-100", size_str);
BLI_str_format_decimal_unit(size_str, size = -155);
EXPECT_STREQ("-155", size_str);
BLI_str_format_decimal_unit(size_str, size = -1000);
EXPECT_STREQ("-1.0K", size_str);
BLI_str_format_decimal_unit(size_str, size = -1555);
EXPECT_STREQ("-1.6K", size_str);
BLI_str_format_decimal_unit(size_str, size = -10000);
EXPECT_STREQ("-10.0K", size_str);
BLI_str_format_decimal_unit(size_str, size = -15555);
EXPECT_STREQ("-15.6K", size_str);
BLI_str_format_decimal_unit(size_str, size = -100000);
EXPECT_STREQ("-100K", size_str);
BLI_str_format_decimal_unit(size_str, size = -155555);
EXPECT_STREQ("-156K", size_str);
BLI_str_format_decimal_unit(size_str, size = -1000000);
EXPECT_STREQ("-1.0M", size_str);
BLI_str_format_decimal_unit(size_str, size = -1555555);
EXPECT_STREQ("-1.6M", size_str);
BLI_str_format_decimal_unit(size_str, size = -10000000);
EXPECT_STREQ("-10.0M", size_str);
BLI_str_format_decimal_unit(size_str, size = -15555555);
EXPECT_STREQ("-15.6M", size_str);
BLI_str_format_decimal_unit(size_str, size = -100000000);
EXPECT_STREQ("-100M", size_str);
BLI_str_format_decimal_unit(size_str, size = -155555555);
EXPECT_STREQ("-156M", size_str);
BLI_str_format_decimal_unit(size_str, size = -1000000000);
EXPECT_STREQ("-1.0B", size_str);
/* Smallest possible value. */
BLI_str_format_decimal_unit(size_str, size = -INT32_MAX);
EXPECT_STREQ("-2.1B", size_str);
}
/* BLI_str_format_integer_unit */
TEST(string, StrFormatIntegerUnits)
{
char size_str[BLI_STR_FORMAT_INT32_INTEGER_UNIT_SIZE];
int size;
BLI_str_format_integer_unit(size_str, size = 0);
EXPECT_STREQ("0", size_str);
BLI_str_format_integer_unit(size_str, size = 1);
EXPECT_STREQ("1", size_str);
BLI_str_format_integer_unit(size_str, size = 10);
EXPECT_STREQ("10", size_str);
BLI_str_format_integer_unit(size_str, size = 15);
EXPECT_STREQ("15", size_str);
BLI_str_format_integer_unit(size_str, size = 100);
EXPECT_STREQ("100", size_str);
BLI_str_format_integer_unit(size_str, size = 155);
EXPECT_STREQ("155", size_str);
BLI_str_format_integer_unit(size_str, size = 1000);
EXPECT_STREQ("1K", size_str);
BLI_str_format_integer_unit(size_str, size = 1555);
EXPECT_STREQ("1K", size_str);
BLI_str_format_integer_unit(size_str, size = 10000);
EXPECT_STREQ("10K", size_str);
BLI_str_format_integer_unit(size_str, size = 15555);
EXPECT_STREQ("15K", size_str);
BLI_str_format_integer_unit(size_str, size = 100000);
EXPECT_STREQ(".1M", size_str);
BLI_str_format_integer_unit(size_str, size = 155555);
EXPECT_STREQ(".1M", size_str);
BLI_str_format_integer_unit(size_str, size = 1000000);
EXPECT_STREQ("1M", size_str);
BLI_str_format_integer_unit(size_str, size = 1555555);
EXPECT_STREQ("1M", size_str);
BLI_str_format_integer_unit(size_str, size = 2555555);
EXPECT_STREQ("2M", size_str);
BLI_str_format_integer_unit(size_str, size = 10000000);
EXPECT_STREQ("10M", size_str);
BLI_str_format_integer_unit(size_str, size = 15555555);
EXPECT_STREQ("15M", size_str);
BLI_str_format_integer_unit(size_str, size = 100000000);
EXPECT_STREQ(".1B", size_str);
BLI_str_format_integer_unit(size_str, size = 155555555);
EXPECT_STREQ(".1B", size_str);
BLI_str_format_integer_unit(size_str, size = 255555555);
EXPECT_STREQ(".2B", size_str);
BLI_str_format_integer_unit(size_str, size = 1000000000);
EXPECT_STREQ("1B", size_str);
/* Largest possible value. */
BLI_str_format_integer_unit(size_str, size = INT32_MAX);
EXPECT_STREQ("2B", size_str);
BLI_str_format_integer_unit(size_str, size = -0);
EXPECT_STREQ("0", size_str);
BLI_str_format_integer_unit(size_str, size = -1);
EXPECT_STREQ("-1", size_str);
BLI_str_format_integer_unit(size_str, size = -10);
EXPECT_STREQ("-10", size_str);
BLI_str_format_integer_unit(size_str, size = -15);
EXPECT_STREQ("-15", size_str);
BLI_str_format_integer_unit(size_str, size = -100);
EXPECT_STREQ("-100", size_str);
BLI_str_format_integer_unit(size_str, size = -155);
EXPECT_STREQ("-155", size_str);
BLI_str_format_integer_unit(size_str, size = -1000);
EXPECT_STREQ("-1K", size_str);
BLI_str_format_integer_unit(size_str, size = -1555);
EXPECT_STREQ("-1K", size_str);
BLI_str_format_integer_unit(size_str, size = -10000);
EXPECT_STREQ("-10K", size_str);
BLI_str_format_integer_unit(size_str, size = -15555);
EXPECT_STREQ("-15K", size_str);
BLI_str_format_integer_unit(size_str, size = -100000);
EXPECT_STREQ("-.1M", size_str);
BLI_str_format_integer_unit(size_str, size = -155555);
EXPECT_STREQ("-.1M", size_str);
BLI_str_format_integer_unit(size_str, size = -1000000);
EXPECT_STREQ("-1M", size_str);
BLI_str_format_integer_unit(size_str, size = -1555555);
EXPECT_STREQ("-1M", size_str);
BLI_str_format_integer_unit(size_str, size = -10000000);
EXPECT_STREQ("-10M", size_str);
BLI_str_format_integer_unit(size_str, size = -15555555);
EXPECT_STREQ("-15M", size_str);
BLI_str_format_integer_unit(size_str, size = -100000000);
EXPECT_STREQ("-.1B", size_str);
BLI_str_format_integer_unit(size_str, size = -155555555);
EXPECT_STREQ("-.1B", size_str);
BLI_str_format_integer_unit(size_str, size = -1000000000);
EXPECT_STREQ("-1B", size_str);
/* Smallest possible value. */
BLI_str_format_integer_unit(size_str, size = -INT32_MAX);
EXPECT_STREQ("-2B", size_str);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name String Length (Clamped)
* \{ */
TEST(string, StringNLen)
{
EXPECT_EQ(0, BLI_strnlen("", 0));
EXPECT_EQ(0, BLI_strnlen("", 1));
EXPECT_EQ(0, BLI_strnlen("", 100));
EXPECT_EQ(0, BLI_strnlen("x", 0));
EXPECT_EQ(1, BLI_strnlen("x", 1));
EXPECT_EQ(1, BLI_strnlen("x", 100));
/* `ü` is `\xc3\xbc`. */
EXPECT_EQ(2, BLI_strnlen("ü", 100));
EXPECT_EQ(0, BLI_strnlen("this is a longer string", 0));
EXPECT_EQ(1, BLI_strnlen("this is a longer string", 1));
EXPECT_EQ(5, BLI_strnlen("this is a longer string", 5));
EXPECT_EQ(47, BLI_strnlen("This string writes about an agent without name.", 100));
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name String Join
* \{ */
#define BUFFER_SIZE 128
static void string_join_array_test_truncate(const char *strings[],
int strings_num,
char buffer[BUFFER_SIZE])
{
const int buffer_len = BLI_string_join_array(buffer, BUFFER_SIZE, strings, strings_num);
{ /* Ensure the allocated version is the same. */
char *buffer_alloc = BLI_string_join_arrayN(strings, strings_num);
EXPECT_STREQ(buffer_alloc, buffer);
MEM_freeN(buffer_alloc);
}
for (int dst_size = buffer_len + 1; dst_size > 0; dst_size--) {
char dst_tmp[BUFFER_SIZE];
int dst_tmp_len = BLI_string_join_array(dst_tmp, dst_size, strings, strings_num);
EXPECT_EQ(dst_tmp_len + 1, dst_size);
EXPECT_EQ(strncmp(dst_tmp, buffer, dst_tmp_len), 0);
}
}
static void string_join_array_with_sep_char_test_truncate(const char *strings[],
int strings_num,
char buffer[BUFFER_SIZE])
{
const int buffer_len = BLI_string_join_array_by_sep_char(
buffer, BUFFER_SIZE, '|', strings, strings_num);
{ /* Ensure the allocated version is the same. */
char *buffer_alloc = BLI_string_join_array_by_sep_charN('|', strings, strings_num);
EXPECT_STREQ(buffer_alloc, buffer);
MEM_freeN(buffer_alloc);
}
for (int dst_size = buffer_len + 1; dst_size > 0; dst_size--) {
char dst_tmp[BUFFER_SIZE];
int dst_tmp_len = BLI_string_join_array_by_sep_char(
dst_tmp, dst_size, '|', strings, strings_num);
EXPECT_EQ(dst_tmp_len + 1, dst_size);
EXPECT_EQ(strncmp(dst_tmp, buffer, dst_tmp_len), 0);
}
}
TEST(string, StrJoin_Truncate)
{
char buffer[BUFFER_SIZE];
{ /* Multiple single char words. */
const char *strings[] = {"a", "b", "c", "d", "e", "f"};
string_join_array_test_truncate(strings, ARRAY_SIZE(strings), buffer);
EXPECT_STREQ(buffer, "abcdef");
string_join_array_with_sep_char_test_truncate(strings, ARRAY_SIZE(strings), buffer);
EXPECT_STREQ(buffer, "a|b|c|d|e|f");
}
{ /* Multiple char pair words. */
const char *strings[] = {"aa", "bb", "cc", "dd", "ee", "ff"};
string_join_array_test_truncate(strings, ARRAY_SIZE(strings), buffer);
EXPECT_STREQ(buffer, "aabbccddeeff");
string_join_array_with_sep_char_test_truncate(strings, ARRAY_SIZE(strings), buffer);
EXPECT_STREQ(buffer, "aa|bb|cc|dd|ee|ff");
}
{ /* Multiple empty words. */
const char *strings[] = {"", "", "", "", "", ""};
string_join_array_test_truncate(strings, ARRAY_SIZE(strings), buffer);
EXPECT_STREQ(buffer, "");
string_join_array_with_sep_char_test_truncate(strings, ARRAY_SIZE(strings), buffer);
EXPECT_STREQ(buffer, "|||||");
}
{ /* Single word. */
const char *strings[] = {"test"};
string_join_array_test_truncate(strings, ARRAY_SIZE(strings), buffer);
EXPECT_STREQ(buffer, "test");
string_join_array_with_sep_char_test_truncate(strings, ARRAY_SIZE(strings), buffer);
EXPECT_STREQ(buffer, "test");
}
{ /* Empty item. */
const char *strings[] = {""};
string_join_array_test_truncate(strings, ARRAY_SIZE(strings), buffer);
EXPECT_STREQ(buffer, "");
string_join_array_with_sep_char_test_truncate(strings, ARRAY_SIZE(strings), buffer);
EXPECT_STREQ(buffer, "");
}
{ /* Empty array. */
string_join_array_test_truncate(nullptr, 0, buffer);
EXPECT_STREQ(buffer, "");
string_join_array_with_sep_char_test_truncate(nullptr, 0, buffer);
EXPECT_STREQ(buffer, "");
}
}
#undef BUFFER_SIZE
/** \} */
/* -------------------------------------------------------------------- */
/** \name String Find Split Words
* \{ */
struct WordInfo {
WordInfo() = default;
WordInfo(int start, int end) : start(start), end(end) {}
bool operator==(const WordInfo &other) const
{
return start == other.start && end == other.end;
}
int start, end;
};
static std::ostream &operator<<(std::ostream &os, const WordInfo &word_info)
{
os << "start: " << word_info.start << ", end: " << word_info.end;
return os;
}
class StringFindSplitWords : public testing::Test {
protected:
StringFindSplitWords() = default;
/* If max_words is -1 it will be initialized from the number of expected
* words +1. This way there is no need to pass an explicit number of words,
* but is also making it possible to catch situation when too many words
* are being returned. */
void testStringFindSplitWords(const string &str,
const size_t max_length,
initializer_list<WordInfo> expected_words_info_init,
int max_words = -1)
{
const vector<WordInfo> expected_words_info = expected_words_info_init;
if (max_words != -1) {
CHECK_LE(max_words, expected_words_info.size() - 1);
}
/* Since number of word info is used here, this makes it so we allow one
* extra word to be collected from the input. This allows to catch possible
* issues with word splitting not doing a correct thing. */
const int effective_max_words = (max_words == -1) ? expected_words_info.size() : max_words;
/* One extra element for the {-1, -1}. */
vector<WordInfo> actual_word_info(effective_max_words + 1, WordInfo(-1, -1));
const int actual_word_num = BLI_string_find_split_words(
str.c_str(),
max_length,
' ',
reinterpret_cast<int(*)[2]>(actual_word_info.data()),
effective_max_words);
/* Schrink actual array to an actual number of words, so we can compare
* vectors as-is. */
EXPECT_LE(actual_word_num, actual_word_info.size() - 1);
actual_word_info.resize(actual_word_num + 1);
/* Perform actual comparison. */
EXPECT_EQ_VECTOR(actual_word_info, expected_words_info);
}
void testStringFindSplitWords(const string &str,
initializer_list<WordInfo> expected_words_info_init)
{
testStringFindSplitWords(str, str.length(), expected_words_info_init);
}
};
/* BLI_string_find_split_words */
TEST_F(StringFindSplitWords, Simple)
{
testStringFindSplitWords("t", {{0, 1}, {-1, -1}});
testStringFindSplitWords("test", {{0, 4}, {-1, -1}});
}
TEST_F(StringFindSplitWords, Triple)
{
testStringFindSplitWords("f t w", {{0, 1}, {2, 1}, {4, 1}, {-1, -1}});
testStringFindSplitWords("find three words", {{0, 4}, {5, 5}, {11, 5}, {-1, -1}});
}
TEST_F(StringFindSplitWords, Spacing)
{
testStringFindSplitWords("# ## ### ####", {{0, 1}, {2, 2}, {5, 3}, {9, 4}, {-1, -1}});
testStringFindSplitWords("# # # #", {{0, 1}, {3, 1}, {7, 1}, {12, 1}, {-1, -1}});
}
TEST_F(StringFindSplitWords, Trailing_Left)
{
testStringFindSplitWords(" t", {{3, 1}, {-1, -1}});
testStringFindSplitWords(" test", {{3, 4}, {-1, -1}});
}
TEST_F(StringFindSplitWords, Trailing_Right)
{
testStringFindSplitWords("t ", {{0, 1}, {-1, -1}});
testStringFindSplitWords("test ", {{0, 4}, {-1, -1}});
}
TEST_F(StringFindSplitWords, Trailing_LeftRight)
{
testStringFindSplitWords(" surrounding space test 123 ",
{{3, 11}, {15, 5}, {21, 4}, {28, 3}, {-1, -1}});
}
TEST_F(StringFindSplitWords, Blank)
{
testStringFindSplitWords("", {{-1, -1}});
}
TEST_F(StringFindSplitWords, Whitespace)
{
testStringFindSplitWords(" ", {{-1, -1}});
testStringFindSplitWords(" ", {{-1, -1}});
}
TEST_F(StringFindSplitWords, LimitWords)
{
const string words = "too many chars";
const int words_len = words.length();
testStringFindSplitWords(words, words_len, {{0, 3}, {4, 4}, {9, 5}, {-1, -1}}, 3);
testStringFindSplitWords(words, words_len, {{0, 3}, {4, 4}, {-1, -1}}, 2);
testStringFindSplitWords(words, words_len, {{0, 3}, {-1, -1}}, 1);
testStringFindSplitWords(words, words_len, {{-1, -1}}, 0);
}
TEST_F(StringFindSplitWords, LimitChars)
{
const string words = "too many chars";
const int words_len = words.length();
testStringFindSplitWords(words, words_len, {{0, 3}, {4, 4}, {9, 5}, {-1, -1}});
testStringFindSplitWords(words, words_len - 1, {{0, 3}, {4, 4}, {9, 4}, {-1, -1}});
testStringFindSplitWords(words, words_len - 5, {{0, 3}, {4, 4}, {-1, -1}});
testStringFindSplitWords(words, 1, {{0, 1}, {-1, -1}});
testStringFindSplitWords(words, 0, {{-1, -1}});
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name String Search (Case Insensitive)
* \{ */
/* BLI_strncasestr */
TEST(string, StringStrncasestr)
{
const char *str_test0 = "search here";
const char *res;
res = BLI_strncasestr(str_test0, "", 0);
EXPECT_EQ(res, str_test0);
res = BLI_strncasestr(str_test0, " ", 1);
EXPECT_EQ(res, str_test0 + 6);
res = BLI_strncasestr(str_test0, "her", 3);
EXPECT_EQ(res, str_test0 + 7);
res = BLI_strncasestr(str_test0, "ARCh", 4);
EXPECT_EQ(res, str_test0 + 2);
res = BLI_strncasestr(str_test0, "earcq", 4);
EXPECT_EQ(res, str_test0 + 1);
res = BLI_strncasestr(str_test0, "not there", 9);
EXPECT_EQ(res, (void *)nullptr);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name String Maximum Word Count
* \{ */
/* BLI_string_max_possible_word_count */
TEST(string, StringMaxPossibleWordCount)
{
EXPECT_EQ(BLI_string_max_possible_word_count(0), 1);
EXPECT_EQ(BLI_string_max_possible_word_count(1), 1);
EXPECT_EQ(BLI_string_max_possible_word_count(2), 2);
EXPECT_EQ(BLI_string_max_possible_word_count(3), 2);
EXPECT_EQ(BLI_string_max_possible_word_count(10), 6);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name String is Decimal
* \{ */
/* BLI_string_is_decimal */
TEST(string, StrIsDecimal)
{
EXPECT_FALSE(BLI_string_is_decimal(""));
EXPECT_FALSE(BLI_string_is_decimal("je moeder"));
EXPECT_FALSE(BLI_string_is_decimal("je møder"));
EXPECT_FALSE(BLI_string_is_decimal("Agent 327"));
EXPECT_FALSE(BLI_string_is_decimal("Agent\000327"));
EXPECT_FALSE(BLI_string_is_decimal("\000327"));
EXPECT_FALSE(BLI_string_is_decimal("0x16"));
EXPECT_FALSE(BLI_string_is_decimal("16.4"));
EXPECT_FALSE(BLI_string_is_decimal("-1"));
EXPECT_TRUE(BLI_string_is_decimal("0"));
EXPECT_TRUE(BLI_string_is_decimal("1"));
EXPECT_TRUE(BLI_string_is_decimal("001"));
EXPECT_TRUE(BLI_string_is_decimal("11342908713948713498745980171334059871345098713405981734"));
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name String Natural Case Insensitive Comparison
* \{ */
/* BLI_strcasecmp_natural */
class StringCasecmpNatural : public testing::Test {
protected:
StringCasecmpNatural() = default;
using CompareWordsArray = vector<std::array<const char *, 2>>;
void testReturnsZeroForAll(const CompareWordsArray &items)
{
for (const auto &item : items) {
int res = BLI_strcasecmp_natural(item[0], item[1]);
EXPECT_EQ(res, 0);
}
}
void testReturnsLessThanZeroForAll(const CompareWordsArray &items)
{
for (const auto &item : items) {
int res = BLI_strcasecmp_natural(item[0], item[1]);
EXPECT_LT(res, 0);
}
}
void testReturnsMoreThanZeroForAll(const CompareWordsArray &items)
{
for (const auto &item : items) {
int res = BLI_strcasecmp_natural(item[0], item[1]);
EXPECT_GT(res, 0);
}
}
CompareWordsArray copyWithSwappedWords(const CompareWordsArray &items)
{
CompareWordsArray ret_array;
/* E.g. {{"a", "b"}, {"ab", "cd"}} becomes {{"b", "a"}, {"cd", "ab"}} */
ret_array.reserve(items.size());
for (const auto &item : items) {
ret_array.push_back({item[1], item[0]});
}
return ret_array;
}
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name String Case Insensitive Comparison
* \{ */
TEST_F(StringCasecmpNatural, Empty)
{
const CompareWordsArray equal{
{"", ""},
};
const CompareWordsArray negative{
{"", "a"},
{"", "A"},
};
CompareWordsArray positive = copyWithSwappedWords(negative);
testReturnsZeroForAll(equal);
testReturnsLessThanZeroForAll(negative);
testReturnsMoreThanZeroForAll(positive);
}
TEST_F(StringCasecmpNatural, Whitespace)
{
const CompareWordsArray equal{
{" ", " "},
{" a", " a"},
{" a ", " a "},
};
const CompareWordsArray negative{
{"", " "},
{"", " a"},
{"", " a "},
{" ", " a"},
};
CompareWordsArray positive = copyWithSwappedWords(negative);
testReturnsZeroForAll(equal);
testReturnsLessThanZeroForAll(negative);
testReturnsMoreThanZeroForAll(positive);
}
TEST_F(StringCasecmpNatural, TextOnlyLowerCase)
{
const CompareWordsArray equal{
{"a", "a"},
{"aa", "aa"},
{"ab", "ab"},
{"ba", "ba"},
{"je møder", "je møder"},
};
const CompareWordsArray negative{
{"a", "b"},
{"a", "aa"},
{"a", "ab"},
{"aa", "b"},
{"je møda", "je møder"},
};
CompareWordsArray positive = copyWithSwappedWords(negative);
testReturnsZeroForAll(equal);
testReturnsLessThanZeroForAll(negative);
testReturnsMoreThanZeroForAll(positive);
}
TEST_F(StringCasecmpNatural, TextMixedCase)
{
const CompareWordsArray equal{
{"A", "A"},
{"AA", "AA"},
{"AB", "AB"},
{"Ab", "Ab"},
{"aB", "aB"},
};
const CompareWordsArray negative{
{"A", "a"},
{"A", "B"},
{"A", "b"},
{"a", "B"},
{"AA", "aA"},
{"AA", "aA"},
{"Ab", "ab"},
{"AB", "Ab"},
/* Different lengths */
{"A", "ab"},
{"Aa", "b"},
{"aA", "b"},
{"AA", "b"},
{"A", "Ab"},
{"A", "aB"},
{"Aa", "B"},
{"aA", "B"},
{"AA", "B"},
};
CompareWordsArray positive = copyWithSwappedWords(negative);
testReturnsZeroForAll(equal);
testReturnsLessThanZeroForAll(negative);
testReturnsMoreThanZeroForAll(positive);
}
TEST_F(StringCasecmpNatural, Period)
{
const CompareWordsArray equal{
{".", "."},
{". ", ". "},
{" .", " ."},
{" . ", " . "},
};
const CompareWordsArray negative{
{".", ". "},
{" .", " . "},
{"foo.bar", "foo 1.bar"},
};
CompareWordsArray positive = copyWithSwappedWords(negative);
testReturnsZeroForAll(equal);
testReturnsLessThanZeroForAll(negative);
testReturnsMoreThanZeroForAll(positive);
}
TEST_F(StringCasecmpNatural, OnlyNumbers)
{
const CompareWordsArray equal{
{"0", "0"},
{"0001", "0001"},
{"42", "42"},
{"0042", "0042"},
};
const CompareWordsArray negative{
/* If numeric values are equal, number of leading zeros is used as tiebreaker. */
{"1", "0001"},
{"01", "001"},
{"0042", "0043"},
{"0042", "43"},
};
const CompareWordsArray positive = copyWithSwappedWords(negative);
testReturnsZeroForAll(equal);
testReturnsLessThanZeroForAll(negative);
testReturnsMoreThanZeroForAll(positive);
}
TEST_F(StringCasecmpNatural, TextAndNumbers)
{
const CompareWordsArray equal{
{"00je møder1", "00je møder1"},
{".0 ", ".0 "},
{" 1.", " 1."},
{" .0 ", " .0 "},
};
const CompareWordsArray negative{
{"00je møder0", "00je møder1"},
{"05je møder0", "06je møder1"},
{"Cube", "Cube.001"},
{"Cube.001", "Cube.002"},
{"CUbe.001", "Cube.002"},
{"CUbe.002", "Cube.002"},
};
const CompareWordsArray positive = copyWithSwappedWords(negative);
testReturnsZeroForAll(equal);
testReturnsLessThanZeroForAll(negative);
testReturnsMoreThanZeroForAll(positive);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name String Escape/Un-Escape
*
* #BLI_str_escape, #BLI_str_unescape.
* \{ */
class StringEscape : public testing::Test {
protected:
StringEscape() = default;
using CompareWordsArray = vector<std::array<const char *, 2>>;
void testEscapeWords(const CompareWordsArray &items)
{
size_t dst_test_len;
char dst_test[64]; /* Must be big enough for all input. */
for (const auto &item : items) {
/* Validate the static size is big enough (test the test it's self). */
EXPECT_LT((strlen(item[0]) * 2) + 1, sizeof(dst_test));
/* Escape the string. */
dst_test_len = BLI_str_escape(dst_test, item[0], sizeof(dst_test));
EXPECT_STREQ(dst_test, item[1]);
EXPECT_EQ(dst_test_len, strlen(dst_test));
/* Escape back. */
dst_test_len = BLI_str_unescape(dst_test, item[1], strlen(item[1]));
EXPECT_STREQ(dst_test, item[0]);
EXPECT_EQ(dst_test_len, strlen(dst_test));
}
}
};
TEST_F(StringEscape, Simple)
{
/* NOTE: clang-tidy `modernize-raw-string-literal` is disabled as it causes errors with MSVC.
* TODO: investigate resolving with `/Zc:preprocessor` flag. */
const CompareWordsArray equal{
{"", ""},
{"/", "/"},
{"'", "'"},
{"?", "?"},
};
const CompareWordsArray escaped{
{"\\", "\\\\"},
{"A\\", "A\\\\"},
{"\\A", "\\\\A"},
{"A\\B", "A\\\\B"},
{"?", "?"},
/* NOLINTNEXTLINE: modernize-raw-string-literal. */
{"\"\\", "\\\"\\\\"},
/* NOLINTNEXTLINE: modernize-raw-string-literal. */
{"\\\"", "\\\\\\\""},
/* NOLINTNEXTLINE: modernize-raw-string-literal. */
{"\"\\\"", "\\\"\\\\\\\""},
/* NOLINTNEXTLINE: modernize-raw-string-literal. */
{"\"\"\"", "\\\"\\\"\\\""},
/* NOLINTNEXTLINE: modernize-raw-string-literal. */
{"\\\\\\", "\\\\\\\\\\\\"},
};
testEscapeWords(equal);
testEscapeWords(escaped);
}
TEST_F(StringEscape, Control)
{
const CompareWordsArray escaped{
{"\n", "\\n"},
{"\r", "\\r"},
{"\t", "\\t"},
{"\a", "\\a"},
{"\b", "\\b"},
{"\f", "\\f"},
{"A\n", "A\\n"},
{"\nA", "\\nA"},
/* NOLINTNEXTLINE: modernize-raw-string-literal. */
{"\n\r\t\a\b\f", "\\n\\r\\t\\a\\b\\f"},
/* NOLINTNEXTLINE: modernize-raw-string-literal. */
{"\n_\r_\t_\a_\b_\f", "\\n_\\r_\\t_\\a_\\b_\\f"},
/* NOLINTNEXTLINE: modernize-raw-string-literal. */
{"\n\\\r\\\t\\\a\\\b\\\f", "\\n\\\\\\r\\\\\\t\\\\\\a\\\\\\b\\\\\\f"},
};
testEscapeWords(escaped);
}
/** \} */
TEST(BLI_string, bounded_strcpy)
{
{
char str[8];
STRNCPY(str, "Hello");
EXPECT_STREQ(str, "Hello");
}
{
char str[8];
STRNCPY(str, "Hello, World!");
EXPECT_STREQ(str, "Hello, ");
}
}