Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/tests/core/string/test_string.cpp
45997 views
1
/**************************************************************************/
2
/* test_string.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "tests/test_macros.h"
32
33
TEST_FORCE_LINK(test_string)
34
35
#include "core/string/ustring.h"
36
37
namespace TestString {
38
39
int u32scmp(const char32_t *l, const char32_t *r) {
40
for (; *l == *r && *l && *r; l++, r++) {
41
// Continue.
42
}
43
return *l - *r;
44
}
45
46
TEST_CASE("[String] Assign Latin-1 char string") {
47
String s = "Hello";
48
CHECK(u32scmp(s.get_data(), U"Hello") == 0);
49
}
50
51
TEST_CASE("[String] Assign from Latin-1 char string (operator=)") {
52
String s = "Dolly";
53
const String &t = s;
54
CHECK(u32scmp(t.get_data(), U"Dolly") == 0);
55
}
56
57
TEST_CASE("[String] Assign from Latin-1 char string (copycon)") {
58
String s("Sheep");
59
const String &t1(s);
60
CHECK(u32scmp(t1.get_data(), U"Sheep") == 0);
61
62
String t2 = String::latin1(Span("Sheep", 3));
63
CHECK(u32scmp(t2.get_data(), U"She") == 0);
64
}
65
66
TEST_CASE("[String] Assign from wchar_t string (operator=)") {
67
String s = L"Give me";
68
CHECK(u32scmp(s.get_data(), U"Give me") == 0);
69
}
70
71
TEST_CASE("[String] Assign from wchar_t string (copycon)") {
72
String s(L"Wool");
73
CHECK(u32scmp(s.get_data(), U"Wool") == 0);
74
}
75
76
TEST_CASE("[String] Assign from char32_t string (operator=)") {
77
String s = U"Give me";
78
CHECK(u32scmp(s.get_data(), U"Give me") == 0);
79
}
80
81
TEST_CASE("[String] Assign from char32_t string (copycon)") {
82
String s(U"Wool");
83
CHECK(u32scmp(s.get_data(), U"Wool") == 0);
84
}
85
86
TEST_CASE("[String] UTF8") {
87
/* how can i embed UTF in here? */
88
static const char32_t u32str[] = { 0x0045, 0x0020, 0x304A, 0x360F, 0x3088, 0x3046, 0x1F3A4, 0 };
89
static const uint8_t u8str[] = { 0x45, 0x20, 0xE3, 0x81, 0x8A, 0xE3, 0x98, 0x8F, 0xE3, 0x82, 0x88, 0xE3, 0x81, 0x86, 0xF0, 0x9F, 0x8E, 0xA4, 0 };
90
String expected = u32str;
91
String parsed;
92
Error err = parsed.append_utf8(expected.utf8().get_data());
93
CHECK(err == OK);
94
CHECK(parsed == u32str);
95
96
parsed.clear();
97
err = parsed.append_utf8((const char *)u8str);
98
CHECK(err == OK);
99
CHECK(parsed == u32str);
100
101
CharString cs = (const char *)u8str;
102
CHECK(String::utf8(cs) == parsed);
103
}
104
105
TEST_CASE("[String] UTF16") {
106
/* how can i embed UTF in here? */
107
static const char32_t u32str[] = { 0x0045, 0x0020, 0x304A, 0x360F, 0x3088, 0x3046, 0x1F3A4, 0 };
108
static const char16_t u16str[] = { 0x0045, 0x0020, 0x304A, 0x360F, 0x3088, 0x3046, 0xD83C, 0xDFA4, 0 };
109
String expected = u32str;
110
String parsed;
111
Error err = parsed.append_utf16(expected.utf16().get_data());
112
CHECK(err == OK);
113
CHECK(parsed == u32str);
114
115
parsed.clear();
116
err = parsed.append_utf16(u16str);
117
CHECK(err == OK);
118
CHECK(parsed == u32str);
119
120
Char16String cs = u16str;
121
CHECK(String::utf16(cs) == parsed);
122
}
123
124
TEST_CASE("[String] UTF8 with BOM") {
125
/* how can i embed UTF in here? */
126
static const char32_t u32str[] = { 0x0045, 0x0020, 0x304A, 0x360F, 0x3088, 0x3046, 0x1F3A4, 0 };
127
static const uint8_t u8str[] = { 0xEF, 0xBB, 0xBF, 0x45, 0x20, 0xE3, 0x81, 0x8A, 0xE3, 0x98, 0x8F, 0xE3, 0x82, 0x88, 0xE3, 0x81, 0x86, 0xF0, 0x9F, 0x8E, 0xA4, 0 };
128
String s;
129
Error err = s.append_utf8((const char *)u8str);
130
CHECK(err == OK);
131
CHECK(s == u32str);
132
133
CharString cs = (const char *)u8str;
134
CHECK(String::utf8(cs) == s);
135
}
136
137
TEST_CASE("[String] UTF16 with BOM") {
138
/* how can i embed UTF in here? */
139
static const char32_t u32str[] = { 0x0020, 0x0045, 0x304A, 0x360F, 0x3088, 0x3046, 0x1F3A4, 0 };
140
static const char16_t u16str[] = { 0xFEFF, 0x0020, 0x0045, 0x304A, 0x360F, 0x3088, 0x3046, 0xD83C, 0xDFA4, 0 };
141
static const char16_t u16str_swap[] = { 0xFFFE, 0x2000, 0x4500, 0x4A30, 0x0F36, 0x8830, 0x4630, 0x3CD8, 0xA4DF, 0 };
142
String s;
143
Error err = s.append_utf16(u16str);
144
CHECK(err == OK);
145
CHECK(s == u32str);
146
147
s.clear();
148
err = s.append_utf16(u16str_swap);
149
CHECK(err == OK);
150
CHECK(s == u32str);
151
152
Char16String cs = u16str;
153
CHECK(String::utf16(cs) == s);
154
155
cs = u16str_swap;
156
CHECK(String::utf16(cs) == s);
157
}
158
159
TEST_CASE("[String] Invalid UTF8 (non shortest form sequence)") {
160
ERR_PRINT_OFF
161
// Examples from the unicode standard : 3.9 Unicode Encoding Forms - Table 3.8.
162
static const uint8_t u8str[] = { 0xC0, 0xAF, 0xE0, 0x80, 0xBF, 0xF0, 0x81, 0x82, 0x41, 0 };
163
static const char32_t u32str[] = { 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x41, 0 };
164
String s;
165
Error err = s.append_utf8((const char *)u8str);
166
CHECK(err == ERR_INVALID_DATA);
167
CHECK(s == u32str);
168
169
CharString cs = (const char *)u8str;
170
CHECK(String::utf8(cs) == s);
171
ERR_PRINT_ON
172
}
173
174
TEST_CASE("[String] Invalid UTF8 (ill formed sequences for surrogates)") {
175
ERR_PRINT_OFF
176
// Examples from the unicode standard : 3.9 Unicode Encoding Forms - Table 3.9.
177
static const uint8_t u8str[] = { 0xED, 0xA0, 0x80, 0xED, 0xBF, 0xBF, 0xED, 0xAF, 0x41, 0 };
178
static const char32_t u32str[] = { 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x41, 0 };
179
String s;
180
Error err = s.append_utf8((const char *)u8str);
181
CHECK(err == ERR_INVALID_DATA);
182
CHECK(s == u32str);
183
184
CharString cs = (const char *)u8str;
185
CHECK(String::utf8(cs) == s);
186
ERR_PRINT_ON
187
}
188
189
TEST_CASE("[String] Invalid UTF8 (other ill formed sequences)") {
190
ERR_PRINT_OFF
191
// Examples from the unicode standard : 3.9 Unicode Encoding Forms - Table 3.10.
192
static const uint8_t u8str[] = { 0xF4, 0x91, 0x92, 0x93, 0xFF, 0x41, 0x80, 0xBF, 0x42, 0 };
193
static const char32_t u32str[] = { 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x41, 0xFFFD, 0xFFFD, 0x42, 0 };
194
String s;
195
Error err = s.append_utf8((const char *)u8str);
196
CHECK(err == ERR_INVALID_DATA);
197
CHECK(s == u32str);
198
199
CharString cs = (const char *)u8str;
200
CHECK(String::utf8(cs) == s);
201
ERR_PRINT_ON
202
}
203
204
TEST_CASE("[String] Invalid UTF8 (truncated sequences)") {
205
ERR_PRINT_OFF
206
// Examples from the unicode standard : 3.9 Unicode Encoding Forms - Table 3.11.
207
static const uint8_t u8str[] = { 0xE1, 0x80, 0xE2, 0xF0, 0x91, 0x92, 0xF1, 0xBF, 0x41, 0 };
208
static const char32_t u32str[] = { 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x41, 0 };
209
String s;
210
Error err = s.append_utf8((const char *)u8str);
211
CHECK(err == ERR_INVALID_DATA);
212
CHECK(s == u32str);
213
214
CharString cs = (const char *)u8str;
215
CHECK(String::utf8(cs) == s);
216
ERR_PRINT_ON
217
}
218
219
TEST_CASE("[String] Invalid UTF16 (non-standard)") {
220
ERR_PRINT_OFF
221
static const char16_t u16str[] = { 0x0045, 0x304A, 0x3088, 0x3046, 0xDFA4, 0 };
222
// + + + + unpaired
223
static const char32_t u32str[] = { 0x0045, 0x304A, 0x3088, 0x3046, 0xDFA4, 0 };
224
String s;
225
Error err = s.append_utf16(u16str);
226
CHECK(err == ERR_PARSE_ERROR);
227
CHECK(s == u32str);
228
229
Char16String cs = u16str;
230
CHECK(String::utf16(cs) == s);
231
ERR_PRINT_ON
232
}
233
234
TEST_CASE("[String] ASCII") {
235
String s = U"Primero Leche";
236
String t = s.ascii(false).get_data();
237
CHECK(s == t);
238
239
t = s.ascii(true).get_data();
240
CHECK(s == t);
241
}
242
243
TEST_CASE("[String] Comparisons (equal)") {
244
String s = "Test Compare";
245
CHECK(s == "Test Compare");
246
CHECK(s == U"Test Compare");
247
CHECK(s == L"Test Compare");
248
CHECK(s == String("Test Compare"));
249
250
CharString empty = "";
251
CharString cs = "Test Compare";
252
CHECK(!(empty == cs));
253
CHECK(!(cs == empty));
254
CHECK(cs == CharString("Test Compare"));
255
}
256
257
TEST_CASE("[String] Comparisons (not equal)") {
258
String s = "Test Compare";
259
CHECK(s != "Peanut");
260
CHECK(s != U"Coconut");
261
CHECK(s != L"Coconut");
262
CHECK(s != String("Butter"));
263
}
264
265
TEST_CASE("[String] Comparisons (operator <)") {
266
String s = "Bees";
267
CHECK(s < "Elephant");
268
CHECK(!(s < U"Amber"));
269
CHECK(!(s < L"Amber"));
270
CHECK(!(s < String("Beatrix")));
271
}
272
273
TEST_CASE("[String] Concatenation") {
274
String s;
275
276
s += "Have";
277
s += ' ';
278
s += 'a';
279
s += String(" ");
280
s = s + U"Nice";
281
s = s + " ";
282
s = s + String("Day");
283
284
CHECK(s == "Have a Nice Day");
285
}
286
287
TEST_CASE("[String] Testing size and length of string") {
288
// todo: expand this test to do more tests on size() as it is complicated under the hood.
289
CHECK(String("Mellon").size() == 7);
290
CHECK(String("Mellon1").size() == 8);
291
292
// length works fine and is easier to test
293
CHECK(String("Mellon").length() == 6);
294
CHECK(String("Mellon1").length() == 7);
295
CHECK(String("Mellon2").length() == 7);
296
CHECK(String("Mellon3").length() == 7);
297
}
298
299
TEST_CASE("[String] Testing for empty string") {
300
CHECK(!String("Mellon").is_empty());
301
// do this more than once, to check for string corruption
302
CHECK(String("").is_empty());
303
CHECK(String("").is_empty());
304
CHECK(String("").is_empty());
305
}
306
307
TEST_CASE("[String] Contains") {
308
String s = "C:\\Godot\\project\\string_test.tscn";
309
CHECK(s.contains(":\\"));
310
CHECK(s.contains("Godot"));
311
CHECK(s.contains(String("project\\string_test")));
312
CHECK(s.contains(String("\\string_test.tscn")));
313
314
CHECK(!s.contains("://"));
315
CHECK(!s.contains("Godoh"));
316
CHECK(!s.contains(String("project\\string test")));
317
CHECK(!s.contains(String("\\char_test.tscn")));
318
}
319
320
TEST_CASE("[String] Contains case insensitive") {
321
String s = "C:\\Godot\\project\\string_test.tscn";
322
CHECK(s.containsn("Godot"));
323
CHECK(s.containsn("godot"));
324
CHECK(s.containsn(String("Project\\string_test")));
325
CHECK(s.containsn(String("\\string_Test.tscn")));
326
327
CHECK(!s.containsn("Godoh"));
328
CHECK(!s.containsn("godoh"));
329
CHECK(!s.containsn(String("project\\string test")));
330
CHECK(!s.containsn(String("\\char_test.tscn")));
331
}
332
333
TEST_CASE("[String] Test chr") {
334
CHECK(String::chr('H') == "H");
335
CHECK(String::chr(0x3012)[0] == 0x3012);
336
ERR_PRINT_OFF
337
CHECK(String::chr(0xd812)[0] == 0xfffd); // Unpaired UTF-16 surrogate
338
CHECK(String::chr(0x20d812)[0] == 0xfffd); // Outside UTF-32 range
339
ERR_PRINT_ON
340
}
341
342
TEST_CASE("[String] Operator []") {
343
String a = "Kugar Sane";
344
a[0] = 'S';
345
a[6] = 'C';
346
CHECK(a == "Sugar Cane");
347
CHECK(a[1] == 'u');
348
CHECK(a.unicode_at(1) == 'u');
349
}
350
351
TEST_CASE("[String] Case function test") {
352
String a = "MoMoNgA";
353
354
CHECK(a.to_upper() == "MOMONGA");
355
CHECK(a.to_lower() == "momonga");
356
}
357
358
TEST_CASE("[String] Case compare function test") {
359
String a = "MoMoNgA";
360
361
CHECK(a.casecmp_to("momonga") != 0);
362
CHECK(a.nocasecmp_to("momonga") == 0);
363
}
364
365
TEST_CASE("[String] Natural compare function test") {
366
String a = "img2.png";
367
368
CHECK(a.nocasecmp_to("img10.png") > 0);
369
CHECK(a.naturalnocasecmp_to("img10.png") < 0);
370
}
371
372
TEST_CASE("[String] File compare function test") {
373
String a = "_img2.png";
374
String b = "img2.png";
375
376
CHECK(a.nocasecmp_to("img10.png") > 0);
377
CHECK_MESSAGE(a.filenocasecmp_to("img10.png") < 0, "Should sort before letters.");
378
CHECK_MESSAGE(a.filenocasecmp_to(".img10.png") > 0, "Should sort after period.");
379
CHECK(b.filenocasecmp_to("img3.png") < 0);
380
}
381
382
TEST_CASE("[String] hex_encode_buffer") {
383
static const uint8_t u8str[] = { 0x45, 0xE3, 0x81, 0x8A, 0x8F, 0xE3 };
384
String s = String::hex_encode_buffer(u8str, 6);
385
CHECK(s == U"45e3818a8fe3");
386
}
387
388
TEST_CASE("[String] Substr") {
389
String s = "Killer Baby";
390
CHECK(s.substr(3, 4) == "ler ");
391
CHECK(s.substr(3) == "ler Baby");
392
}
393
394
TEST_CASE("[String] Find") {
395
String s = "Pretty Woman Woman";
396
MULTICHECK_STRING_EQ(s, find, "tty", 3);
397
MULTICHECK_STRING_EQ(s, find, "Revenge of the Monster Truck", -1);
398
MULTICHECK_STRING_INT_EQ(s, find, "Wo", 9, 13);
399
MULTICHECK_STRING_INT_EQ(s, find, "Wo", 1000, -1);
400
MULTICHECK_STRING_INT_EQ(s, find, "Wo", -1, -1);
401
MULTICHECK_STRING_EQ(s, find, "", -1);
402
MULTICHECK_STRING_EQ(s, find, "Pretty Woman Woman", 0);
403
MULTICHECK_STRING_EQ(s, find, "WOMAN", -1);
404
MULTICHECK_STRING_INT_EQ(s, find, "", 9, -1);
405
406
MULTICHECK_STRING_EQ(s, rfind, "", -1);
407
MULTICHECK_STRING_EQ(s, rfind, "foo", -1);
408
MULTICHECK_STRING_EQ(s, rfind, "Pretty Woman Woman", 0);
409
MULTICHECK_STRING_EQ(s, rfind, "man", 15);
410
MULTICHECK_STRING_EQ(s, rfind, "WOMAN", -1);
411
MULTICHECK_STRING_INT_EQ(s, rfind, "", 15, -1);
412
MULTICHECK_STRING_INT_EQ(s, rfind, "Wo", 1000, -1);
413
MULTICHECK_STRING_INT_EQ(s, rfind, "Wo", -1, 13);
414
}
415
416
TEST_CASE("[String] Find character") {
417
String s = "racecar";
418
CHECK_EQ(s.find_char('r'), 0);
419
CHECK_EQ(s.find_char('r', 1), 6);
420
CHECK_EQ(s.find_char('e'), 3);
421
CHECK_EQ(s.find_char('e', 4), -1);
422
423
CHECK_EQ(s.rfind_char('r'), 6);
424
CHECK_EQ(s.rfind_char('r', 5), 0);
425
CHECK_EQ(s.rfind_char('e'), 3);
426
CHECK_EQ(s.rfind_char('e', 2), -1);
427
}
428
429
TEST_CASE("[String] Find case insensitive") {
430
String s = "Pretty Whale Whale";
431
MULTICHECK_STRING_EQ(s, findn, "WHA", 7);
432
MULTICHECK_STRING_INT_EQ(s, findn, "WHA", 9, 13);
433
MULTICHECK_STRING_INT_EQ(s, findn, "WHA", 1000, -1);
434
MULTICHECK_STRING_INT_EQ(s, findn, "WHA", -1, -1);
435
MULTICHECK_STRING_EQ(s, findn, "Revenge of the Monster SawFish", -1);
436
MULTICHECK_STRING_EQ(s, findn, "", -1);
437
MULTICHECK_STRING_EQ(s, findn, "wha", 7);
438
MULTICHECK_STRING_EQ(s, findn, "Wha", 7);
439
MULTICHECK_STRING_INT_EQ(s, findn, "", 3, -1);
440
441
MULTICHECK_STRING_EQ(s, rfindn, "WHA", 13);
442
MULTICHECK_STRING_EQ(s, rfindn, "", -1);
443
MULTICHECK_STRING_EQ(s, rfindn, "wha", 13);
444
MULTICHECK_STRING_EQ(s, rfindn, "Wha", 13);
445
MULTICHECK_STRING_INT_EQ(s, rfindn, "", 13, -1);
446
MULTICHECK_STRING_INT_EQ(s, rfindn, "WHA", 1000, -1);
447
MULTICHECK_STRING_INT_EQ(s, rfindn, "WHA", -1, 13);
448
}
449
450
TEST_CASE("[String] Find MK") {
451
Vector<String> keys;
452
keys.push_back("sty");
453
keys.push_back("tty");
454
keys.push_back("man");
455
456
String s = "Pretty Woman";
457
int key = 0;
458
459
CHECK(s.findmk(keys, 0, &key) == 3);
460
CHECK(key == 1);
461
462
CHECK(s.findmk(keys, 5, &key) == 9);
463
CHECK(key == 2);
464
465
CHECK(s.findmk(keys, -1, &key) == -1);
466
CHECK(s.findmk(keys, 1000, &key) == -1);
467
}
468
469
TEST_CASE("[String] Find and replace") {
470
String s = "Happy Birthday, Anna!";
471
MULTICHECK_STRING_STRING_EQ(s, replace, "Birthday", "Halloween", "Happy Halloween, Anna!");
472
MULTICHECK_STRING_STRING_EQ(s, replace_first, "y", "Y", "HappY Birthday, Anna!");
473
MULTICHECK_STRING_STRING_EQ(s, replacen, "Y", "Y", "HappY BirthdaY, Anna!");
474
}
475
476
TEST_CASE("[String] replace_char") {
477
String s = "Banana";
478
CHECK(s.replace_char('n', 'x') == "Baxaxa");
479
CHECK(s.replace_char('\0', 'x') == "Banana");
480
ERR_PRINT_OFF
481
CHECK(s.replace_char('n', '\0') == "Banana");
482
ERR_PRINT_ON
483
}
484
485
TEST_CASE("[String] replace_chars") {
486
String s = "Banana";
487
CHECK(s.replace_chars(String("Bn"), 'x') == "xaxaxa");
488
CHECK(s.replace_chars("Bn", 'x') == "xaxaxa");
489
CHECK(s.replace_chars(String(), 'x') == "Banana");
490
CHECK(s.replace_chars("", 'x') == "Banana");
491
ERR_PRINT_OFF
492
CHECK(s.replace_chars(String("Bn"), '\0') == "Banana");
493
CHECK(s.replace_chars("Bn", '\0') == "Banana");
494
ERR_PRINT_ON
495
}
496
497
TEST_CASE("[String] Insertion") {
498
String s = "Who is Frederic?";
499
s = s.insert(s.find("?"), " Chopin");
500
CHECK(s == "Who is Frederic Chopin?");
501
502
s = "foobar";
503
CHECK(s.insert(0, "X") == "Xfoobar");
504
CHECK(s.insert(-100, "X") == "foobar");
505
CHECK(s.insert(6, "X") == "foobarX");
506
CHECK(s.insert(100, "X") == "foobarX");
507
CHECK(s.insert(2, "") == "foobar");
508
509
s = "";
510
CHECK(s.insert(0, "abc") == "abc");
511
CHECK(s.insert(100, "abc") == "abc");
512
CHECK(s.insert(-100, "abc") == "");
513
CHECK(s.insert(0, "") == "");
514
}
515
516
TEST_CASE("[String] Erasing") {
517
String s = "Josephine is such a cute girl!";
518
s = s.erase(s.find("cute "), String("cute ").length());
519
CHECK(s == "Josephine is such a girl!");
520
}
521
522
TEST_CASE("[String] remove_char") {
523
String s = "Banana";
524
CHECK(s.remove_char('a') == "Bnn");
525
CHECK(s.remove_char('\0') == "Banana");
526
CHECK(s.remove_char('x') == "Banana");
527
}
528
529
TEST_CASE("[String] remove_chars") {
530
String s = "Banana";
531
CHECK(s.remove_chars("Ba") == "nn");
532
CHECK(s.remove_chars(String("Ba")) == "nn");
533
CHECK(s.remove_chars("") == "Banana");
534
CHECK(s.remove_chars(String()) == "Banana");
535
CHECK(s.remove_chars("xy") == "Banana");
536
CHECK(s.remove_chars(String("xy")) == "Banana");
537
}
538
539
TEST_CASE("[String] Number to string") {
540
CHECK(String::num(0) == "0.0"); // The method takes double, so always add zeros.
541
CHECK(String::num(0.0) == "0.0");
542
CHECK(String::num(-0.0) == "-0.0"); // Includes sign even for zero.
543
CHECK(String::num(3.141593) == "3.141593");
544
CHECK(String::num(3.141593, 3) == "3.142");
545
CHECK(String::num(42.100023, 4) == "42.1"); // No trailing zeros.
546
547
// String::num_int64 tests.
548
CHECK(String::num_int64(3141593) == "3141593");
549
CHECK(String::num_int64(-3141593) == "-3141593");
550
CHECK(String::num_int64(0xA141593, 16) == "a141593");
551
CHECK(String::num_int64(0xA141593, 16, true) == "A141593");
552
ERR_PRINT_OFF;
553
CHECK(String::num_int64(3141593, 1) == ""); // Invalid base < 2.
554
CHECK(String::num_int64(3141593, 37) == ""); // Invalid base > 36.
555
ERR_PRINT_ON;
556
557
// String::num_uint64 tests.
558
CHECK(String::num_uint64(4294967295) == "4294967295");
559
CHECK(String::num_uint64(0xF141593, 16) == "f141593");
560
CHECK(String::num_uint64(0xF141593, 16, true) == "F141593");
561
ERR_PRINT_OFF;
562
CHECK(String::num_uint64(4294967295, 1) == ""); // Invalid base < 2.
563
CHECK(String::num_uint64(4294967295, 37) == ""); // Invalid base > 36.
564
ERR_PRINT_ON;
565
566
// String::num_scientific tests.
567
CHECK(String::num_scientific(30000000.0) == "30000000");
568
CHECK(String::num_scientific(1234567890.0) == "1234567890");
569
CHECK(String::num_scientific(3e100) == "3e+100");
570
CHECK(String::num_scientific(7e-100) == "7e-100");
571
CHECK(String::num_scientific(Math::TAU) == "6.283185307179586");
572
CHECK(String::num_scientific(Math::INF) == "inf");
573
CHECK(String::num_scientific(-Math::INF) == "-inf");
574
CHECK(String::num_scientific(Math::NaN) == "nan");
575
CHECK(String::num_scientific(2.0) == "2");
576
CHECK(String::num_scientific(1.0) == "1");
577
CHECK(String::num_scientific(0.0) == "0");
578
CHECK(String::num_scientific(-0.0) == "-0");
579
580
// String::num_real tests.
581
CHECK(String::num_real(1.0) == "1.0");
582
CHECK(String::num_real(1.0, false) == "1");
583
CHECK(String::num_real(9.9) == "9.9");
584
CHECK(String::num_real(9.99) == "9.99");
585
CHECK(String::num_real(9.999) == "9.999");
586
CHECK(String::num_real(9.9999) == "9.9999");
587
CHECK(String::num_real(3.141593) == "3.141593");
588
CHECK(String::num_real(3.141) == "3.141"); // No trailing zeros.
589
#ifdef REAL_T_IS_DOUBLE
590
CHECK_MESSAGE(String::num_real(real_t(123.456789)) == "123.456789", "Prints the appropriate amount of digits for real_t = double.");
591
CHECK_MESSAGE(String::num_real(real_t(-123.456789)) == "-123.456789", "Prints the appropriate amount of digits for real_t = double.");
592
CHECK_MESSAGE(String::num_real(real_t(Math::PI)) == "3.14159265358979", "Prints the appropriate amount of digits for real_t = double.");
593
CHECK_MESSAGE(String::num_real(real_t(3.1415f)) == "3.1414999961853", "Prints more digits of 32-bit float when real_t = double (ones that would be reliable for double) and no trailing zero.");
594
#else
595
CHECK_MESSAGE(String::num_real(real_t(123.456789)) == "123.4568", "Prints the appropriate amount of digits for real_t = float.");
596
CHECK_MESSAGE(String::num_real(real_t(-123.456789)) == "-123.4568", "Prints the appropriate amount of digits for real_t = float.");
597
CHECK_MESSAGE(String::num_real(real_t(Math::PI)) == "3.141593", "Prints the appropriate amount of digits for real_t = float.");
598
CHECK_MESSAGE(String::num_real(real_t(3.1415f)) == "3.1415", "Prints only reliable digits of 32-bit float when real_t = float.");
599
#endif // REAL_T_IS_DOUBLE
600
601
// Checks doubles with many decimal places.
602
CHECK(String::num(0.0000012345432123454321, -1) == "0.00000123454321"); // -1 uses 14 as sane default.
603
CHECK(String::num(0.0000012345432123454321) == "0.00000123454321"); // -1 is the default value.
604
CHECK(String::num(-0.0000012345432123454321) == "-0.00000123454321");
605
CHECK(String::num(-10000.0000012345432123454321) == "-10000.0000012345");
606
CHECK(String::num(0.0000000000012345432123454321) == "0.00000000000123");
607
CHECK(String::num(0.0000000000012345432123454321, 3) == "0.0");
608
609
// Note: When relevant (remainder > 0.5), the last digit gets rounded up,
610
// which can also lead to not include a trailing zero, e.g. "...89" -> "...9".
611
CHECK(String::num(0.0000056789876567898765) == "0.00000567898766"); // Should round last digit.
612
CHECK(String::num(10000.000005678999999999) == "10000.000005679"); // We cut at ...789|99 which is rounded to ...79, so only 13 decimals.
613
CHECK(String::num(42.12999999, 6) == "42.13"); // Also happens with lower decimals count.
614
615
// 32 is MAX_DECIMALS. We can't reliably store that many so we can't compare against a string,
616
// but we can check that the string length is 34 (32 + 2 for "0.").
617
CHECK(String::num(0.00000123456789987654321123456789987654321, 32).length() == 34);
618
CHECK(String::num(0.00000123456789987654321123456789987654321, 42).length() == 34); // Should enforce MAX_DECIMALS.
619
CHECK(String::num(10000.00000123456789987654321123456789987654321, 42).length() == 38); // 32 decimals + "10000.".
620
}
621
622
TEST_CASE("[String] String to integer") {
623
static const char *nums[14] = { "1237461283", "- 22", "0", " - 1123412", "", "10_000_000", "-1_2_3_4", "10__000", " 1 2 34 ", "-0", "007", "--45", "---46", "-7-2" };
624
static const int num[14] = { 1237461283, -22, 0, -1123412, 0, 10000000, -1234, 10000, 1234, 0, 7, 45, -46, -72 };
625
626
for (int i = 0; i < 14; i++) {
627
CHECK(String(nums[i]).to_int() == num[i]);
628
}
629
CHECK(String("0b1011").to_int() == 1011); // Looks like a binary number, but to_int() handles this as a base-10 number, "b" is just ignored.
630
CHECK(String("0B1011").to_int() == 1011);
631
632
CHECK(String("0x1012").to_int() == 1012); // Looks like a hexadecimal number, but to_int() handles this as a base-10 number, "x" is just ignored.
633
CHECK(String("0X1012").to_int() == 1012);
634
635
ERR_PRINT_OFF
636
CHECK(String("999999999999999999999999999999999999999999999999999999999").to_int() == INT64_MAX); // Too large, largest possible is returned.
637
CHECK(String("-999999999999999999999999999999999999999999999999999999999").to_int() == INT64_MIN); // Too small, smallest possible is returned.
638
ERR_PRINT_ON
639
}
640
641
TEST_CASE("[String] Hex to integer") {
642
static const char *nums[13] = { "0xFFAE", "22", "0", "AADDAD", "0x7FFFFFFFFFFFFFFF", "-0xf", "", "000", "000f", "0xaA", "-ff", "-", "0XFFAE" };
643
static const int64_t num[13] = { 0xFFAE, 0x22, 0, 0xAADDAD, 0x7FFFFFFFFFFFFFFF, -0xf, 0, 0, 0xf, 0xaa, -0xff, 0x0, 0xFFAE };
644
645
for (int i = 0; i < 13; i++) {
646
CHECK(String(nums[i]).hex_to_int() == num[i]);
647
}
648
649
// Invalid hex strings should return 0.
650
static const char *invalid_nums[15] = { "qwerty", "QWERTY", "0xqwerty", "0x00qwerty", "qwerty00", "0x", "0x__", "__", "x12", "+", " ff", "ff ", "f f", "+ff", "--0x78" };
651
652
ERR_PRINT_OFF
653
for (int i = 0; i < 15; i++) {
654
CHECK(String(invalid_nums[i]).hex_to_int() == 0);
655
}
656
657
CHECK(String("0xFFFFFFFFFFFFFFFFFFFFFFF").hex_to_int() == INT64_MAX); // Too large, largest possible is returned.
658
CHECK(String("-0xFFFFFFFFFFFFFFFFFFFFFFF").hex_to_int() == INT64_MIN); // Too small, smallest possible is returned.
659
ERR_PRINT_ON
660
}
661
662
TEST_CASE("[String] Bin to integer") {
663
static const char *nums[11] = { "", "0", "0b0", "0b1", "0b", "1", "0b1010", "-0b11", "-1010", "0b0111111111111111111111111111111111111111111111111111111111111111", "0B1010" };
664
static const int64_t num[11] = { 0, 0, 0, 1, 0, 1, 10, -3, -10, 0x7FFFFFFFFFFFFFFF, 10 };
665
666
for (int i = 0; i < 11; i++) {
667
CHECK(String(nums[i]).bin_to_int() == num[i]);
668
}
669
670
// Invalid bin strings should return 0. The long "0x11...11" is just too long for a 64 bit int.
671
static const char *invalid_nums[16] = { "qwerty", "QWERTY", "0bqwerty", "0b00qwerty", "qwerty00", "0x__", "0b__", "__", "b12", "+", "-", "0x12ab", " 11", "11 ", "1 1", "--0b11" };
672
673
for (int i = 0; i < 16; i++) {
674
CHECK(String(invalid_nums[i]).bin_to_int() == 0);
675
}
676
677
ERR_PRINT_OFF
678
CHECK(String("0b111111111111111111111111111111111111111111111111111111111111111111111111111111111").bin_to_int() == INT64_MAX); // Too large, largest possible is returned.
679
CHECK(String("-0b111111111111111111111111111111111111111111111111111111111111111111111111111111111").bin_to_int() == INT64_MIN); // Too small, smallest possible is returned.
680
ERR_PRINT_ON
681
}
682
683
TEST_CASE("[String] String to float") {
684
static const char *nums[12] = { "-12348298412.2", "0.05", "2.0002", " -0.0001", "0", "000", "123", "0.0", "000.000", "000.007", "234__", "3..14" };
685
static const double num[12] = { -12348298412.2, 0.05, 2.0002, -0.0001, 0.0, 0.0, 123.0, 0.0, 0.0, 0.007, 234.0, 3.0 };
686
687
for (int i = 0; i < 12; i++) {
688
CHECK(!(Math::abs(String(nums[i]).to_float() - num[i]) > 0.00001));
689
}
690
691
// Invalid float strings should return 0.
692
static const char *invalid_nums[6] = { "qwerty", "qwerty123", "0xffff", "0b1010", "--3.13", "__345" };
693
694
for (int i = 0; i < 6; i++) {
695
CHECK(String(invalid_nums[i]).to_float() == 0);
696
}
697
698
// Very large exponents.
699
CHECK(String("1e308").to_float() == 1e308);
700
CHECK(String("-1e308").to_float() == -1e308);
701
702
// Exponent is so high that value is INFINITY/-INFINITY.
703
CHECK(String("1e309").to_float() == Math::INF);
704
CHECK(String("1e511").to_float() == Math::INF);
705
CHECK(String("-1e309").to_float() == -Math::INF);
706
CHECK(String("-1e511").to_float() == -Math::INF);
707
708
// Exponent is so high that a warning message is printed. Value is INFINITY/-INFINITY.
709
ERR_PRINT_OFF
710
CHECK(String("1e512").to_float() == Math::INF);
711
CHECK(String("-1e512").to_float() == -Math::INF);
712
ERR_PRINT_ON
713
}
714
715
TEST_CASE("[String] Slicing") {
716
String s = "Mars,Jupiter,Saturn,Uranus";
717
const char *slices[4] = { "Mars", "Jupiter", "Saturn", "Uranus" };
718
MULTICHECK_GET_SLICE(s, ",", slices);
719
}
720
721
TEST_CASE("[String] Begins with") {
722
// Test cases for true:
723
MULTICHECK_STRING_EQ(String("res://foobar"), begins_with, "res://", true);
724
MULTICHECK_STRING_EQ(String("abc"), begins_with, "abc", true);
725
MULTICHECK_STRING_EQ(String("abc"), begins_with, "", true);
726
MULTICHECK_STRING_EQ(String(""), begins_with, "", true);
727
728
// Test cases for false:
729
MULTICHECK_STRING_EQ(String("res"), begins_with, "res://", false);
730
MULTICHECK_STRING_EQ(String("abcdef"), begins_with, "foo", false);
731
MULTICHECK_STRING_EQ(String("abc"), begins_with, "ax", false);
732
MULTICHECK_STRING_EQ(String(""), begins_with, "abc", false);
733
734
// Test "const char *" version also with nullptr.
735
String s("foo");
736
bool state = s.begins_with(nullptr) == false;
737
CHECK_MESSAGE(state, "nullptr check failed");
738
739
String empty("");
740
state = empty.begins_with(nullptr) == false;
741
CHECK_MESSAGE(state, "nullptr check with empty string failed");
742
}
743
744
TEST_CASE("[String] Ends with") {
745
// Test cases for true:
746
MULTICHECK_STRING_EQ(String("res://foobar"), ends_with, "foobar", true);
747
MULTICHECK_STRING_EQ(String("abc"), ends_with, "abc", true);
748
MULTICHECK_STRING_EQ(String("abc"), ends_with, "", true);
749
MULTICHECK_STRING_EQ(String(""), ends_with, "", true);
750
751
// Test cases for false:
752
MULTICHECK_STRING_EQ(String("res"), ends_with, "res://", false);
753
MULTICHECK_STRING_EQ(String("abcdef"), ends_with, "foo", false);
754
MULTICHECK_STRING_EQ(String("abc"), ends_with, "ax", false);
755
MULTICHECK_STRING_EQ(String(""), ends_with, "abc", false);
756
757
// Test "const char *" version also with nullptr.
758
String s("foo");
759
bool state = s.ends_with(nullptr) == false;
760
CHECK_MESSAGE(state, "nullptr check failed");
761
762
String empty("");
763
state = empty.ends_with(nullptr) == false;
764
CHECK_MESSAGE(state, "nullptr check with empty string failed");
765
}
766
767
TEST_CASE("[String] Splitting") {
768
{
769
const String s = "Mars,Jupiter,Saturn,Uranus";
770
771
const char *slices_l[3] = { "Mars", "Jupiter", "Saturn,Uranus" };
772
MULTICHECK_SPLIT(s, split, ",", true, 2, slices_l, 3);
773
774
const char *slices_r[3] = { "Mars,Jupiter", "Saturn", "Uranus" };
775
MULTICHECK_SPLIT(s, rsplit, ",", true, 2, slices_r, 3);
776
}
777
778
{
779
const String s = "test";
780
const char *slices[4] = { "t", "e", "s", "t" };
781
MULTICHECK_SPLIT(s, split, "", true, 0, slices, 4);
782
}
783
784
{
785
const String s = "";
786
const char *slices[1] = { "" };
787
MULTICHECK_SPLIT(s, split, "", true, 0, slices, 1);
788
MULTICHECK_SPLIT(s, split, "", false, 0, slices, 0);
789
}
790
791
{
792
const String s = "Mars Jupiter Saturn Uranus";
793
const char *slices[4] = { "Mars", "Jupiter", "Saturn", "Uranus" };
794
Vector<String> l = s.split_spaces();
795
for (int i = 0; i < l.size(); i++) {
796
CHECK(l[i] == slices[i]);
797
}
798
}
799
{
800
const String s = "Mars Jupiter Saturn Uranus";
801
const char *slices[2] = { "Mars", "Jupiter Saturn Uranus" };
802
Vector<String> l = s.split_spaces(1);
803
for (int i = 0; i < l.size(); i++) {
804
CHECK(l[i] == slices[i]);
805
}
806
}
807
808
{
809
const String s = "1.2;2.3 4.5";
810
const double slices[3] = { 1.2, 2.3, 4.5 };
811
812
const Vector<double> d_arr = s.split_floats(";");
813
CHECK(d_arr.size() == 2);
814
for (int i = 0; i < d_arr.size(); i++) {
815
CHECK(Math::abs(d_arr[i] - slices[i]) <= 0.00001);
816
}
817
818
const Vector<String> keys = { ";", " " };
819
const Vector<float> f_arr = s.split_floats_mk(keys);
820
CHECK(f_arr.size() == 3);
821
for (int i = 0; i < f_arr.size(); i++) {
822
CHECK(Math::abs(f_arr[i] - slices[i]) <= 0.00001);
823
}
824
}
825
826
{
827
const String s = " -2.0 5";
828
const double slices[10] = { 0, -2, 0, 0, 0, 0, 0, 0, 0, 5 };
829
830
const Vector<double> arr = s.split_floats(" ");
831
CHECK(arr.size() == 10);
832
for (int i = 0; i < arr.size(); i++) {
833
CHECK(Math::abs(arr[i] - slices[i]) <= 0.00001);
834
}
835
836
const Vector<String> keys = { ";", " " };
837
const Vector<float> mk = s.split_floats_mk(keys);
838
CHECK(mk.size() == 10);
839
for (int i = 0; i < mk.size(); i++) {
840
CHECK(mk[i] == slices[i]);
841
}
842
}
843
844
{
845
const String s = "1;2 4";
846
const int slices[3] = { 1, 2, 4 };
847
848
const Vector<int> arr = s.split_ints(";");
849
CHECK(arr.size() == 2);
850
for (int i = 0; i < arr.size(); i++) {
851
CHECK(arr[i] == slices[i]);
852
}
853
854
const Vector<String> keys = { ";", " " };
855
const Vector<int> mk = s.split_ints_mk(keys);
856
CHECK(mk.size() == 3);
857
for (int i = 0; i < mk.size(); i++) {
858
CHECK(mk[i] == slices[i]);
859
}
860
}
861
}
862
863
TEST_CASE("[String] format") {
864
const String value_format = "red=\"$red\" green=\"$green\" blue=\"$blue\" alpha=\"$alpha\"";
865
866
Dictionary value_dictionary;
867
value_dictionary["red"] = 10;
868
value_dictionary["green"] = 20;
869
value_dictionary["blue"] = "bla";
870
value_dictionary["alpha"] = 0.4;
871
String value = value_format.format(value_dictionary, "$_");
872
873
CHECK(value == "red=\"10\" green=\"20\" blue=\"bla\" alpha=\"0.4\"");
874
}
875
876
TEST_CASE("[String] sprintf") {
877
String format, output;
878
Array args;
879
bool error;
880
881
// %%
882
format = "fish %% frog";
883
args.clear();
884
output = format.sprintf(args, &error);
885
REQUIRE(error == false);
886
CHECK(output == String("fish % frog"));
887
///// Ints
888
889
// Int
890
format = "fish %d frog";
891
args.clear();
892
args.push_back(5);
893
output = format.sprintf(args, &error);
894
REQUIRE(error == false);
895
CHECK(output == String("fish 5 frog"));
896
897
// Int left padded with zeroes.
898
format = "fish %05d frog";
899
args.clear();
900
args.push_back(5);
901
output = format.sprintf(args, &error);
902
REQUIRE(error == false);
903
CHECK(output == String("fish 00005 frog"));
904
905
// Int left padded with spaces.
906
format = "fish %5d frog";
907
args.clear();
908
args.push_back(5);
909
output = format.sprintf(args, &error);
910
REQUIRE(error == false);
911
CHECK(output == String("fish 5 frog"));
912
913
// Int right padded with spaces.
914
format = "fish %-5d frog";
915
args.clear();
916
args.push_back(5);
917
output = format.sprintf(args, &error);
918
REQUIRE(error == false);
919
CHECK(output == String("fish 5 frog"));
920
921
// Int with sign (positive).
922
format = "fish %+d frog";
923
args.clear();
924
args.push_back(5);
925
output = format.sprintf(args, &error);
926
REQUIRE(error == false);
927
CHECK(output == String("fish +5 frog"));
928
929
// Negative int.
930
format = "fish %d frog";
931
args.clear();
932
args.push_back(-5);
933
output = format.sprintf(args, &error);
934
REQUIRE(error == false);
935
CHECK(output == String("fish -5 frog"));
936
937
// Negative int left padded with spaces.
938
format = "fish %5d frog";
939
args.clear();
940
args.push_back(-5);
941
output = format.sprintf(args, &error);
942
REQUIRE(error == false);
943
CHECK(output == String("fish -5 frog"));
944
945
// Negative int left padded with zeros.
946
format = "fish %05d frog";
947
args.clear();
948
args.push_back(-5);
949
output = format.sprintf(args, &error);
950
REQUIRE(error == false);
951
CHECK(output == String("fish -0005 frog"));
952
953
// Negative int right padded with spaces.
954
format = "fish %-5d frog";
955
args.clear();
956
args.push_back(-5);
957
output = format.sprintf(args, &error);
958
REQUIRE(error == false);
959
CHECK(output == String("fish -5 frog"));
960
961
// Negative int right padded with zeros. (0 ignored)
962
format = "fish %-05d frog";
963
args.clear();
964
args.push_back(-5);
965
ERR_PRINT_OFF; // Silence warning about 0 ignored.
966
output = format.sprintf(args, &error);
967
ERR_PRINT_ON;
968
REQUIRE(error == false);
969
CHECK(output == String("fish -5 frog"));
970
971
// Hex (lower)
972
format = "fish %x frog";
973
args.clear();
974
args.push_back(45);
975
output = format.sprintf(args, &error);
976
REQUIRE(error == false);
977
CHECK(output == String("fish 2d frog"));
978
979
// Hex (upper)
980
format = "fish %X frog";
981
args.clear();
982
args.push_back(45);
983
output = format.sprintf(args, &error);
984
REQUIRE(error == false);
985
CHECK(output == String("fish 2D frog"));
986
987
// Octal
988
format = "fish %o frog";
989
args.clear();
990
args.push_back(99);
991
output = format.sprintf(args, &error);
992
REQUIRE(error == false);
993
CHECK(output == String("fish 143 frog"));
994
995
// INT64_MIN
996
format = "fish %d frog";
997
args.clear();
998
args.push_back(INT64_MIN);
999
output = format.sprintf(args, &error);
1000
REQUIRE(error == false);
1001
CHECK(output == String("fish -9223372036854775808 frog"));
1002
1003
// INT64_MIN hex (lower)
1004
format = "fish %x frog";
1005
args.clear();
1006
args.push_back(INT64_MIN);
1007
output = format.sprintf(args, &error);
1008
REQUIRE(error == false);
1009
CHECK(output == String("fish -8000000000000000 frog"));
1010
1011
// INT64_MIN hex (upper)
1012
format = "fish %X frog";
1013
args.clear();
1014
args.push_back(INT64_MIN);
1015
output = format.sprintf(args, &error);
1016
REQUIRE(error == false);
1017
CHECK(output == String("fish -8000000000000000 frog"));
1018
1019
// INT64_MIN octal
1020
format = "fish %o frog";
1021
args.clear();
1022
args.push_back(INT64_MIN);
1023
output = format.sprintf(args, &error);
1024
REQUIRE(error == false);
1025
CHECK(output == String("fish -1000000000000000000000 frog"));
1026
1027
///// Reals
1028
1029
// Real
1030
format = "fish %f frog";
1031
args.clear();
1032
args.push_back(99.99);
1033
output = format.sprintf(args, &error);
1034
REQUIRE(error == false);
1035
CHECK(output == String("fish 99.990000 frog"));
1036
1037
// Real left-padded.
1038
format = "fish %11f frog";
1039
args.clear();
1040
args.push_back(99.99);
1041
output = format.sprintf(args, &error);
1042
REQUIRE(error == false);
1043
CHECK(output == String("fish 99.990000 frog"));
1044
1045
// Real (infinity) left-padded
1046
format = "fish %11f frog";
1047
args.clear();
1048
args.push_back(Math::INF);
1049
output = format.sprintf(args, &error);
1050
REQUIRE(error == false);
1051
CHECK(output == String("fish inf frog"));
1052
1053
// Real right-padded.
1054
format = "fish %-11f frog";
1055
args.clear();
1056
args.push_back(99.99);
1057
output = format.sprintf(args, &error);
1058
REQUIRE(error == false);
1059
CHECK(output == String("fish 99.990000 frog"));
1060
1061
// Real given int.
1062
format = "fish %f frog";
1063
args.clear();
1064
args.push_back(99);
1065
output = format.sprintf(args, &error);
1066
REQUIRE(error == false);
1067
CHECK(output == String("fish 99.000000 frog"));
1068
1069
// Real with sign (positive).
1070
format = "fish %+f frog";
1071
args.clear();
1072
args.push_back(99.99);
1073
output = format.sprintf(args, &error);
1074
REQUIRE(error == false);
1075
CHECK(output == String("fish +99.990000 frog"));
1076
1077
// Real with sign (negative zero).
1078
format = "fish %+f frog";
1079
args.clear();
1080
args.push_back(-0.0);
1081
output = format.sprintf(args, &error);
1082
REQUIRE(error == false);
1083
CHECK(output == String("fish -0.000000 frog"));
1084
1085
// Real with sign (positive zero).
1086
format = "fish %+f frog";
1087
args.clear();
1088
args.push_back(0.0);
1089
output = format.sprintf(args, &error);
1090
REQUIRE(error == false);
1091
CHECK(output == String("fish +0.000000 frog"));
1092
1093
// Real with 1 decimal.
1094
format = "fish %.1f frog";
1095
args.clear();
1096
args.push_back(99.99);
1097
output = format.sprintf(args, &error);
1098
REQUIRE(error == false);
1099
CHECK(output == String("fish 100.0 frog"));
1100
1101
// Real with 12 decimals.
1102
format = "fish %.12f frog";
1103
args.clear();
1104
args.push_back(99.99);
1105
output = format.sprintf(args, &error);
1106
REQUIRE(error == false);
1107
CHECK(output == String("fish 99.990000000000 frog"));
1108
1109
// Real with no decimals.
1110
format = "fish %.f frog";
1111
args.clear();
1112
args.push_back(99.99);
1113
output = format.sprintf(args, &error);
1114
REQUIRE(error == false);
1115
CHECK(output == String("fish 100 frog"));
1116
1117
// Negative real right padded with zeros. (0 ignored)
1118
format = "fish %-011f frog";
1119
args.clear();
1120
args.push_back(-99.99);
1121
ERR_PRINT_OFF; // Silence warning about 0 ignored.
1122
output = format.sprintf(args, &error);
1123
ERR_PRINT_ON;
1124
REQUIRE(error == false);
1125
CHECK(output == String("fish -99.990000 frog"));
1126
1127
///// Vectors
1128
1129
// Vector2
1130
format = "fish %v frog";
1131
args.clear();
1132
args.push_back(Variant(Vector2(19.99, 1.00)));
1133
output = format.sprintf(args, &error);
1134
REQUIRE(error == false);
1135
CHECK(output == String("fish (19.990000, 1.000000) frog"));
1136
1137
// Vector3
1138
format = "fish %v frog";
1139
args.clear();
1140
args.push_back(Variant(Vector3(19.99, 1.00, -2.05)));
1141
output = format.sprintf(args, &error);
1142
REQUIRE(error == false);
1143
CHECK(output == String("fish (19.990000, 1.000000, -2.050000) frog"));
1144
1145
// Vector4
1146
format = "fish %v frog";
1147
args.clear();
1148
args.push_back(Variant(Vector4(19.99, 1.00, -2.05, 5.5)));
1149
output = format.sprintf(args, &error);
1150
REQUIRE(error == false);
1151
CHECK(output == String("fish (19.990000, 1.000000, -2.050000, 5.500000) frog"));
1152
1153
// Vector with negative values.
1154
format = "fish %v frog";
1155
args.clear();
1156
args.push_back(Variant(Vector2(-19.99, -1.00)));
1157
output = format.sprintf(args, &error);
1158
REQUIRE(error == false);
1159
CHECK(output == String("fish (-19.990000, -1.000000) frog"));
1160
1161
// Vector left-padded.
1162
format = "fish %11v frog";
1163
args.clear();
1164
args.push_back(Variant(Vector3(19.99, 1.00, -2.05)));
1165
output = format.sprintf(args, &error);
1166
REQUIRE(error == false);
1167
CHECK(output == String("fish ( 19.990000, 1.000000, -2.050000) frog"));
1168
1169
// Vector left-padded with inf/nan
1170
format = "fish %11v frog";
1171
args.clear();
1172
args.push_back(Variant(Vector2(Math::INF, Math::NaN)));
1173
output = format.sprintf(args, &error);
1174
REQUIRE(error == false);
1175
CHECK(output == String("fish ( inf, nan) frog"));
1176
1177
// Vector right-padded.
1178
format = "fish %-11v frog";
1179
args.clear();
1180
args.push_back(Variant(Vector3(19.99, 1.00, -2.05)));
1181
output = format.sprintf(args, &error);
1182
REQUIRE(error == false);
1183
CHECK(output == String("fish (19.990000 , 1.000000 , -2.050000 ) frog"));
1184
1185
// Vector left-padded with zeros.
1186
format = "fish %011v frog";
1187
args.clear();
1188
args.push_back(Variant(Vector3(19.99, 1.00, -2.05)));
1189
output = format.sprintf(args, &error);
1190
REQUIRE(error == false);
1191
CHECK(output == String("fish (0019.990000, 0001.000000, -002.050000) frog"));
1192
1193
// Vector given Vector3i.
1194
format = "fish %v frog";
1195
args.clear();
1196
args.push_back(Variant(Vector3i(19, 1, -2)));
1197
output = format.sprintf(args, &error);
1198
REQUIRE(error == false);
1199
CHECK(output == String("fish (19.000000, 1.000000, -2.000000) frog"));
1200
1201
// Vector with 1 decimal.
1202
format = "fish %.1v frog";
1203
args.clear();
1204
args.push_back(Variant(Vector3(19.99, 1.00, -2.05)));
1205
output = format.sprintf(args, &error);
1206
REQUIRE(error == false);
1207
CHECK(output == String("fish (20.0, 1.0, -2.0) frog"));
1208
1209
// Vector with 12 decimals.
1210
format = "fish %.12v frog";
1211
args.clear();
1212
args.push_back(Variant(Vector3(19.00, 1.00, -2.00)));
1213
output = format.sprintf(args, &error);
1214
REQUIRE(error == false);
1215
CHECK(output == String("fish (19.000000000000, 1.000000000000, -2.000000000000) frog"));
1216
1217
// Vector with no decimals.
1218
format = "fish %.v frog";
1219
args.clear();
1220
args.push_back(Variant(Vector3(19.99, 1.00, -2.05)));
1221
output = format.sprintf(args, &error);
1222
REQUIRE(error == false);
1223
CHECK(output == String("fish (20, 1, -2) frog"));
1224
1225
///// Strings
1226
1227
// String
1228
format = "fish %s frog";
1229
args.clear();
1230
args.push_back("cheese");
1231
output = format.sprintf(args, &error);
1232
REQUIRE(error == false);
1233
CHECK(output == String("fish cheese frog"));
1234
1235
// String left-padded.
1236
format = "fish %10s frog";
1237
args.clear();
1238
args.push_back("cheese");
1239
output = format.sprintf(args, &error);
1240
REQUIRE(error == false);
1241
CHECK(output == String("fish cheese frog"));
1242
1243
// String right-padded.
1244
format = "fish %-10s frog";
1245
args.clear();
1246
args.push_back("cheese");
1247
output = format.sprintf(args, &error);
1248
REQUIRE(error == false);
1249
CHECK(output == String("fish cheese frog"));
1250
1251
///// Characters
1252
1253
// Character as string.
1254
format = "fish %c frog";
1255
args.clear();
1256
args.push_back("A");
1257
output = format.sprintf(args, &error);
1258
REQUIRE(error == false);
1259
CHECK(output == String("fish A frog"));
1260
1261
// Character as int.
1262
format = "fish %c frog";
1263
args.clear();
1264
args.push_back(65);
1265
output = format.sprintf(args, &error);
1266
REQUIRE(error == false);
1267
CHECK(output == String("fish A frog"));
1268
1269
///// Dynamic width
1270
1271
// String dynamic width.
1272
format = "fish %*s frog";
1273
args.clear();
1274
args.push_back(10);
1275
args.push_back("cheese");
1276
output = format.sprintf(args, &error);
1277
REQUIRE(error == false);
1278
REQUIRE(output == String("fish cheese frog"));
1279
1280
// Int dynamic width.
1281
format = "fish %*d frog";
1282
args.clear();
1283
args.push_back(10);
1284
args.push_back(99);
1285
output = format.sprintf(args, &error);
1286
REQUIRE(error == false);
1287
REQUIRE(output == String("fish 99 frog"));
1288
1289
// Float dynamic width.
1290
format = "fish %*.*f frog";
1291
args.clear();
1292
args.push_back(10);
1293
args.push_back(3);
1294
args.push_back(99.99);
1295
output = format.sprintf(args, &error);
1296
REQUIRE(error == false);
1297
CHECK(output == String("fish 99.990 frog"));
1298
1299
///// Argument indices.
1300
format = "fish %2$d frog %1$s xx";
1301
args.clear();
1302
args.push_back("test");
1303
args.push_back(5);
1304
output = format.sprintf(args, &error);
1305
REQUIRE(error == false);
1306
CHECK(output == String("fish 5 frog test xx"));
1307
1308
///// Errors
1309
1310
// More formats than arguments.
1311
format = "fish %s %s frog";
1312
args.clear();
1313
args.push_back("cheese");
1314
output = format.sprintf(args, &error);
1315
REQUIRE(error);
1316
CHECK(output == "not enough arguments for format string");
1317
1318
// More arguments than formats.
1319
format = "fish %s frog";
1320
args.clear();
1321
args.push_back("hello");
1322
args.push_back("cheese");
1323
output = format.sprintf(args, &error);
1324
REQUIRE(error);
1325
CHECK(output == "not all arguments converted during string formatting");
1326
1327
// Incomplete format.
1328
format = "fish %10";
1329
args.clear();
1330
args.push_back("cheese");
1331
output = format.sprintf(args, &error);
1332
REQUIRE(error);
1333
CHECK(output == "incomplete format");
1334
1335
// Bad character in format string.
1336
format = "fish %&f frog";
1337
args.clear();
1338
args.push_back("cheese");
1339
output = format.sprintf(args, &error);
1340
REQUIRE(error);
1341
CHECK(output == "unsupported format character");
1342
1343
// Too many decimals.
1344
format = "fish %2.2.2f frog";
1345
args.clear();
1346
args.push_back(99.99);
1347
output = format.sprintf(args, &error);
1348
REQUIRE(error);
1349
CHECK(output == "too many decimal points in format");
1350
1351
// * not a number or vector.
1352
format = "fish %*f frog";
1353
args.clear();
1354
args.push_back("cheese");
1355
args.push_back(99.99);
1356
output = format.sprintf(args, &error);
1357
REQUIRE(error);
1358
CHECK(output == "* wants number or vector");
1359
1360
// Character too long.
1361
format = "fish %c frog";
1362
args.clear();
1363
args.push_back("sc");
1364
output = format.sprintf(args, &error);
1365
REQUIRE(error);
1366
CHECK(output == "%c requires number or single-character string");
1367
1368
// Character bad type.
1369
format = "fish %c frog";
1370
args.clear();
1371
args.push_back(Array());
1372
output = format.sprintf(args, &error);
1373
REQUIRE(error);
1374
CHECK(output == "%c requires number or single-character string");
1375
}
1376
1377
TEST_CASE("[String] is_numeric") {
1378
CHECK(String("12").is_numeric());
1379
CHECK(String("1.2").is_numeric());
1380
CHECK(!String("AF").is_numeric());
1381
CHECK(String("-12").is_numeric());
1382
CHECK(String("-1.2").is_numeric());
1383
}
1384
1385
TEST_CASE("[String] pad") {
1386
String s = String("test");
1387
CHECK(s.lpad(10, "x") == U"xxxxxxtest");
1388
CHECK(s.rpad(10, "x") == U"testxxxxxx");
1389
1390
s = String("10.10");
1391
CHECK(s.pad_decimals(4) == U"10.1000");
1392
CHECK(s.pad_decimals(1) == U"10.1");
1393
CHECK(s.pad_zeros(4) == U"0010.10");
1394
CHECK(s.pad_zeros(1) == U"10.10");
1395
}
1396
1397
TEST_CASE("[String] is_subsequence_of") {
1398
String a = "is subsequence of";
1399
CHECK(String("sub").is_subsequence_of(a));
1400
CHECK(!String("Sub").is_subsequence_of(a));
1401
CHECK(String("Sub").is_subsequence_ofn(a));
1402
}
1403
1404
TEST_CASE("[String] is_lowercase") {
1405
CHECK(String("abcd1234 !@#$%^&*()_-=+,.<>/\\|[]{};':\"`~").is_lowercase());
1406
CHECK(String("").is_lowercase());
1407
CHECK(!String("abc_ABC").is_lowercase());
1408
}
1409
1410
TEST_CASE("[String] match") {
1411
CHECK(String("img1.png").match("*.png"));
1412
CHECK(!String("img1.jpeg").match("*.png"));
1413
CHECK(!String("img1.Png").match("*.png"));
1414
CHECK(String("img1.Png").matchn("*.png"));
1415
}
1416
1417
TEST_CASE("[String] IPVX address to string") {
1418
IPAddress ip0("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
1419
IPAddress ip(0x0123, 0x4567, 0x89ab, 0xcdef, true);
1420
IPAddress ip2("fe80::52e5:49ff:fe93:1baf");
1421
IPAddress ip3("::ffff:192.168.0.1");
1422
String ip4 = "192.168.0.1";
1423
CHECK(ip4.is_valid_ip_address());
1424
1425
ip4 = "192.368.0.1";
1426
CHECK(!ip4.is_valid_ip_address());
1427
1428
String ip6 = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
1429
CHECK(ip6.is_valid_ip_address());
1430
1431
ip6 = "2001:0db8:85j3:0000:0000:8a2e:0370:7334";
1432
CHECK(!ip6.is_valid_ip_address());
1433
1434
ip6 = "2001:0db8:85f345:0000:0000:8a2e:0370:7334";
1435
CHECK(!ip6.is_valid_ip_address());
1436
1437
ip6 = "2001:0db8::0:8a2e:370:7334";
1438
CHECK(ip6.is_valid_ip_address());
1439
1440
ip6 = "::ffff:192.168.0.1";
1441
CHECK(ip6.is_valid_ip_address());
1442
}
1443
1444
TEST_CASE("[String] Capitalize against many strings") {
1445
String input = "2D";
1446
String output = "2d";
1447
CHECK(input.capitalize() == output);
1448
1449
input = "2d";
1450
output = "2d";
1451
CHECK(input.capitalize() == output);
1452
1453
input = "2db";
1454
output = "2 Db";
1455
CHECK(input.capitalize() == output);
1456
1457
input = "HTML5 Html5 html5 html_5";
1458
output = "Html 5 Html 5 Html 5 Html 5";
1459
CHECK(input.capitalize() == output);
1460
1461
input = "Node2D Node2d NODE2D NODE_2D node_2d";
1462
output = "Node 2d Node 2d Node 2d Node 2d Node 2d";
1463
CHECK(input.capitalize() == output);
1464
1465
input = "Node2DPosition";
1466
output = "Node 2d Position";
1467
CHECK(input.capitalize() == output);
1468
1469
input = "Number2Digits";
1470
output = "Number 2 Digits";
1471
CHECK(input.capitalize() == output);
1472
1473
input = "bytes2var";
1474
output = "Bytes 2 Var";
1475
CHECK(input.capitalize() == output);
1476
1477
input = "linear2db";
1478
output = "Linear 2 Db";
1479
CHECK(input.capitalize() == output);
1480
1481
input = "vector3";
1482
output = "Vector 3";
1483
CHECK(input.capitalize() == output);
1484
1485
input = "sha256";
1486
output = "Sha 256";
1487
CHECK(input.capitalize() == output);
1488
1489
input = "PascalCase";
1490
output = "Pascal Case";
1491
CHECK(input.capitalize() == output);
1492
1493
input = "PascalPascalCase";
1494
output = "Pascal Pascal Case";
1495
CHECK(input.capitalize() == output);
1496
1497
input = "snake_case";
1498
output = "Snake Case";
1499
CHECK(input.capitalize() == output);
1500
1501
input = "snake_snake_case";
1502
output = "Snake Snake Case";
1503
CHECK(input.capitalize() == output);
1504
1505
input = "kebab-case";
1506
output = "Kebab Case";
1507
CHECK(input.capitalize() == output);
1508
1509
input = "kebab-kebab-case";
1510
output = "Kebab Kebab Case";
1511
CHECK(input.capitalize() == output);
1512
1513
input = "sha256sum";
1514
output = "Sha 256 Sum";
1515
CHECK(input.capitalize() == output);
1516
1517
input = "cat2dog";
1518
output = "Cat 2 Dog";
1519
CHECK(input.capitalize() == output);
1520
1521
input = "function(name)";
1522
output = "Function(name)";
1523
CHECK(input.capitalize() == output);
1524
1525
input = "snake_case_function(snake_case_arg)";
1526
output = "Snake Case Function(snake Case Arg)";
1527
CHECK(input.capitalize() == output);
1528
1529
input = "snake_case_function( snake_case_arg )";
1530
output = "Snake Case Function( Snake Case Arg )";
1531
CHECK(input.capitalize() == output);
1532
1533
input = "kebab-case-function( kebab-case-arg )";
1534
output = "Kebab Case Function( Kebab Case Arg )";
1535
CHECK(input.capitalize() == output);
1536
1537
input = "kebab_case_function( kebab_case_arg )";
1538
output = "Kebab Case Function( Kebab Case Arg )";
1539
CHECK(input.capitalize() == output);
1540
1541
input = U"словоСлово_слово слово";
1542
output = U"Слово Слово Слово Слово";
1543
CHECK(input.capitalize() == output);
1544
1545
input = U"λέξηΛέξη_λέξη λέξη";
1546
output = U"Λέξη Λέξη Λέξη Λέξη";
1547
CHECK(input.capitalize() == output);
1548
1549
input = U"բառԲառ_բառ բառ";
1550
output = U"Բառ Բառ Բառ Բառ";
1551
CHECK(input.capitalize() == output);
1552
}
1553
1554
struct StringCasesTestCase {
1555
const char32_t *input;
1556
const char32_t *camel_case;
1557
const char32_t *pascal_case;
1558
const char32_t *snake_case;
1559
const char32_t *kebab_case;
1560
};
1561
1562
TEST_CASE("[String] Checking case conversion methods") {
1563
StringCasesTestCase test_cases[] = {
1564
/* clang-format off */
1565
{ U"2D", U"2d", U"2d", U"2d", U"2d" },
1566
{ U"2d", U"2d", U"2d", U"2d", U"2d" },
1567
{ U"2db", U"2Db", U"2Db", U"2_db", U"2-db" },
1568
{ U"Vector3", U"vector3", U"Vector3", U"vector_3", U"vector-3" },
1569
{ U"sha256", U"sha256", U"Sha256", U"sha_256", U"sha-256" },
1570
{ U"Node2D", U"node2d", U"Node2d", U"node_2d", U"node-2d" },
1571
{ U"RichTextLabel", U"richTextLabel", U"RichTextLabel", U"rich_text_label", U"rich-text-label" },
1572
{ U"HTML5", U"html5", U"Html5", U"html_5", U"html-5" },
1573
{ U"Node2DPosition", U"node2dPosition", U"Node2dPosition", U"node_2d_position", U"node-2d-position" },
1574
{ U"Number2Digits", U"number2Digits", U"Number2Digits", U"number_2_digits", U"number-2-digits" },
1575
{ U"get_property_list", U"getPropertyList", U"GetPropertyList", U"get_property_list", U"get-property-list" },
1576
{ U"get_camera_2d", U"getCamera2d", U"GetCamera2d", U"get_camera_2d", U"get-camera-2d" },
1577
{ U"_physics_process", U"physicsProcess", U"PhysicsProcess", U"_physics_process", U"-physics-process" },
1578
{ U"bytes2var", U"bytes2Var", U"Bytes2Var", U"bytes_2_var", U"bytes-2-var" },
1579
{ U"linear2db", U"linear2Db", U"Linear2Db", U"linear_2_db", U"linear-2-db" },
1580
{ U"sha256sum", U"sha256Sum", U"Sha256Sum", U"sha_256_sum", U"sha-256-sum" },
1581
{ U"camelCase", U"camelCase", U"CamelCase", U"camel_case", U"camel-case" },
1582
{ U"PascalCase", U"pascalCase", U"PascalCase", U"pascal_case", U"pascal-case" },
1583
{ U"snake_case", U"snakeCase", U"SnakeCase", U"snake_case", U"snake-case" },
1584
{ U"kebab-case", U"kebabCase", U"KebabCase", U"kebab_case", U"kebab-case" },
1585
{ U"Test TEST test", U"testTestTest", U"TestTestTest", U"test_test_test", U"test-test-test" },
1586
{ U"словоСлово_слово слово", U"словоСловоСловоСлово", U"СловоСловоСловоСлово", U"слово_слово_слово_слово", U"слово-слово-слово-слово" },
1587
{ U"λέξηΛέξη_λέξη λέξη", U"λέξηΛέξηΛέξηΛέξη", U"ΛέξηΛέξηΛέξηΛέξη", U"λέξη_λέξη_λέξη_λέξη", U"λέξη-λέξη-λέξη-λέξη" },
1588
{ U"բառԲառ_բառ բառ", U"բառԲառԲառԲառ", U"ԲառԲառԲառԲառ", U"բառ_բառ_բառ_բառ", U"բառ-բառ-բառ-բառ" },
1589
{ nullptr, nullptr, nullptr, nullptr, nullptr },
1590
/* clang-format on */
1591
};
1592
1593
int idx = 0;
1594
while (test_cases[idx].input != nullptr) {
1595
String input = test_cases[idx].input;
1596
CHECK(input.to_camel_case() == test_cases[idx].camel_case);
1597
CHECK(input.to_pascal_case() == test_cases[idx].pascal_case);
1598
CHECK(input.to_snake_case() == test_cases[idx].snake_case);
1599
CHECK(input.to_kebab_case() == test_cases[idx].kebab_case);
1600
idx++;
1601
}
1602
}
1603
1604
TEST_CASE("[String] Checking string is empty when it should be") {
1605
bool state = true;
1606
bool success;
1607
1608
String a = "";
1609
success = a[0] == 0;
1610
if (!success) {
1611
state = false;
1612
}
1613
String b = "Godot";
1614
success = b[b.size()] == 0;
1615
if (!success) {
1616
state = false;
1617
}
1618
const String c = "";
1619
success = c[0] == 0;
1620
if (!success) {
1621
state = false;
1622
}
1623
1624
const String d = "Godot";
1625
success = d[d.size()] == 0;
1626
if (!success) {
1627
state = false;
1628
}
1629
1630
CHECK(state);
1631
}
1632
1633
TEST_CASE("[String] lstrip and rstrip") {
1634
#define STRIP_TEST(x) \
1635
{ \
1636
bool success = x; \
1637
state = state && success; \
1638
}
1639
1640
bool state = true;
1641
1642
// strip none
1643
STRIP_TEST(String("abc").lstrip("") == "abc");
1644
STRIP_TEST(String("abc").rstrip("") == "abc");
1645
// strip one
1646
STRIP_TEST(String("abc").lstrip("a") == "bc");
1647
STRIP_TEST(String("abc").rstrip("c") == "ab");
1648
// strip lots
1649
STRIP_TEST(String("bababbababccc").lstrip("ab") == "ccc");
1650
STRIP_TEST(String("aaabcbcbcbbcbbc").rstrip("cb") == "aaa");
1651
// strip empty string
1652
STRIP_TEST(String("").lstrip("") == "");
1653
STRIP_TEST(String("").rstrip("") == "");
1654
// strip to empty string
1655
STRIP_TEST(String("abcabcabc").lstrip("bca") == "");
1656
STRIP_TEST(String("abcabcabc").rstrip("bca") == "");
1657
// don't strip wrong end
1658
STRIP_TEST(String("abc").lstrip("c") == "abc");
1659
STRIP_TEST(String("abca").lstrip("a") == "bca");
1660
STRIP_TEST(String("abc").rstrip("a") == "abc");
1661
STRIP_TEST(String("abca").rstrip("a") == "abc");
1662
// in utf-8 "¿" (\u00bf) has the same first byte as "µ" (\u00b5)
1663
// and the same second as "ÿ" (\u00ff)
1664
STRIP_TEST(String::utf8("¿").lstrip(String::utf8("µÿ")) == String::utf8("¿"));
1665
STRIP_TEST(String::utf8("¿").rstrip(String::utf8("µÿ")) == String::utf8("¿"));
1666
STRIP_TEST(String::utf8("µ¿ÿ").lstrip(String::utf8("µÿ")) == String::utf8("¿ÿ"));
1667
STRIP_TEST(String::utf8("µ¿ÿ").rstrip(String::utf8("µÿ")) == String::utf8("µ¿"));
1668
1669
// the above tests repeated with additional superfluous strip chars
1670
1671
// strip none
1672
STRIP_TEST(String("abc").lstrip("qwjkl") == "abc");
1673
STRIP_TEST(String("abc").rstrip("qwjkl") == "abc");
1674
// strip one
1675
STRIP_TEST(String("abc").lstrip("qwajkl") == "bc");
1676
STRIP_TEST(String("abc").rstrip("qwcjkl") == "ab");
1677
// strip lots
1678
STRIP_TEST(String("bababbababccc").lstrip("qwabjkl") == "ccc");
1679
STRIP_TEST(String("aaabcbcbcbbcbbc").rstrip("qwcbjkl") == "aaa");
1680
// strip empty string
1681
STRIP_TEST(String("").lstrip("qwjkl") == "");
1682
STRIP_TEST(String("").rstrip("qwjkl") == "");
1683
// strip to empty string
1684
STRIP_TEST(String("abcabcabc").lstrip("qwbcajkl") == "");
1685
STRIP_TEST(String("abcabcabc").rstrip("qwbcajkl") == "");
1686
// don't strip wrong end
1687
STRIP_TEST(String("abc").lstrip("qwcjkl") == "abc");
1688
STRIP_TEST(String("abca").lstrip("qwajkl") == "bca");
1689
STRIP_TEST(String("abc").rstrip("qwajkl") == "abc");
1690
STRIP_TEST(String("abca").rstrip("qwajkl") == "abc");
1691
// in utf-8 "¿" (\u00bf) has the same first byte as "µ" (\u00b5)
1692
// and the same second as "ÿ" (\u00ff)
1693
STRIP_TEST(String::utf8("¿").lstrip(String::utf8("qwaµÿjkl")) == String::utf8("¿"));
1694
STRIP_TEST(String::utf8("¿").rstrip(String::utf8("qwaµÿjkl")) == String::utf8("¿"));
1695
STRIP_TEST(String::utf8("µ¿ÿ").lstrip(String::utf8("qwaµÿjkl")) == String::utf8("¿ÿ"));
1696
STRIP_TEST(String::utf8("µ¿ÿ").rstrip(String::utf8("qwaµÿjkl")) == String::utf8("µ¿"));
1697
1698
CHECK(state);
1699
1700
#undef STRIP_TEST
1701
}
1702
1703
TEST_CASE("[String] Ensuring empty string into extend_utf8 passes empty string") {
1704
String empty;
1705
CHECK(empty.append_utf8(nullptr, -1) == ERR_INVALID_DATA);
1706
}
1707
1708
TEST_CASE("[String] Cyrillic to_lower()") {
1709
String upper = U"АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ";
1710
String lower = U"абвгдеёжзийклмнопрстуфхцчшщъыьэюя";
1711
1712
String test = upper.to_lower();
1713
1714
bool state = test == lower;
1715
1716
CHECK(state);
1717
}
1718
1719
TEST_CASE("[String] Count and countn functionality") {
1720
String s = String("");
1721
MULTICHECK_STRING_EQ(s, count, "Test", 0);
1722
1723
s = "Test";
1724
MULTICHECK_STRING_EQ(s, count, "", 0);
1725
1726
s = "Test";
1727
MULTICHECK_STRING_EQ(s, count, "test", 0);
1728
1729
s = "Test";
1730
MULTICHECK_STRING_EQ(s, count, "TEST", 0);
1731
1732
s = "TEST";
1733
MULTICHECK_STRING_EQ(s, count, "TEST", 1);
1734
1735
s = "Test";
1736
MULTICHECK_STRING_EQ(s, count, "Test", 1);
1737
1738
s = "aTest";
1739
MULTICHECK_STRING_EQ(s, count, "Test", 1);
1740
1741
s = "Testa";
1742
MULTICHECK_STRING_EQ(s, count, "Test", 1);
1743
1744
s = "TestTestTest";
1745
MULTICHECK_STRING_EQ(s, count, "Test", 3);
1746
1747
s = "TestTestTest";
1748
MULTICHECK_STRING_EQ(s, count, "TestTest", 1);
1749
1750
s = "TestGodotTestGodotTestGodot";
1751
MULTICHECK_STRING_EQ(s, count, "Test", 3);
1752
1753
s = "TestTestTestTest";
1754
MULTICHECK_STRING_INT_INT_EQ(s, count, "Test", 4, 8, 1);
1755
1756
s = "TestTestTestTest";
1757
MULTICHECK_STRING_INT_INT_EQ(s, count, "Test", 4, 12, 2);
1758
1759
s = "TestTestTestTest";
1760
MULTICHECK_STRING_INT_INT_EQ(s, count, "Test", 4, 16, 3);
1761
1762
s = "TestTestTestTest";
1763
MULTICHECK_STRING_INT_EQ(s, count, "Test", 4, 3);
1764
1765
s = "Test";
1766
MULTICHECK_STRING_EQ(s, countn, "test", 1);
1767
1768
s = "Test";
1769
MULTICHECK_STRING_EQ(s, countn, "TEST", 1);
1770
1771
s = "testTest-Testatest";
1772
MULTICHECK_STRING_EQ(s, countn, "tEst", 4);
1773
1774
s = "testTest-TeStatest";
1775
MULTICHECK_STRING_INT_INT_EQ(s, countn, "tEsT", 4, 16, 2);
1776
}
1777
1778
TEST_CASE("[String] Bigrams") {
1779
String s = "abcd";
1780
Vector<String> bigr = s.bigrams();
1781
1782
CHECK(bigr.size() == 3);
1783
CHECK(bigr[0] == "ab");
1784
CHECK(bigr[1] == "bc");
1785
CHECK(bigr[2] == "cd");
1786
}
1787
1788
TEST_CASE("[String] c-escape/unescape") {
1789
String s = "\\1\a2\b\f3\n45\r6\t7\v8\'9\?0\"";
1790
CHECK(s.c_escape().c_unescape() == s);
1791
}
1792
1793
TEST_CASE("[String] indent") {
1794
static const char *input[] = {
1795
"",
1796
"aaa\nbbb",
1797
"\tcontains\n\tindent",
1798
"empty\n\nline",
1799
};
1800
static const char *expected[] = {
1801
"",
1802
"\taaa\n\tbbb",
1803
"\t\tcontains\n\t\tindent",
1804
"\tempty\n\n\tline",
1805
};
1806
1807
for (int i = 0; i < 3; i++) {
1808
CHECK(String(input[i]).indent("\t") == expected[i]);
1809
}
1810
}
1811
1812
TEST_CASE("[String] dedent") {
1813
String s = " aaa\n bbb";
1814
String t = "aaa\nbbb";
1815
CHECK(s.dedent() == t);
1816
}
1817
1818
TEST_CASE("[String] Path functions") {
1819
static const char *path[8] = { "C:\\Godot\\project\\test.tscn", "/Godot/project/test.xscn", "../Godot/project/test.scn", "Godot\\test.doc", "C:\\test.", "res://test", "user://test", "/.test" };
1820
static const char *base_dir[8] = { "C:\\Godot\\project", "/Godot/project", "../Godot/project", "Godot", "C:\\", "res://", "user://", "/" };
1821
static const char *base_name[8] = { "C:\\Godot\\project\\test", "/Godot/project/test", "../Godot/project/test", "Godot\\test", "C:\\test", "res://test", "user://test", "/" };
1822
static const char *ext[8] = { "tscn", "xscn", "scn", "doc", "", "", "", "test" };
1823
static const char *file[8] = { "test.tscn", "test.xscn", "test.scn", "test.doc", "test.", "test", "test", ".test" };
1824
static const char *simplified[8] = { "C:/Godot/project/test.tscn", "/Godot/project/test.xscn", "../Godot/project/test.scn", "Godot/test.doc", "C:/test.", "res://test", "user://test", "/.test" };
1825
static const bool abs[8] = { true, true, false, false, true, true, true, true };
1826
1827
for (int i = 0; i < 8; i++) {
1828
CHECK(String(path[i]).get_base_dir() == base_dir[i]);
1829
CHECK(String(path[i]).get_basename() == base_name[i]);
1830
CHECK(String(path[i]).get_extension() == ext[i]);
1831
CHECK(String(path[i]).get_file() == file[i]);
1832
CHECK(String(path[i]).is_absolute_path() == abs[i]);
1833
CHECK(String(path[i]).is_relative_path() != abs[i]);
1834
CHECK(String(path[i]).simplify_path() == String(simplified[i]));
1835
CHECK(String(path[i]).simplify_path().get_base_dir().path_join(file[i]) == String(path[i]).simplify_path());
1836
}
1837
1838
CHECK(String("res://test.png").has_extension("png"));
1839
CHECK(String("res://test.PNG").has_extension("png"));
1840
CHECK_FALSE(String("res://test.png").has_extension("jpg"));
1841
CHECK_FALSE(String("res://test.png/README").has_extension("png"));
1842
CHECK_FALSE(String("res://test.").has_extension("png"));
1843
CHECK_FALSE(String("res://test").has_extension("png"));
1844
1845
static const char *file_name[3] = { "test.tscn", "test://.xscn", "?tes*t.scn" };
1846
static const bool valid[3] = { true, false, false };
1847
for (int i = 0; i < 3; i++) {
1848
CHECK(String(file_name[i]).is_valid_filename() == valid[i]);
1849
}
1850
1851
CHECK(String("res://texture.png") == String("res://folder/../folder/../texture.png").simplify_path());
1852
CHECK(String("res://texture.png") == String("res://folder/sub/../../texture.png").simplify_path());
1853
CHECK(String("res://../../texture.png") == String("res://../../texture.png").simplify_path());
1854
}
1855
1856
TEST_CASE("[String] hash") {
1857
String a = "Test";
1858
String b = "Test";
1859
String c = "West";
1860
CHECK(a.hash() == b.hash());
1861
CHECK(a.hash() != c.hash());
1862
1863
CHECK(a.hash64() == b.hash64());
1864
CHECK(a.hash64() != c.hash64());
1865
}
1866
1867
TEST_CASE("[String] uri_encode/unescape") {
1868
String s = "Godot Engine:'docs'";
1869
String t = "Godot%20Engine%3A%27docs%27";
1870
1871
String x1 = "T%C4%93%C5%A1t";
1872
static const uint8_t u8str[] = { 0x54, 0xC4, 0x93, 0xC5, 0xA1, 0x74, 0x00 };
1873
String x2 = String::utf8((const char *)u8str);
1874
String x3 = U"Tēšt";
1875
String x4 = U"file+name";
1876
1877
CHECK(x1.uri_decode() == x2);
1878
CHECK(x1.uri_decode() == x3);
1879
CHECK((x1 + x3).uri_decode() == (x2 + x3)); // Mixed unicode and URL encoded string, e.g. GTK+ bookmark.
1880
CHECK(x2.uri_encode() == x1);
1881
CHECK(x3.uri_encode() == x1);
1882
1883
CHECK(s.uri_encode() == t);
1884
CHECK(t.uri_decode() == s);
1885
CHECK(x4.uri_file_decode() == x4);
1886
CHECK(x4.uri_decode() == U"file name");
1887
}
1888
1889
TEST_CASE("[String] xml_escape/unescape") {
1890
String s = "\"Test\" <test@test&'test'>";
1891
CHECK(s.xml_escape(true).xml_unescape() == s);
1892
CHECK(s.xml_escape(false).xml_unescape() == s);
1893
}
1894
1895
TEST_CASE("[String] xml_unescape") {
1896
// Named entities
1897
String input = "&quot;&amp;&apos;&lt;&gt;";
1898
CHECK(input.xml_unescape() == "\"&\'<>");
1899
1900
// Numeric entities
1901
input = "&#x41;&#66;";
1902
CHECK(input.xml_unescape() == "AB");
1903
1904
input = "&#0;&x#0;More text";
1905
String result = input.xml_unescape();
1906
// Didn't put in a leading NUL and terminate the string
1907
CHECK(input.length() > 0);
1908
CHECK(input[0] != '\0');
1909
// Entity should be left as-is if invalid
1910
CHECK(input.xml_unescape() == input);
1911
1912
// Check near char32_t range
1913
input = "&#xFFFFFFFF;";
1914
result = input.xml_unescape();
1915
CHECK(result.length() == 1);
1916
CHECK(result[0] == 0xFFFFFFFF);
1917
input = "&#4294967295;";
1918
result = input.xml_unescape();
1919
CHECK(result.length() == 1);
1920
CHECK(result[0] == 0xFFFFFFFF);
1921
1922
// Check out of range of char32_t
1923
input = "&#xFFFFFFFFF;";
1924
CHECK(input.xml_unescape() == input);
1925
input = "&#4294967296;";
1926
CHECK(input.xml_unescape() == input);
1927
1928
// Shouldn't consume without ending in a ';'
1929
input = "&#66";
1930
CHECK(input.xml_unescape() == input);
1931
input = "&#x41";
1932
CHECK(input.xml_unescape() == input);
1933
1934
// Invalid characters should make the entity ignored
1935
input = "&#x41SomeIrrelevantText;";
1936
CHECK(input.xml_unescape() == input);
1937
input = "&#66SomeIrrelevantText;";
1938
CHECK(input.xml_unescape() == input);
1939
}
1940
1941
TEST_CASE("[String] Strip escapes") {
1942
String s = "\t\tTest Test\r\n Test";
1943
CHECK(s.strip_escapes() == "Test Test Test");
1944
}
1945
1946
TEST_CASE("[String] Similarity") {
1947
String a = "Test";
1948
String b = "West";
1949
String c = "Toad";
1950
CHECK(a.similarity(b) > a.similarity(c));
1951
}
1952
1953
TEST_CASE("[String] Strip edges") {
1954
String s = "\t Test Test ";
1955
CHECK(s.strip_edges(true, false) == "Test Test ");
1956
CHECK(s.strip_edges(false, true) == "\t Test Test");
1957
CHECK(s.strip_edges(true, true) == "Test Test");
1958
}
1959
1960
TEST_CASE("[String] Trim") {
1961
String s = "aaaTestbbb";
1962
MULTICHECK_STRING_EQ(s, trim_prefix, "aaa", "Testbbb");
1963
MULTICHECK_STRING_EQ(s, trim_prefix, "Test", s);
1964
MULTICHECK_STRING_EQ(s, trim_prefix, "", s);
1965
MULTICHECK_STRING_EQ(s, trim_prefix, "aaaTestbbb", "");
1966
MULTICHECK_STRING_EQ(s, trim_prefix, "bbb", s);
1967
MULTICHECK_STRING_EQ(s, trim_prefix, "AAA", s);
1968
1969
MULTICHECK_STRING_EQ(s, trim_suffix, "bbb", "aaaTest");
1970
MULTICHECK_STRING_EQ(s, trim_suffix, "Test", s);
1971
MULTICHECK_STRING_EQ(s, trim_suffix, "", s);
1972
MULTICHECK_STRING_EQ(s, trim_suffix, "aaaTestbbb", "");
1973
MULTICHECK_STRING_EQ(s, trim_suffix, "aaa", s);
1974
MULTICHECK_STRING_EQ(s, trim_suffix, "BBB", s);
1975
}
1976
1977
TEST_CASE("[String] Right/Left") {
1978
String s = "aaaTestbbb";
1979
// ^
1980
CHECK(s.right(6) == "estbbb");
1981
CHECK(s.right(-6) == "tbbb");
1982
CHECK(s.left(6) == "aaaTes");
1983
CHECK(s.left(-6) == "aaaT");
1984
}
1985
1986
TEST_CASE("[String] Repeat") {
1987
String s = "abababab";
1988
String x = "ab";
1989
String t = x.repeat(4);
1990
CHECK(t == s);
1991
}
1992
1993
TEST_CASE("[String] Reverse") {
1994
String s = "Abcd";
1995
CHECK(s.reverse() == "dcbA");
1996
}
1997
1998
TEST_CASE("[String] SHA1/SHA256/MD5") {
1999
String s = "Godot";
2000
String sha1 = "a1e91f39b9fce6a9998b14bdbe2aa2b39dc2d201";
2001
static uint8_t sha1_buf[20] = {
2002
0xA1, 0xE9, 0x1F, 0x39, 0xB9, 0xFC, 0xE6, 0xA9, 0x99, 0x8B, 0x14, 0xBD, 0xBE, 0x2A, 0xA2, 0xB3,
2003
0x9D, 0xC2, 0xD2, 0x01
2004
};
2005
String sha256 = "2a02b2443f7985d89d09001086ae3dcfa6eb0f55c6ef170715d42328e16e6cb8";
2006
static uint8_t sha256_buf[32] = {
2007
0x2A, 0x02, 0xB2, 0x44, 0x3F, 0x79, 0x85, 0xD8, 0x9D, 0x09, 0x00, 0x10, 0x86, 0xAE, 0x3D, 0xCF,
2008
0xA6, 0xEB, 0x0F, 0x55, 0xC6, 0xEF, 0x17, 0x07, 0x15, 0xD4, 0x23, 0x28, 0xE1, 0x6E, 0x6C, 0xB8
2009
};
2010
String md5 = "4a336d087aeb0390da10ee2ea7cb87f8";
2011
static uint8_t md5_buf[16] = {
2012
0x4A, 0x33, 0x6D, 0x08, 0x7A, 0xEB, 0x03, 0x90, 0xDA, 0x10, 0xEE, 0x2E, 0xA7, 0xCB, 0x87, 0xF8
2013
};
2014
2015
PackedByteArray buf = s.sha1_buffer();
2016
CHECK(memcmp(sha1_buf, buf.ptr(), 20) == 0);
2017
CHECK(s.sha1_text() == sha1);
2018
2019
buf = s.sha256_buffer();
2020
CHECK(memcmp(sha256_buf, buf.ptr(), 32) == 0);
2021
CHECK(s.sha256_text() == sha256);
2022
2023
buf = s.md5_buffer();
2024
CHECK(memcmp(md5_buf, buf.ptr(), 16) == 0);
2025
CHECK(s.md5_text() == md5);
2026
}
2027
2028
TEST_CASE("[String] Join") {
2029
String comma = ", ";
2030
String empty = "";
2031
Vector<String> parts;
2032
2033
CHECK(comma.join(parts) == "");
2034
CHECK(empty.join(parts) == "");
2035
2036
parts.push_back("One");
2037
CHECK(comma.join(parts) == "One");
2038
CHECK(empty.join(parts) == "One");
2039
2040
parts.push_back("B");
2041
parts.push_back("C");
2042
CHECK(comma.join(parts) == "One, B, C");
2043
CHECK(empty.join(parts) == "OneBC");
2044
2045
parts.push_back("");
2046
CHECK(comma.join(parts) == "One, B, C, ");
2047
CHECK(empty.join(parts) == "OneBC");
2048
}
2049
2050
TEST_CASE("[String] Is_*") {
2051
static const char *data[] = { "-30", "100", "10.1", "10,1", "1e2", "1e-2", "1e2e3", "0xAB", "AB", "Test1", "1Test", "Test*1", "文字", "1E2", "1E-2" };
2052
static bool isnum[] = { true, true, true, false, false, false, false, false, false, false, false, false, false, false, false };
2053
static bool isint[] = { true, true, false, false, false, false, false, false, false, false, false, false, false, false, false };
2054
static bool ishex[] = { true, true, false, false, true, false, true, false, true, false, false, false, false, true, false };
2055
static bool ishex_p[] = { false, false, false, false, false, false, false, true, false, false, false, false, false, false, false };
2056
static bool isflt[] = { true, true, true, false, true, true, false, false, false, false, false, false, false, true, true };
2057
static bool isaid[] = { false, false, false, false, false, false, false, false, true, true, false, false, false, false, false };
2058
static bool isuid[] = { false, false, false, false, false, false, false, false, true, true, false, false, true, false, false };
2059
for (unsigned int i = 0; i < std_size(data); i++) {
2060
String s = String::utf8(data[i]);
2061
CHECK(s.is_numeric() == isnum[i]);
2062
CHECK(s.is_valid_int() == isint[i]);
2063
CHECK(s.is_valid_hex_number(false) == ishex[i]);
2064
CHECK(s.is_valid_hex_number(true) == ishex_p[i]);
2065
CHECK(s.is_valid_float() == isflt[i]);
2066
CHECK(s.is_valid_ascii_identifier() == isaid[i]);
2067
CHECK(s.is_valid_unicode_identifier() == isuid[i]);
2068
}
2069
}
2070
2071
TEST_CASE("[String] humanize_size") {
2072
CHECK(String::humanize_size(1000) == "1000 B");
2073
CHECK(String::humanize_size(1025) == "1.00 KiB");
2074
CHECK(String::humanize_size(1025300) == "1001.2 KiB");
2075
CHECK(String::humanize_size(100523550) == "95.86 MiB");
2076
CHECK(String::humanize_size(5345555000) == "4.97 GiB");
2077
}
2078
2079
TEST_CASE("[String] validate_node_name") {
2080
String numeric_only = "12345";
2081
CHECK(numeric_only.validate_node_name() == "12345");
2082
2083
String name_with_spaces = "Name with spaces";
2084
CHECK(name_with_spaces.validate_node_name() == "Name with spaces");
2085
2086
String name_with_kana = U"Name with kana ゴドツ";
2087
CHECK(name_with_kana.validate_node_name() == U"Name with kana ゴドツ");
2088
2089
String name_with_invalid_chars = "Name with invalid characters :.@%removed!";
2090
CHECK(name_with_invalid_chars.validate_node_name() == "Name with invalid characters ____removed!");
2091
}
2092
2093
TEST_CASE("[String] validate_ascii_identifier") {
2094
String empty_string;
2095
CHECK(empty_string.validate_ascii_identifier() == "_");
2096
2097
String numeric_only = "12345";
2098
CHECK(numeric_only.validate_ascii_identifier() == "_12345");
2099
2100
String name_with_spaces = "Name with spaces";
2101
CHECK(name_with_spaces.validate_ascii_identifier() == "Name_with_spaces");
2102
2103
String name_with_invalid_chars = U"Invalid characters:@*#&世界";
2104
CHECK(name_with_invalid_chars.validate_ascii_identifier() == "Invalid_characters_______");
2105
}
2106
2107
TEST_CASE("[String] validate_unicode_identifier") {
2108
String empty_string;
2109
CHECK(empty_string.validate_unicode_identifier() == "_");
2110
2111
String numeric_only = "12345";
2112
CHECK(numeric_only.validate_unicode_identifier() == "_12345");
2113
2114
String name_with_spaces = "Name with spaces";
2115
CHECK(name_with_spaces.validate_unicode_identifier() == "Name_with_spaces");
2116
2117
String name_with_invalid_chars = U"Invalid characters:@*#&世界";
2118
CHECK(name_with_invalid_chars.validate_unicode_identifier() == U"Invalid_characters_____世界");
2119
}
2120
2121
TEST_CASE("[String] Variant indexed get") {
2122
Variant s = String("abcd");
2123
bool valid = false;
2124
bool oob = true;
2125
2126
String r = s.get_indexed(1, valid, oob);
2127
2128
CHECK(valid);
2129
CHECK_FALSE(oob);
2130
CHECK_EQ(r, String("b"));
2131
}
2132
2133
TEST_CASE("[String] Variant validated indexed get") {
2134
Variant s = String("abcd");
2135
2136
Variant::ValidatedIndexedGetter getter = Variant::get_member_validated_indexed_getter(Variant::STRING);
2137
2138
Variant r;
2139
bool oob = true;
2140
getter(&s, 1, &r, &oob);
2141
2142
CHECK_FALSE(oob);
2143
CHECK_EQ(r, String("b"));
2144
}
2145
2146
TEST_CASE("[String] Variant ptr indexed get") {
2147
String s("abcd");
2148
2149
Variant::PTRIndexedGetter getter = Variant::get_member_ptr_indexed_getter(Variant::STRING);
2150
2151
String r;
2152
getter(&s, 1, &r);
2153
2154
CHECK_EQ(r, String("b"));
2155
}
2156
2157
TEST_CASE("[String] Variant indexed set") {
2158
Variant s = String("abcd");
2159
bool valid = false;
2160
bool oob = true;
2161
2162
s.set_indexed(1, String("z"), valid, oob);
2163
2164
CHECK(valid);
2165
CHECK_FALSE(oob);
2166
CHECK_EQ(s, String("azcd"));
2167
}
2168
2169
TEST_CASE("[String] Variant validated indexed set") {
2170
Variant s = String("abcd");
2171
2172
Variant::ValidatedIndexedSetter setter = Variant::get_member_validated_indexed_setter(Variant::STRING);
2173
2174
Variant v = String("z");
2175
bool oob = true;
2176
setter(&s, 1, &v, &oob);
2177
2178
CHECK_FALSE(oob);
2179
CHECK_EQ(s, String("azcd"));
2180
}
2181
2182
TEST_CASE("[String] Variant ptr indexed set") {
2183
String s("abcd");
2184
2185
Variant::PTRIndexedSetter setter = Variant::get_member_ptr_indexed_setter(Variant::STRING);
2186
2187
String v("z");
2188
setter(&s, 1, &v);
2189
2190
CHECK_EQ(s, String("azcd"));
2191
}
2192
2193
TEST_CASE("[String][URL] Parse URL") {
2194
#define CHECK_URL(m_url_to_parse, m_expected_schema, m_expected_host, m_expected_port, m_expected_path, m_expected_fragment, m_expected_error) \
2195
if (true) { \
2196
int port; \
2197
String url(m_url_to_parse), schema, host, path, fragment; \
2198
\
2199
CHECK_EQ(url.parse_url(schema, host, port, path, fragment), m_expected_error); \
2200
CHECK_EQ(schema, m_expected_schema); \
2201
CHECK_EQ(host, m_expected_host); \
2202
CHECK_EQ(path, m_expected_path); \
2203
CHECK_EQ(fragment, m_expected_fragment); \
2204
CHECK_EQ(port, m_expected_port); \
2205
} else \
2206
((void)0)
2207
2208
// All elements.
2209
CHECK_URL("https://www.example.com:8080/path/to/file.html#fragment", "https://", "www.example.com", 8080, "/path/to/file.html", "fragment", Error::OK);
2210
2211
// Valid URLs.
2212
CHECK_URL("https://godotengine.org", "https://", "godotengine.org", 0, "", "", Error::OK);
2213
CHECK_URL("https://godotengine.org/", "https://", "godotengine.org", 0, "/", "", Error::OK);
2214
CHECK_URL("godotengine.org/", "", "godotengine.org", 0, "/", "", Error::OK);
2215
CHECK_URL("HTTPS://godotengine.org/", "https://", "godotengine.org", 0, "/", "", Error::OK);
2216
CHECK_URL("https://GODOTENGINE.ORG/", "https://", "godotengine.org", 0, "/", "", Error::OK);
2217
CHECK_URL("http://godotengine.org", "http://", "godotengine.org", 0, "", "", Error::OK);
2218
CHECK_URL("https://godotengine.org:8080", "https://", "godotengine.org", 8080, "", "", Error::OK);
2219
CHECK_URL("https://godotengine.org/blog", "https://", "godotengine.org", 0, "/blog", "", Error::OK);
2220
CHECK_URL("https://godotengine.org/blog/", "https://", "godotengine.org", 0, "/blog/", "", Error::OK);
2221
CHECK_URL("https://docs.godotengine.org/en/stable", "https://", "docs.godotengine.org", 0, "/en/stable", "", Error::OK);
2222
CHECK_URL("https://docs.godotengine.org/en/stable/", "https://", "docs.godotengine.org", 0, "/en/stable/", "", Error::OK);
2223
CHECK_URL("https://me:[email protected]", "https://", "godotengine.org", 0, "", "", Error::OK);
2224
CHECK_URL("https://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]/ipv6", "https://", "fedc:ba98:7654:3210:fedc:ba98:7654:3210", 0, "/ipv6", "", Error::OK);
2225
2226
// Scheme vs Fragment.
2227
CHECK_URL("google.com/#goto=http://redirect_url/", "", "google.com", 0, "/", "goto=http://redirect_url/", Error::OK);
2228
2229
// Invalid URLs.
2230
2231
// Invalid Scheme.
2232
CHECK_URL("https_://godotengine.org", "", "https_", 0, "//godotengine.org", "", Error::ERR_INVALID_PARAMETER);
2233
2234
// Multiple ports.
2235
CHECK_URL("https://godotengine.org:8080:433", "https://", "", 0, "", "", Error::ERR_INVALID_PARAMETER);
2236
// Missing ] on literal IPv6.
2237
CHECK_URL("https://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210/ipv6", "https://", "", 0, "/ipv6", "", Error::ERR_INVALID_PARAMETER);
2238
// Missing host.
2239
CHECK_URL("https:///blog", "https://", "", 0, "/blog", "", Error::ERR_INVALID_PARAMETER);
2240
// Invalid ports.
2241
CHECK_URL("https://godotengine.org:notaport", "https://", "godotengine.org", 0, "", "", Error::ERR_INVALID_PARAMETER);
2242
CHECK_URL("https://godotengine.org:-8080", "https://", "godotengine.org", -8080, "", "", Error::ERR_INVALID_PARAMETER);
2243
CHECK_URL("https://godotengine.org:88888", "https://", "godotengine.org", 88888, "", "", Error::ERR_INVALID_PARAMETER);
2244
2245
#undef CHECK_URL
2246
}
2247
2248
} // namespace TestString
2249
2250