Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/common-tests/string_tests.cpp
4212 views
1
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <[email protected]>
2
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
3
4
#include "common/string_util.h"
5
6
#include <gtest/gtest.h>
7
8
TEST(StringUtil, Ellipsise)
9
{
10
ASSERT_EQ(StringUtil::Ellipsise("HelloWorld", 6, "..."), "Hel...");
11
ASSERT_EQ(StringUtil::Ellipsise("HelloWorld", 7, ".."), "Hello..");
12
ASSERT_EQ(StringUtil::Ellipsise("HelloWorld", 20, ".."), "HelloWorld");
13
ASSERT_EQ(StringUtil::Ellipsise("", 20, "..."), "");
14
ASSERT_EQ(StringUtil::Ellipsise("Hello", 10, "..."), "Hello");
15
}
16
17
TEST(StringUtil, EllipsiseInPlace)
18
{
19
std::string s;
20
s = "HelloWorld";
21
StringUtil::EllipsiseInPlace(s, 6, "...");
22
ASSERT_EQ(s, "Hel...");
23
s = "HelloWorld";
24
StringUtil::EllipsiseInPlace(s, 7, "..");
25
ASSERT_EQ(s, "Hello..");
26
s = "HelloWorld";
27
StringUtil::EllipsiseInPlace(s, 20, "..");
28
ASSERT_EQ(s, "HelloWorld");
29
s = "";
30
StringUtil::EllipsiseInPlace(s, 20, "...");
31
ASSERT_EQ(s, "");
32
s = "Hello";
33
StringUtil::EllipsiseInPlace(s, 10, "...");
34
ASSERT_EQ(s, "Hello");
35
}
36
37
TEST(StringUtil, Base64EncodeDecode)
38
{
39
struct TestCase
40
{
41
const char* hexString;
42
const char* base64String;
43
};
44
static const TestCase testCases[] = {
45
{"33326a6f646933326a68663937683732383368", "MzJqb2RpMzJqaGY5N2g3MjgzaA=="},
46
{"32753965333268756979386672677537366967723839683432703075693132393065755c5d0931325c335c31323439303438753839333272",
47
"MnU5ZTMyaHVpeThmcmd1NzZpZ3I4OWg0MnAwdWkxMjkwZXVcXQkxMlwzXDEyNDkwNDh1ODkzMnI="},
48
{"3332726a33323738676838666233326830393233386637683938323139", "MzJyajMyNzhnaDhmYjMyaDA5MjM4ZjdoOTgyMTk="},
49
{"9956967BE9C96E10B27FF8897A5B768A2F4B103CE934718D020FE6B5B770", "mVaWe+nJbhCyf/iJelt2ii9LEDzpNHGNAg/mtbdw"},
50
{"BC94251814827A5D503D62D5EE6CBAB0FD55D2E2FCEDBB2261D6010084B95DD648766D8983F03AFA3908956D8201E26BB09FE52B515A61A9E"
51
"1D3ADC207BD9E622128F22929CDED456B595A410F7168B0BA6370289E6291E38E47C18278561C79A7297C21D23C06BB2F694DC2F65FAAF994"
52
"59E3FC14B1FA415A3320AF00ACE54C00BE",
53
"vJQlGBSCel1QPWLV7my6sP1V0uL87bsiYdYBAIS5XdZIdm2Jg/A6+jkIlW2CAeJrsJ/"
54
"lK1FaYanh063CB72eYiEo8ikpze1Fa1laQQ9xaLC6Y3AonmKR445HwYJ4Vhx5pyl8IdI8BrsvaU3C9l+q+ZRZ4/wUsfpBWjMgrwCs5UwAvg=="},
55
{"192B42CB0F66F69BE8A5", "GStCyw9m9pvopQ=="},
56
{"38ABD400F3BB6960EB60C056719B5362", "OKvUAPO7aWDrYMBWcZtTYg=="},
57
{"776FAB27DC7F8DA86F298D55B69F8C278D53871F8CBCCF", "d2+rJ9x/jahvKY1Vtp+MJ41Thx+MvM8="},
58
{"B1ED3EA2E35EE69C7E16707B05042A", "se0+ouNe5px+FnB7BQQq"},
59
};
60
61
for (const TestCase& tc : testCases)
62
{
63
std::optional<std::vector<u8>> bytes = StringUtil::DecodeHex(tc.hexString);
64
ASSERT_TRUE(bytes.has_value());
65
66
std::string encoded_b64 = StringUtil::EncodeBase64(bytes.value());
67
ASSERT_EQ(encoded_b64, tc.base64String);
68
69
std::optional<std::vector<u8>> dbytes = StringUtil::DecodeBase64(tc.base64String);
70
ASSERT_TRUE(dbytes.has_value());
71
ASSERT_EQ(dbytes.value(), bytes.value());
72
}
73
}
74
75
TEST(StringUtil, CompareNoCase)
76
{
77
// Test identical strings
78
ASSERT_EQ(StringUtil::CompareNoCase("hello", "hello"), 0);
79
ASSERT_EQ(StringUtil::CompareNoCase("", ""), 0);
80
81
// Test case insensitive comparison - should be equal
82
ASSERT_EQ(StringUtil::CompareNoCase("Hello", "hello"), 0);
83
ASSERT_EQ(StringUtil::CompareNoCase("HELLO", "hello"), 0);
84
ASSERT_EQ(StringUtil::CompareNoCase("hello", "HELLO"), 0);
85
ASSERT_EQ(StringUtil::CompareNoCase("HeLLo", "hEllO"), 0);
86
ASSERT_EQ(StringUtil::CompareNoCase("WoRlD", "world"), 0);
87
88
// Test different strings - first string lexicographically less than second
89
ASSERT_LT(StringUtil::CompareNoCase("apple", "banana"), 0);
90
ASSERT_LT(StringUtil::CompareNoCase("Apple", "BANANA"), 0);
91
ASSERT_LT(StringUtil::CompareNoCase("APPLE", "banana"), 0);
92
ASSERT_LT(StringUtil::CompareNoCase("aaa", "aab"), 0);
93
94
// Test different strings - first string lexicographically greater than second
95
ASSERT_GT(StringUtil::CompareNoCase("zebra", "apple"), 0);
96
ASSERT_GT(StringUtil::CompareNoCase("ZEBRA", "apple"), 0);
97
ASSERT_GT(StringUtil::CompareNoCase("zebra", "APPLE"), 0);
98
ASSERT_GT(StringUtil::CompareNoCase("aab", "aaa"), 0);
99
100
// Test different length strings - shorter vs longer
101
ASSERT_LT(StringUtil::CompareNoCase("abc", "abcd"), 0);
102
ASSERT_GT(StringUtil::CompareNoCase("abcd", "abc"), 0);
103
ASSERT_LT(StringUtil::CompareNoCase("ABC", "abcd"), 0);
104
ASSERT_GT(StringUtil::CompareNoCase("ABCD", "abc"), 0);
105
106
// Test empty string comparisons
107
ASSERT_GT(StringUtil::CompareNoCase("hello", ""), 0);
108
ASSERT_LT(StringUtil::CompareNoCase("", "hello"), 0);
109
ASSERT_GT(StringUtil::CompareNoCase("A", ""), 0);
110
ASSERT_LT(StringUtil::CompareNoCase("", "a"), 0);
111
112
// Test strings with numbers and special characters
113
ASSERT_EQ(StringUtil::CompareNoCase("Test123", "test123"), 0);
114
ASSERT_EQ(StringUtil::CompareNoCase("Hello_World", "hello_world"), 0);
115
ASSERT_LT(StringUtil::CompareNoCase("Test1", "Test2"), 0);
116
ASSERT_GT(StringUtil::CompareNoCase("Test2", "Test1"), 0);
117
ASSERT_EQ(StringUtil::CompareNoCase("File.txt", "FILE.TXT"), 0);
118
119
// Test prefix scenarios
120
ASSERT_LT(StringUtil::CompareNoCase("test", "testing"), 0);
121
ASSERT_GT(StringUtil::CompareNoCase("testing", "test"), 0);
122
ASSERT_LT(StringUtil::CompareNoCase("TEST", "testing"), 0);
123
124
// Test single character differences
125
ASSERT_LT(StringUtil::CompareNoCase("a", "b"), 0);
126
ASSERT_GT(StringUtil::CompareNoCase("B", "a"), 0);
127
ASSERT_EQ(StringUtil::CompareNoCase("A", "a"), 0);
128
ASSERT_EQ(StringUtil::CompareNoCase("z", "Z"), 0);
129
}
130
131
// New tests for methods not already covered
132
133
TEST(StringUtil, ToLowerToUpper)
134
{
135
// Test ToLower
136
ASSERT_EQ(StringUtil::ToLower('A'), 'a');
137
ASSERT_EQ(StringUtil::ToLower('Z'), 'z');
138
ASSERT_EQ(StringUtil::ToLower('M'), 'm');
139
ASSERT_EQ(StringUtil::ToLower('a'), 'a'); // Already lowercase
140
ASSERT_EQ(StringUtil::ToLower('z'), 'z'); // Already lowercase
141
ASSERT_EQ(StringUtil::ToLower('1'), '1'); // Non-alphabetic
142
ASSERT_EQ(StringUtil::ToLower('!'), '!'); // Non-alphabetic
143
ASSERT_EQ(StringUtil::ToLower(' '), ' '); // Space
144
145
// Test ToUpper
146
ASSERT_EQ(StringUtil::ToUpper('a'), 'A');
147
ASSERT_EQ(StringUtil::ToUpper('z'), 'Z');
148
ASSERT_EQ(StringUtil::ToUpper('m'), 'M');
149
ASSERT_EQ(StringUtil::ToUpper('A'), 'A'); // Already uppercase
150
ASSERT_EQ(StringUtil::ToUpper('Z'), 'Z'); // Already uppercase
151
ASSERT_EQ(StringUtil::ToUpper('1'), '1'); // Non-alphabetic
152
ASSERT_EQ(StringUtil::ToUpper('!'), '!'); // Non-alphabetic
153
ASSERT_EQ(StringUtil::ToUpper(' '), ' '); // Space
154
}
155
156
TEST(StringUtil, WildcardMatch)
157
{
158
// Basic wildcard tests
159
ASSERT_TRUE(StringUtil::WildcardMatch("test", "test"));
160
ASSERT_TRUE(StringUtil::WildcardMatch("test", "*"));
161
ASSERT_TRUE(StringUtil::WildcardMatch("test", "t*"));
162
ASSERT_TRUE(StringUtil::WildcardMatch("test", "*t"));
163
ASSERT_TRUE(StringUtil::WildcardMatch("test", "te*"));
164
ASSERT_TRUE(StringUtil::WildcardMatch("test", "*st"));
165
ASSERT_TRUE(StringUtil::WildcardMatch("test", "t*t"));
166
ASSERT_TRUE(StringUtil::WildcardMatch("test", "?est"));
167
ASSERT_TRUE(StringUtil::WildcardMatch("test", "t?st"));
168
ASSERT_TRUE(StringUtil::WildcardMatch("test", "tes?"));
169
ASSERT_TRUE(StringUtil::WildcardMatch("test", "????"));
170
171
// Negative tests
172
ASSERT_FALSE(StringUtil::WildcardMatch("test", "best"));
173
ASSERT_FALSE(StringUtil::WildcardMatch("test", "tests"));
174
ASSERT_FALSE(StringUtil::WildcardMatch("test", "???"));
175
ASSERT_FALSE(StringUtil::WildcardMatch("test", "?????"));
176
177
// Case sensitivity tests
178
ASSERT_TRUE(StringUtil::WildcardMatch("Test", "test", false));
179
ASSERT_FALSE(StringUtil::WildcardMatch("Test", "test", true));
180
ASSERT_TRUE(StringUtil::WildcardMatch("TEST", "*est", false));
181
ASSERT_FALSE(StringUtil::WildcardMatch("TEST", "*est", true));
182
183
// Empty string tests
184
ASSERT_TRUE(StringUtil::WildcardMatch("", ""));
185
ASSERT_TRUE(StringUtil::WildcardMatch("", "*"));
186
ASSERT_FALSE(StringUtil::WildcardMatch("", "?"));
187
ASSERT_FALSE(StringUtil::WildcardMatch("test", ""));
188
}
189
190
TEST(StringUtil, Strlcpy)
191
{
192
char buffer[10];
193
194
// Normal copy
195
std::size_t result = StringUtil::Strlcpy(buffer, "hello", sizeof(buffer));
196
ASSERT_EQ(result, 5u);
197
ASSERT_STREQ(buffer, "hello");
198
199
// Truncation test
200
result = StringUtil::Strlcpy(buffer, "hello world", sizeof(buffer));
201
ASSERT_EQ(result, 11u); // Should return original string length
202
ASSERT_STREQ(buffer, "hello wor"); // Should be truncated and null-terminated
203
204
// Empty string
205
result = StringUtil::Strlcpy(buffer, "", sizeof(buffer));
206
ASSERT_EQ(result, 0u);
207
ASSERT_STREQ(buffer, "");
208
209
// Buffer size 1 (only null terminator)
210
result = StringUtil::Strlcpy(buffer, "test", 1);
211
ASSERT_EQ(result, 4u);
212
ASSERT_STREQ(buffer, "");
213
214
// Test with string_view
215
std::string_view sv = "test string";
216
result = StringUtil::Strlcpy(buffer, sv, sizeof(buffer));
217
ASSERT_EQ(result, 11u);
218
ASSERT_STREQ(buffer, "test stri");
219
}
220
221
TEST(StringUtil, Strnlen)
222
{
223
const char* str = "hello world";
224
ASSERT_EQ(StringUtil::Strnlen(str, 100), 11u);
225
ASSERT_EQ(StringUtil::Strnlen(str, 5), 5u);
226
ASSERT_EQ(StringUtil::Strnlen(str, 0), 0u);
227
ASSERT_EQ(StringUtil::Strnlen("", 10), 0u);
228
}
229
230
TEST(StringUtil, Strcasecmp)
231
{
232
ASSERT_EQ(StringUtil::Strcasecmp("hello", "hello"), 0);
233
ASSERT_EQ(StringUtil::Strcasecmp("Hello", "hello"), 0);
234
ASSERT_EQ(StringUtil::Strcasecmp("HELLO", "hello"), 0);
235
ASSERT_LT(StringUtil::Strcasecmp("apple", "banana"), 0);
236
ASSERT_GT(StringUtil::Strcasecmp("zebra", "apple"), 0);
237
}
238
239
TEST(StringUtil, Strncasecmp)
240
{
241
ASSERT_EQ(StringUtil::Strncasecmp("hello", "hello", 5), 0);
242
ASSERT_EQ(StringUtil::Strncasecmp("Hello", "hello", 5), 0);
243
ASSERT_EQ(StringUtil::Strncasecmp("hello world", "hello test", 5), 0);
244
ASSERT_NE(StringUtil::Strncasecmp("hello world", "hello test", 10), 0);
245
}
246
247
TEST(StringUtil, EqualNoCase)
248
{
249
ASSERT_TRUE(StringUtil::EqualNoCase("hello", "hello"));
250
ASSERT_TRUE(StringUtil::EqualNoCase("Hello", "hello"));
251
ASSERT_TRUE(StringUtil::EqualNoCase("HELLO", "hello"));
252
ASSERT_TRUE(StringUtil::EqualNoCase("", ""));
253
ASSERT_FALSE(StringUtil::EqualNoCase("hello", "world"));
254
ASSERT_FALSE(StringUtil::EqualNoCase("hello", "hello world"));
255
ASSERT_FALSE(StringUtil::EqualNoCase("hello world", "hello"));
256
}
257
258
TEST(StringUtil, ContainsNoCase)
259
{
260
ASSERT_TRUE(StringUtil::ContainsNoCase("hello world", "world"));
261
ASSERT_TRUE(StringUtil::ContainsNoCase("hello world", "WORLD"));
262
ASSERT_TRUE(StringUtil::ContainsNoCase("Hello World", "lo wo"));
263
ASSERT_TRUE(StringUtil::ContainsNoCase("test", "test"));
264
ASSERT_TRUE(StringUtil::ContainsNoCase("test", ""));
265
ASSERT_FALSE(StringUtil::ContainsNoCase("hello", "world"));
266
ASSERT_FALSE(StringUtil::ContainsNoCase("test", "testing"));
267
}
268
269
TEST(StringUtil, FromCharsIntegral)
270
{
271
// Test integers
272
auto result = StringUtil::FromChars<int>("123");
273
ASSERT_TRUE(result.has_value());
274
ASSERT_EQ(*result, 123);
275
276
result = StringUtil::FromChars<int>("-456");
277
ASSERT_TRUE(result.has_value());
278
ASSERT_EQ(*result, -456);
279
280
// Test hex
281
auto hex_result = StringUtil::FromChars<int>("FF", 16);
282
ASSERT_TRUE(hex_result.has_value());
283
ASSERT_EQ(*hex_result, 255);
284
285
// Test invalid input
286
auto invalid = StringUtil::FromChars<int>("abc");
287
ASSERT_FALSE(invalid.has_value());
288
289
// Test with endptr
290
std::string_view endptr;
291
auto endptr_result = StringUtil::FromChars<int>("123abc", 10, &endptr);
292
ASSERT_TRUE(endptr_result.has_value());
293
ASSERT_EQ(*endptr_result, 123);
294
ASSERT_EQ(endptr, "abc");
295
}
296
297
TEST(StringUtil, FromCharsWithOptionalBase)
298
{
299
// Test hex prefix
300
auto hex = StringUtil::FromCharsWithOptionalBase<int>("0xFF");
301
ASSERT_TRUE(hex.has_value());
302
ASSERT_EQ(*hex, 255);
303
304
// Test binary prefix
305
auto bin = StringUtil::FromCharsWithOptionalBase<int>("0b1010");
306
ASSERT_TRUE(bin.has_value());
307
ASSERT_EQ(*bin, 10);
308
309
// Test octal prefix
310
auto oct = StringUtil::FromCharsWithOptionalBase<int>("0123");
311
ASSERT_TRUE(oct.has_value());
312
ASSERT_EQ(*oct, 83); // 123 in octal = 83 in decimal
313
314
// Test decimal (no prefix)
315
auto dec = StringUtil::FromCharsWithOptionalBase<int>("123");
316
ASSERT_TRUE(dec.has_value());
317
ASSERT_EQ(*dec, 123);
318
}
319
320
TEST(StringUtil, FromCharsFloatingPoint)
321
{
322
auto result = StringUtil::FromChars<float>("123.45");
323
ASSERT_TRUE(result.has_value());
324
ASSERT_FLOAT_EQ(*result, 123.45f);
325
326
auto double_result = StringUtil::FromChars<double>("-456.789");
327
ASSERT_TRUE(double_result.has_value());
328
ASSERT_DOUBLE_EQ(*double_result, -456.789);
329
330
// Test scientific notation
331
auto sci = StringUtil::FromChars<double>("1.23e-4");
332
ASSERT_TRUE(sci.has_value());
333
ASSERT_DOUBLE_EQ(*sci, 0.000123);
334
335
// Test invalid
336
auto invalid = StringUtil::FromChars<float>("abc");
337
ASSERT_FALSE(invalid.has_value());
338
}
339
340
TEST(StringUtil, FromCharsBool)
341
{
342
// Test true values
343
ASSERT_TRUE(StringUtil::FromChars<bool>("true", 10).value_or(false));
344
ASSERT_TRUE(StringUtil::FromChars<bool>("TRUE", 10).value_or(false));
345
ASSERT_TRUE(StringUtil::FromChars<bool>("yes", 10).value_or(false));
346
ASSERT_TRUE(StringUtil::FromChars<bool>("YES", 10).value_or(false));
347
ASSERT_TRUE(StringUtil::FromChars<bool>("on", 10).value_or(false));
348
ASSERT_TRUE(StringUtil::FromChars<bool>("ON", 10).value_or(false));
349
ASSERT_TRUE(StringUtil::FromChars<bool>("1", 10).value_or(false));
350
ASSERT_TRUE(StringUtil::FromChars<bool>("enabled", 10).value_or(false));
351
ASSERT_TRUE(StringUtil::FromChars<bool>("ENABLED", 10).value_or(false));
352
353
// Test false values
354
ASSERT_FALSE(StringUtil::FromChars<bool>("false", 10).value_or(true));
355
ASSERT_FALSE(StringUtil::FromChars<bool>("FALSE", 10).value_or(true));
356
ASSERT_FALSE(StringUtil::FromChars<bool>("no", 10).value_or(true));
357
ASSERT_FALSE(StringUtil::FromChars<bool>("NO", 10).value_or(true));
358
ASSERT_FALSE(StringUtil::FromChars<bool>("off", 10).value_or(true));
359
ASSERT_FALSE(StringUtil::FromChars<bool>("OFF", 10).value_or(true));
360
ASSERT_FALSE(StringUtil::FromChars<bool>("0", 10).value_or(true));
361
ASSERT_FALSE(StringUtil::FromChars<bool>("disabled", 10).value_or(true));
362
ASSERT_FALSE(StringUtil::FromChars<bool>("DISABLED", 10).value_or(true));
363
364
// Test invalid
365
ASSERT_FALSE(StringUtil::FromChars<bool>("maybe", 10).has_value());
366
ASSERT_FALSE(StringUtil::FromChars<bool>("2", 10).has_value());
367
}
368
369
TEST(StringUtil, ToCharsIntegral)
370
{
371
ASSERT_EQ(StringUtil::ToChars(123), "123");
372
ASSERT_EQ(StringUtil::ToChars(-456), "-456");
373
ASSERT_EQ(StringUtil::ToChars(255, 16), "ff");
374
ASSERT_EQ(StringUtil::ToChars(15, 2), "1111");
375
}
376
377
TEST(StringUtil, ToCharsFloatingPoint)
378
{
379
std::string result = StringUtil::ToChars(123.45f);
380
ASSERT_FALSE(result.empty());
381
// Just check it's a valid representation, exact format may vary
382
ASSERT_NE(result.find("123"), std::string::npos);
383
}
384
385
TEST(StringUtil, ToCharsBool)
386
{
387
ASSERT_EQ(StringUtil::ToChars(true, 10), "true");
388
ASSERT_EQ(StringUtil::ToChars(false, 10), "false");
389
}
390
391
TEST(StringUtil, IsWhitespace)
392
{
393
ASSERT_TRUE(StringUtil::IsWhitespace(' '));
394
ASSERT_TRUE(StringUtil::IsWhitespace('\t'));
395
ASSERT_TRUE(StringUtil::IsWhitespace('\n'));
396
ASSERT_TRUE(StringUtil::IsWhitespace('\r'));
397
ASSERT_TRUE(StringUtil::IsWhitespace('\f'));
398
ASSERT_TRUE(StringUtil::IsWhitespace('\v'));
399
400
ASSERT_FALSE(StringUtil::IsWhitespace('a'));
401
ASSERT_FALSE(StringUtil::IsWhitespace('1'));
402
ASSERT_FALSE(StringUtil::IsWhitespace('!'));
403
}
404
405
TEST(StringUtil, DecodeHexDigit)
406
{
407
ASSERT_EQ(StringUtil::DecodeHexDigit('0'), 0);
408
ASSERT_EQ(StringUtil::DecodeHexDigit('9'), 9);
409
ASSERT_EQ(StringUtil::DecodeHexDigit('a'), 10);
410
ASSERT_EQ(StringUtil::DecodeHexDigit('f'), 15);
411
ASSERT_EQ(StringUtil::DecodeHexDigit('A'), 10);
412
ASSERT_EQ(StringUtil::DecodeHexDigit('F'), 15);
413
ASSERT_EQ(StringUtil::DecodeHexDigit('g'), 0); // Invalid should return 0
414
}
415
416
TEST(StringUtil, EncodeHex)
417
{
418
std::vector<u8> data = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};
419
std::string hex = StringUtil::EncodeHex(data.data(), data.size());
420
ASSERT_EQ(hex, "0123456789abcdef");
421
422
// Test with span
423
std::string hex_span = StringUtil::EncodeHex(std::span<const u8>(data));
424
ASSERT_EQ(hex_span, "0123456789abcdef");
425
426
// Test empty
427
std::string empty_hex = StringUtil::EncodeHex(nullptr, 0);
428
ASSERT_EQ(empty_hex, "");
429
}
430
431
TEST(StringUtil, DecodeHex)
432
{
433
// Test buffer version
434
std::vector<u8> buffer(8);
435
size_t decoded = StringUtil::DecodeHex(std::span<u8>(buffer), "0123456789ABCDEF");
436
ASSERT_EQ(decoded, 8u);
437
ASSERT_EQ(buffer[0], 0x01u);
438
ASSERT_EQ(buffer[1], 0x23u);
439
ASSERT_EQ(buffer[7], 0xEFu);
440
441
// Test vector version
442
auto result = StringUtil::DecodeHex("0123456789ABCDEF");
443
ASSERT_TRUE(result.has_value());
444
ASSERT_EQ(result->size(), 8u);
445
ASSERT_EQ((*result)[0], 0x01u);
446
ASSERT_EQ((*result)[7], 0xEFu);
447
448
// Test invalid hex
449
auto invalid = StringUtil::DecodeHex("xyz");
450
ASSERT_FALSE(invalid.has_value());
451
}
452
453
TEST(StringUtil, IsHexDigit)
454
{
455
ASSERT_TRUE(StringUtil::IsHexDigit('0'));
456
ASSERT_TRUE(StringUtil::IsHexDigit('9'));
457
ASSERT_TRUE(StringUtil::IsHexDigit('a'));
458
ASSERT_TRUE(StringUtil::IsHexDigit('f'));
459
ASSERT_TRUE(StringUtil::IsHexDigit('A'));
460
ASSERT_TRUE(StringUtil::IsHexDigit('F'));
461
462
ASSERT_FALSE(StringUtil::IsHexDigit('g'));
463
ASSERT_FALSE(StringUtil::IsHexDigit('G'));
464
ASSERT_FALSE(StringUtil::IsHexDigit('!'));
465
ASSERT_FALSE(StringUtil::IsHexDigit(' '));
466
}
467
468
TEST(StringUtil, ParseFixedHexString)
469
{
470
constexpr auto result = StringUtil::ParseFixedHexString<4>("01234567");
471
ASSERT_EQ(result[0], 0x01);
472
ASSERT_EQ(result[1], 0x23);
473
ASSERT_EQ(result[2], 0x45);
474
ASSERT_EQ(result[3], 0x67);
475
}
476
477
TEST(StringUtil, Base64Lengths)
478
{
479
ASSERT_EQ(StringUtil::DecodedBase64Length(""), 0u);
480
ASSERT_EQ(StringUtil::DecodedBase64Length("SGVsbG8="), 5u);
481
ASSERT_EQ(StringUtil::DecodedBase64Length("SGVsbG8h"), 6u);
482
ASSERT_EQ(StringUtil::DecodedBase64Length("abc"), 0u); // Invalid length
483
484
std::vector<u8> data = {1, 2, 3, 4, 5};
485
ASSERT_EQ(StringUtil::EncodedBase64Length(std::span<const u8>(data)), 8u);
486
}
487
488
TEST(StringUtil, StartsWithNoCase)
489
{
490
ASSERT_TRUE(StringUtil::StartsWithNoCase("Hello World", "hello"));
491
ASSERT_TRUE(StringUtil::StartsWithNoCase("Hello World", "HELLO"));
492
ASSERT_TRUE(StringUtil::StartsWithNoCase("test", "test"));
493
ASSERT_TRUE(StringUtil::StartsWithNoCase("test", ""));
494
ASSERT_FALSE(StringUtil::StartsWithNoCase("Hello", "world"));
495
ASSERT_FALSE(StringUtil::StartsWithNoCase("Hi", "Hello"));
496
ASSERT_FALSE(StringUtil::StartsWithNoCase("", "test"));
497
}
498
499
TEST(StringUtil, EndsWithNoCase)
500
{
501
ASSERT_TRUE(StringUtil::EndsWithNoCase("Hello World", "world"));
502
ASSERT_TRUE(StringUtil::EndsWithNoCase("Hello World", "WORLD"));
503
ASSERT_TRUE(StringUtil::EndsWithNoCase("test", "test"));
504
ASSERT_TRUE(StringUtil::EndsWithNoCase("test", ""));
505
ASSERT_FALSE(StringUtil::EndsWithNoCase("Hello", "world"));
506
ASSERT_FALSE(StringUtil::EndsWithNoCase("Hi", "Hello"));
507
ASSERT_FALSE(StringUtil::EndsWithNoCase("", "test"));
508
}
509
510
TEST(StringUtil, StripWhitespace)
511
{
512
// Test string_view version
513
ASSERT_EQ(StringUtil::StripWhitespace(" hello "), "hello");
514
ASSERT_EQ(StringUtil::StripWhitespace("\t\n hello world \r\f"), "hello world");
515
ASSERT_EQ(StringUtil::StripWhitespace(" "), "");
516
ASSERT_EQ(StringUtil::StripWhitespace(""), "");
517
ASSERT_EQ(StringUtil::StripWhitespace("hello"), "hello");
518
ASSERT_EQ(StringUtil::StripWhitespace(" hello"), "hello");
519
ASSERT_EQ(StringUtil::StripWhitespace("hello "), "hello");
520
521
// Test in-place version
522
std::string s = " hello world ";
523
StringUtil::StripWhitespace(&s);
524
ASSERT_EQ(s, "hello world");
525
526
s = "\t\n test \r\f";
527
StringUtil::StripWhitespace(&s);
528
ASSERT_EQ(s, "test");
529
530
s = " ";
531
StringUtil::StripWhitespace(&s);
532
ASSERT_EQ(s, "");
533
}
534
535
TEST(StringUtil, SplitString)
536
{
537
auto result = StringUtil::SplitString("a,b,c", ',');
538
ASSERT_EQ(result.size(), 3u);
539
ASSERT_EQ(result[0], "a");
540
ASSERT_EQ(result[1], "b");
541
ASSERT_EQ(result[2], "c");
542
543
// Test with empty parts
544
result = StringUtil::SplitString("a,,c", ',', false);
545
ASSERT_EQ(result.size(), 3u);
546
ASSERT_EQ(result[1], "");
547
548
// Test skip empty
549
result = StringUtil::SplitString("a,,c", ',', true);
550
ASSERT_EQ(result.size(), 2u);
551
ASSERT_EQ(result[0], "a");
552
ASSERT_EQ(result[1], "c");
553
554
// Test empty string
555
result = StringUtil::SplitString("", ',');
556
ASSERT_EQ(result.size(), 0u);
557
558
// Test no delimiter
559
result = StringUtil::SplitString("hello", ',');
560
ASSERT_EQ(result.size(), 1u);
561
ASSERT_EQ(result[0], "hello");
562
}
563
564
TEST(StringUtil, SplitNewString)
565
{
566
auto result = StringUtil::SplitNewString("a,b,c", ',');
567
ASSERT_EQ(result.size(), 3u);
568
ASSERT_EQ(result[0], "a");
569
ASSERT_EQ(result[1], "b");
570
ASSERT_EQ(result[2], "c");
571
572
// Test empty string
573
result = StringUtil::SplitNewString("", ',');
574
ASSERT_EQ(result.size(), 0u);
575
}
576
577
TEST(StringUtil, IsInStringList)
578
{
579
std::vector<std::string> list = {"apple", "banana", "cherry"};
580
ASSERT_TRUE(StringUtil::IsInStringList(list, "apple"));
581
ASSERT_TRUE(StringUtil::IsInStringList(list, "banana"));
582
ASSERT_FALSE(StringUtil::IsInStringList(list, "grape"));
583
ASSERT_FALSE(StringUtil::IsInStringList(list, ""));
584
585
std::vector<std::string> empty_list;
586
ASSERT_FALSE(StringUtil::IsInStringList(empty_list, "apple"));
587
}
588
589
TEST(StringUtil, AddToStringList)
590
{
591
std::vector<std::string> list = {"apple", "banana"};
592
593
// Add new item
594
ASSERT_TRUE(StringUtil::AddToStringList(list, "cherry"));
595
ASSERT_EQ(list.size(), 3u);
596
ASSERT_EQ(list[2], "cherry");
597
598
// Try to add existing item
599
ASSERT_FALSE(StringUtil::AddToStringList(list, "apple"));
600
ASSERT_EQ(list.size(), 3u);
601
}
602
603
TEST(StringUtil, RemoveFromStringList)
604
{
605
std::vector<std::string> list = {"apple", "banana", "apple", "cherry"};
606
607
// Remove existing item (should remove all occurrences)
608
ASSERT_TRUE(StringUtil::RemoveFromStringList(list, "apple"));
609
ASSERT_EQ(list.size(), 2u);
610
ASSERT_EQ(list[0], "banana");
611
ASSERT_EQ(list[1], "cherry");
612
613
// Try to remove non-existing item
614
ASSERT_FALSE(StringUtil::RemoveFromStringList(list, "grape"));
615
ASSERT_EQ(list.size(), 2u);
616
}
617
618
TEST(StringUtil, JoinString)
619
{
620
std::vector<std::string> list = {"apple", "banana", "cherry"};
621
622
// Test with char delimiter
623
ASSERT_EQ(StringUtil::JoinString(list, ','), "apple,banana,cherry");
624
ASSERT_EQ(StringUtil::JoinString(list, ' '), "apple banana cherry");
625
626
// Test with string delimiter
627
ASSERT_EQ(StringUtil::JoinString(list, ", "), "apple, banana, cherry");
628
ASSERT_EQ(StringUtil::JoinString(list, " and "), "apple and banana and cherry");
629
630
// Test with iterator range
631
ASSERT_EQ(StringUtil::JoinString(list.begin(), list.end(), ','), "apple,banana,cherry");
632
633
// Test empty list
634
std::vector<std::string> empty_list;
635
ASSERT_EQ(StringUtil::JoinString(empty_list, ','), "");
636
637
// Test single item
638
std::vector<std::string> single = {"apple"};
639
ASSERT_EQ(StringUtil::JoinString(single, ','), "apple");
640
}
641
642
TEST(StringUtil, ReplaceAll)
643
{
644
// Test string return version
645
ASSERT_EQ(StringUtil::ReplaceAll("hello world", "world", "universe"), "hello universe");
646
ASSERT_EQ(StringUtil::ReplaceAll("test test test", "test", "exam"), "exam exam exam");
647
ASSERT_EQ(StringUtil::ReplaceAll("abcdef", "xyz", "123"), "abcdef"); // No match
648
ASSERT_EQ(StringUtil::ReplaceAll("", "test", "exam"), "");
649
ASSERT_EQ(StringUtil::ReplaceAll("test", "", "exam"), "test"); // Empty search
650
651
// Test in-place version
652
std::string s = "hello world";
653
StringUtil::ReplaceAll(&s, "world", "universe");
654
ASSERT_EQ(s, "hello universe");
655
656
// Test char versions
657
ASSERT_EQ(StringUtil::ReplaceAll("a,b,c", ',', ';'), "a;b;c");
658
659
s = "a,b,c";
660
StringUtil::ReplaceAll(&s, ',', ';');
661
ASSERT_EQ(s, "a;b;c");
662
}
663
664
TEST(StringUtil, ParseAssignmentString)
665
{
666
std::string_view key, value;
667
668
// Test normal assignment
669
ASSERT_TRUE(StringUtil::ParseAssignmentString("key=value", &key, &value));
670
ASSERT_EQ(key, "key");
671
ASSERT_EQ(value, "value");
672
673
// Test with spaces
674
ASSERT_TRUE(StringUtil::ParseAssignmentString(" key = value ", &key, &value));
675
ASSERT_EQ(key, "key");
676
ASSERT_EQ(value, "value");
677
678
// Test empty value
679
ASSERT_TRUE(StringUtil::ParseAssignmentString("key=", &key, &value));
680
ASSERT_EQ(key, "key");
681
ASSERT_EQ(value, "");
682
683
// Test no equals sign
684
ASSERT_FALSE(StringUtil::ParseAssignmentString("keyvalue", &key, &value));
685
686
// Test empty string
687
ASSERT_FALSE(StringUtil::ParseAssignmentString("", &key, &value));
688
689
// Test only equals
690
ASSERT_TRUE(StringUtil::ParseAssignmentString("=", &key, &value));
691
ASSERT_EQ(key, "");
692
ASSERT_EQ(value, "");
693
}
694
695
TEST(StringUtil, GetNextToken)
696
{
697
std::string_view caret = "a,b,c,d";
698
699
auto token = StringUtil::GetNextToken(caret, ',');
700
ASSERT_TRUE(token.has_value());
701
ASSERT_EQ(*token, "a");
702
ASSERT_EQ(caret, "b,c,d");
703
704
token = StringUtil::GetNextToken(caret, ',');
705
ASSERT_TRUE(token.has_value());
706
ASSERT_EQ(*token, "b");
707
ASSERT_EQ(caret, "c,d");
708
709
token = StringUtil::GetNextToken(caret, ',');
710
ASSERT_TRUE(token.has_value());
711
ASSERT_EQ(*token, "c");
712
ASSERT_EQ(caret, "d");
713
714
token = StringUtil::GetNextToken(caret, ',');
715
ASSERT_FALSE(token.has_value());
716
ASSERT_EQ(caret, "d");
717
}
718
719
TEST(StringUtil, EncodeAndAppendUTF8)
720
{
721
std::string s;
722
723
// Test ASCII character
724
StringUtil::EncodeAndAppendUTF8(s, U'A');
725
ASSERT_EQ(s, "A");
726
727
// Test 2-byte UTF-8
728
s.clear();
729
StringUtil::EncodeAndAppendUTF8(s, U'ñ'); // U+00F1
730
ASSERT_EQ(s.size(), 2u);
731
732
// Test 3-byte UTF-8
733
s.clear();
734
StringUtil::EncodeAndAppendUTF8(s, U'€'); // U+20AC
735
ASSERT_EQ(s.size(), 3u);
736
737
// Test 4-byte UTF-8
738
s.clear();
739
StringUtil::EncodeAndAppendUTF8(s, U'💖'); // U+1F496
740
ASSERT_EQ(s.size(), 4u);
741
742
// Test invalid character (should encode replacement character)
743
s.clear();
744
StringUtil::EncodeAndAppendUTF8(s, 0x110000); // Invalid
745
ASSERT_EQ(s.size(), 3u); // Replacement character is 3 bytes
746
747
// Test buffer version
748
u8 buffer[10] = {0};
749
size_t written = StringUtil::EncodeAndAppendUTF8(buffer, 0, sizeof(buffer), U'A');
750
ASSERT_EQ(written, 1u);
751
ASSERT_EQ(buffer[0], 'A');
752
753
written = StringUtil::EncodeAndAppendUTF8(buffer, 1, sizeof(buffer), U'€');
754
ASSERT_EQ(written, 3u);
755
756
// Test buffer overflow
757
written = StringUtil::EncodeAndAppendUTF8(buffer, 9, sizeof(buffer), U'💖');
758
ASSERT_EQ(written, 0u); // Should fail due to insufficient space
759
}
760
761
TEST(StringUtil, GetEncodedUTF8Length)
762
{
763
ASSERT_EQ(StringUtil::GetEncodedUTF8Length(U'A'), 1u); // ASCII
764
ASSERT_EQ(StringUtil::GetEncodedUTF8Length(U'ñ'), 2u); // 2-byte
765
ASSERT_EQ(StringUtil::GetEncodedUTF8Length(U'€'), 3u); // 3-byte
766
ASSERT_EQ(StringUtil::GetEncodedUTF8Length(U'💖'), 4u); // 4-byte
767
ASSERT_EQ(StringUtil::GetEncodedUTF8Length(0x110000), 3u); // Invalid -> replacement
768
}
769
770
TEST(StringUtil, DecodeUTF8)
771
{
772
// Test ASCII
773
char32_t ch;
774
size_t len = StringUtil::DecodeUTF8("A", 0, &ch);
775
ASSERT_EQ(len, 1u);
776
ASSERT_EQ(ch, U'A');
777
778
// Test 2-byte UTF-8 (ñ = C3 B1)
779
std::string utf8_2byte = "\xC3\xB1";
780
len = StringUtil::DecodeUTF8(utf8_2byte, 0, &ch);
781
ASSERT_EQ(len, 2u);
782
ASSERT_EQ(ch, U'ñ');
783
784
// Test 3-byte UTF-8 (€ = E2 82 AC)
785
std::string utf8_3byte = "\xE2\x82\xAC";
786
len = StringUtil::DecodeUTF8(utf8_3byte, 0, &ch);
787
ASSERT_EQ(len, 3u);
788
ASSERT_EQ(ch, U'€');
789
790
// Test void* version
791
len = StringUtil::DecodeUTF8(utf8_3byte.data(), utf8_3byte.size(), &ch);
792
ASSERT_EQ(len, 3u);
793
ASSERT_EQ(ch, U'€');
794
795
// Test invalid UTF-8 sequence
796
std::string invalid_utf8 = "\xFF\xFE";
797
len = StringUtil::DecodeUTF8(invalid_utf8.data(), invalid_utf8.size(), &ch);
798
ASSERT_EQ(len, 1u);
799
ASSERT_EQ(ch, StringUtil::UNICODE_REPLACEMENT_CHARACTER);
800
}
801
802
TEST(StringUtil, EncodeAndAppendUTF16)
803
{
804
// Test ASCII character
805
u16 buffer[10] = {0};
806
size_t written = StringUtil::EncodeAndAppendUTF16(buffer, 0, 10, U'A');
807
ASSERT_EQ(written, 1u);
808
ASSERT_EQ(buffer[0], u16('A'));
809
810
// Test basic multi-byte character
811
written = StringUtil::EncodeAndAppendUTF16(buffer, 1, 10, U'€'); // U+20AC
812
ASSERT_EQ(written, 1u);
813
ASSERT_EQ(buffer[1], u16(0x20AC));
814
815
// Test surrogate pair (4-byte UTF-8 character)
816
written = StringUtil::EncodeAndAppendUTF16(buffer, 2, 10, U'💖'); // U+1F496
817
ASSERT_EQ(written, 2u);
818
// Should encode as surrogate pair: High surrogate D83D, Low surrogate DC96
819
ASSERT_EQ(buffer[2], u16(0xD83D));
820
ASSERT_EQ(buffer[3], u16(0xDC96));
821
822
// Test invalid surrogate range (should become replacement character)
823
written = StringUtil::EncodeAndAppendUTF16(buffer, 4, 10, 0xD800); // In surrogate range
824
ASSERT_EQ(written, 1u);
825
ASSERT_EQ(buffer[4], u16(StringUtil::UNICODE_REPLACEMENT_CHARACTER));
826
827
// Test invalid codepoint (should become replacement character)
828
written = StringUtil::EncodeAndAppendUTF16(buffer, 5, 10, 0x110000); // Invalid codepoint
829
ASSERT_EQ(written, 1u);
830
ASSERT_EQ(buffer[5], u16(StringUtil::UNICODE_REPLACEMENT_CHARACTER));
831
832
// Test buffer overflow
833
written = StringUtil::EncodeAndAppendUTF16(buffer, 9, 10, U'💖'); // Needs 2 units but only 1 available
834
ASSERT_EQ(written, 0u);
835
}
836
837
TEST(StringUtil, DecodeUTF16)
838
{
839
// Test ASCII character
840
u16 ascii_data[] = {u16('A')};
841
char32_t ch;
842
size_t len = StringUtil::DecodeUTF16(ascii_data, 0, 1, &ch);
843
ASSERT_EQ(len, 1u);
844
ASSERT_EQ(ch, U'A');
845
846
// Test basic multi-byte character
847
u16 euro_data[] = {u16(0x20AC)}; // €
848
len = StringUtil::DecodeUTF16(euro_data, 0, 1, &ch);
849
ASSERT_EQ(len, 1u);
850
ASSERT_EQ(ch, U'€');
851
852
// Test surrogate pair
853
u16 emoji_data[] = {u16(0xD83D), u16(0xDC96)}; // 💖
854
len = StringUtil::DecodeUTF16(emoji_data, 0, 2, &ch);
855
ASSERT_EQ(len, 2u);
856
ASSERT_EQ(ch, U'💖');
857
858
// Test invalid high surrogate (missing low surrogate)
859
u16 invalid_high[] = {u16(0xD83D)};
860
len = StringUtil::DecodeUTF16(invalid_high, 0, 1, &ch);
861
ASSERT_EQ(len, 1u);
862
ASSERT_EQ(ch, StringUtil::UNICODE_REPLACEMENT_CHARACTER);
863
864
// Test invalid high surrogate followed by invalid low surrogate
865
u16 invalid_surrogates[] = {u16(0xD83D), u16(0x0041)}; // High surrogate followed by 'A'
866
len = StringUtil::DecodeUTF16(invalid_surrogates, 0, 2, &ch);
867
ASSERT_EQ(len, 2u);
868
ASSERT_EQ(ch, StringUtil::UNICODE_REPLACEMENT_CHARACTER);
869
}
870
871
TEST(StringUtil, DecodeUTF16BE)
872
{
873
// Test with byte-swapped data (big-endian)
874
alignas(alignof(u16)) static constexpr const u8 be_data[] = {0x20, 0xAC}; // 0x20AC (€) byte-swapped
875
char32_t ch;
876
size_t len = StringUtil::DecodeUTF16BE(be_data, 0, sizeof(be_data), &ch);
877
ASSERT_EQ(len, 1u);
878
ASSERT_EQ(ch, U'€');
879
880
// Test surrogate pair with byte swapping
881
alignas(alignof(u16)) static constexpr const u8 be_emoji_data[] = {0xD8, 0x3D, 0xDC, 0x96}; // D83D DC96 byte-swapped
882
len = StringUtil::DecodeUTF16BE(be_emoji_data, 0, 2, &ch);
883
ASSERT_EQ(len, 2u);
884
ASSERT_EQ(ch, U'💖');
885
}
886
887
TEST(StringUtil, DecodeUTF16String)
888
{
889
// Test simple ASCII string
890
u16 ascii_utf16[] = {u16('H'), u16('e'), u16('l'), u16('l'), u16('o')};
891
std::string result = StringUtil::DecodeUTF16String(ascii_utf16, sizeof(ascii_utf16));
892
ASSERT_EQ(result, "Hello");
893
894
// Test string with multi-byte characters
895
u16 mixed_utf16[] = {u16('H'), u16('e'), u16('l'), u16('l'), u16('o'), u16(0x20AC)}; // Hello€
896
result = StringUtil::DecodeUTF16String(mixed_utf16, sizeof(mixed_utf16));
897
ASSERT_EQ(result.size(), 8u); // 5 ASCII + 3 bytes for €
898
ASSERT_TRUE(result.starts_with("Hello"));
899
900
// Test with surrogate pairs
901
u16 emoji_utf16[] = {u16('H'), u16('i'), u16(0xD83D), u16(0xDC96)}; // Hi💖
902
result = StringUtil::DecodeUTF16String(emoji_utf16, sizeof(emoji_utf16));
903
ASSERT_EQ(result.size(), 6u); // 2 ASCII + 4 bytes for 💖
904
ASSERT_TRUE(result.starts_with("Hi"));
905
}
906
907
TEST(StringUtil, DecodeUTF16BEString)
908
{
909
// Test with byte-swapped data
910
u16 be_utf16[] = {0x4800, 0x6500}; // "He" in big-endian
911
std::string result = StringUtil::DecodeUTF16BEString(be_utf16, sizeof(be_utf16));
912
ASSERT_EQ(result, "He");
913
914
// Test with multi-byte character
915
u16 be_euro[] = {0x3D20}; // € in big-endian
916
result = StringUtil::DecodeUTF16BEString(be_euro, sizeof(be_euro));
917
ASSERT_EQ(result.size(), 3u); // € is 3 bytes in UTF-8
918
}
919
920
TEST(StringUtil, BytePatternSearch)
921
{
922
std::vector<u8> data = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
923
924
// Test exact match
925
auto result = StringUtil::BytePatternSearch(std::span<const u8>(data), "01 02 03");
926
ASSERT_TRUE(result.has_value());
927
ASSERT_EQ(result.value(), 0u);
928
929
// Test match in middle
930
result = StringUtil::BytePatternSearch(std::span<const u8>(data), "03 04 05");
931
ASSERT_TRUE(result.has_value());
932
ASSERT_EQ(result.value(), 2u);
933
934
// Test with wildcards
935
result = StringUtil::BytePatternSearch(std::span<const u8>(data), "01 ?? 03");
936
ASSERT_TRUE(result.has_value());
937
ASSERT_EQ(result.value(), 0u);
938
939
// Test no match
940
result = StringUtil::BytePatternSearch(std::span<const u8>(data), "FF FF FF");
941
ASSERT_FALSE(result.has_value());
942
943
// Test empty pattern
944
result = StringUtil::BytePatternSearch(std::span<const u8>(data), "");
945
ASSERT_FALSE(result.has_value());
946
947
// Test lowercase hex
948
result = StringUtil::BytePatternSearch(std::span<const u8>(data), "01 02 03");
949
ASSERT_TRUE(result.has_value());
950
ASSERT_EQ(result.value(), 0u);
951
952
// Test mixed case
953
result = StringUtil::BytePatternSearch(std::span<const u8>(data), "01 ?? 03");
954
ASSERT_TRUE(result.has_value());
955
ASSERT_EQ(result.value(), 0u);
956
}
957
958
TEST(StringUtil, StrideMemCpy)
959
{
960
static constexpr const u8 src[] = {1, 2, 3, 4, 5, 6, 7, 8};
961
u8 dst[8] = {0};
962
963
// Test normal memcpy (same stride and copy size)
964
StringUtil::StrideMemCpy(dst, 2, src, 2, 2, 4);
965
ASSERT_EQ(dst[0], 1);
966
ASSERT_EQ(dst[1], 2);
967
ASSERT_EQ(dst[2], 3);
968
ASSERT_EQ(dst[3], 4);
969
970
// Reset and test different strides
971
memset(dst, 0, sizeof(dst));
972
StringUtil::StrideMemCpy(dst, 3, src, 2, 1, 3);
973
ASSERT_EQ(dst[0], 1);
974
ASSERT_EQ(dst[3], 3);
975
ASSERT_EQ(dst[6], 5);
976
}
977
978
TEST(StringUtil, StrideMemCmp)
979
{
980
static constexpr const u8 data1[] = {1, 0, 2, 0, 3, 0};
981
u8 data2[] = {1, 2, 3};
982
983
// Test equal comparison with different strides
984
int result = StringUtil::StrideMemCmp(data1, 2, data2, 1, 1, 3);
985
ASSERT_EQ(result, 0);
986
987
// Test unequal comparison
988
data2[1] = 4;
989
result = StringUtil::StrideMemCmp(data1, 2, data2, 1, 1, 3);
990
ASSERT_NE(result, 0);
991
}
992
993
#ifdef _WIN32
994
TEST(StringUtil, UTF8StringToWideString)
995
{
996
std::wstring result = StringUtil::UTF8StringToWideString("Hello");
997
ASSERT_EQ(result, L"Hello");
998
999
// Test with UTF-8 characters
1000
std::wstring utf8_result = StringUtil::UTF8StringToWideString("Héllo");
1001
ASSERT_FALSE(utf8_result.empty());
1002
1003
// Test bool version
1004
std::wstring dest;
1005
bool success = StringUtil::UTF8StringToWideString(dest, "Hello");
1006
ASSERT_TRUE(success);
1007
ASSERT_EQ(dest, L"Hello");
1008
}
1009
1010
TEST(StringUtil, WideStringToUTF8String)
1011
{
1012
std::string result = StringUtil::WideStringToUTF8String(L"Hello");
1013
ASSERT_EQ(result, "Hello");
1014
1015
// Test bool version
1016
std::string dest;
1017
bool success = StringUtil::WideStringToUTF8String(dest, L"Hello");
1018
ASSERT_TRUE(success);
1019
ASSERT_EQ(dest, "Hello");
1020
}
1021
#endif
1022
1023