Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/tests/core/string/test_string.h
20843 views
1
/**************************************************************************/
2
/* test_string.h */
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
#pragma once
32
33
#include "core/string/ustring.h"
34
35
#include "tests/test_macros.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
///// Errors
1300
1301
// More formats than arguments.
1302
format = "fish %s %s frog";
1303
args.clear();
1304
args.push_back("cheese");
1305
output = format.sprintf(args, &error);
1306
REQUIRE(error);
1307
CHECK(output == "not enough arguments for format string");
1308
1309
// More arguments than formats.
1310
format = "fish %s frog";
1311
args.clear();
1312
args.push_back("hello");
1313
args.push_back("cheese");
1314
output = format.sprintf(args, &error);
1315
REQUIRE(error);
1316
CHECK(output == "not all arguments converted during string formatting");
1317
1318
// Incomplete format.
1319
format = "fish %10";
1320
args.clear();
1321
args.push_back("cheese");
1322
output = format.sprintf(args, &error);
1323
REQUIRE(error);
1324
CHECK(output == "incomplete format");
1325
1326
// Bad character in format string.
1327
format = "fish %&f frog";
1328
args.clear();
1329
args.push_back("cheese");
1330
output = format.sprintf(args, &error);
1331
REQUIRE(error);
1332
CHECK(output == "unsupported format character");
1333
1334
// Too many decimals.
1335
format = "fish %2.2.2f frog";
1336
args.clear();
1337
args.push_back(99.99);
1338
output = format.sprintf(args, &error);
1339
REQUIRE(error);
1340
CHECK(output == "too many decimal points in format");
1341
1342
// * not a number or vector.
1343
format = "fish %*f frog";
1344
args.clear();
1345
args.push_back("cheese");
1346
args.push_back(99.99);
1347
output = format.sprintf(args, &error);
1348
REQUIRE(error);
1349
CHECK(output == "* wants number or vector");
1350
1351
// Character too long.
1352
format = "fish %c frog";
1353
args.clear();
1354
args.push_back("sc");
1355
output = format.sprintf(args, &error);
1356
REQUIRE(error);
1357
CHECK(output == "%c requires number or single-character string");
1358
1359
// Character bad type.
1360
format = "fish %c frog";
1361
args.clear();
1362
args.push_back(Array());
1363
output = format.sprintf(args, &error);
1364
REQUIRE(error);
1365
CHECK(output == "%c requires number or single-character string");
1366
}
1367
1368
TEST_CASE("[String] is_numeric") {
1369
CHECK(String("12").is_numeric());
1370
CHECK(String("1.2").is_numeric());
1371
CHECK(!String("AF").is_numeric());
1372
CHECK(String("-12").is_numeric());
1373
CHECK(String("-1.2").is_numeric());
1374
}
1375
1376
TEST_CASE("[String] pad") {
1377
String s = String("test");
1378
CHECK(s.lpad(10, "x") == U"xxxxxxtest");
1379
CHECK(s.rpad(10, "x") == U"testxxxxxx");
1380
1381
s = String("10.10");
1382
CHECK(s.pad_decimals(4) == U"10.1000");
1383
CHECK(s.pad_decimals(1) == U"10.1");
1384
CHECK(s.pad_zeros(4) == U"0010.10");
1385
CHECK(s.pad_zeros(1) == U"10.10");
1386
}
1387
1388
TEST_CASE("[String] is_subsequence_of") {
1389
String a = "is subsequence of";
1390
CHECK(String("sub").is_subsequence_of(a));
1391
CHECK(!String("Sub").is_subsequence_of(a));
1392
CHECK(String("Sub").is_subsequence_ofn(a));
1393
}
1394
1395
TEST_CASE("[String] is_lowercase") {
1396
CHECK(String("abcd1234 !@#$%^&*()_-=+,.<>/\\|[]{};':\"`~").is_lowercase());
1397
CHECK(String("").is_lowercase());
1398
CHECK(!String("abc_ABC").is_lowercase());
1399
}
1400
1401
TEST_CASE("[String] match") {
1402
CHECK(String("img1.png").match("*.png"));
1403
CHECK(!String("img1.jpeg").match("*.png"));
1404
CHECK(!String("img1.Png").match("*.png"));
1405
CHECK(String("img1.Png").matchn("*.png"));
1406
}
1407
1408
TEST_CASE("[String] IPVX address to string") {
1409
IPAddress ip0("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
1410
IPAddress ip(0x0123, 0x4567, 0x89ab, 0xcdef, true);
1411
IPAddress ip2("fe80::52e5:49ff:fe93:1baf");
1412
IPAddress ip3("::ffff:192.168.0.1");
1413
String ip4 = "192.168.0.1";
1414
CHECK(ip4.is_valid_ip_address());
1415
1416
ip4 = "192.368.0.1";
1417
CHECK(!ip4.is_valid_ip_address());
1418
1419
String ip6 = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
1420
CHECK(ip6.is_valid_ip_address());
1421
1422
ip6 = "2001:0db8:85j3:0000:0000:8a2e:0370:7334";
1423
CHECK(!ip6.is_valid_ip_address());
1424
1425
ip6 = "2001:0db8:85f345:0000:0000:8a2e:0370:7334";
1426
CHECK(!ip6.is_valid_ip_address());
1427
1428
ip6 = "2001:0db8::0:8a2e:370:7334";
1429
CHECK(ip6.is_valid_ip_address());
1430
1431
ip6 = "::ffff:192.168.0.1";
1432
CHECK(ip6.is_valid_ip_address());
1433
}
1434
1435
TEST_CASE("[String] Capitalize against many strings") {
1436
String input = "2D";
1437
String output = "2d";
1438
CHECK(input.capitalize() == output);
1439
1440
input = "2d";
1441
output = "2d";
1442
CHECK(input.capitalize() == output);
1443
1444
input = "2db";
1445
output = "2 Db";
1446
CHECK(input.capitalize() == output);
1447
1448
input = "HTML5 Html5 html5 html_5";
1449
output = "Html 5 Html 5 Html 5 Html 5";
1450
CHECK(input.capitalize() == output);
1451
1452
input = "Node2D Node2d NODE2D NODE_2D node_2d";
1453
output = "Node 2d Node 2d Node 2d Node 2d Node 2d";
1454
CHECK(input.capitalize() == output);
1455
1456
input = "Node2DPosition";
1457
output = "Node 2d Position";
1458
CHECK(input.capitalize() == output);
1459
1460
input = "Number2Digits";
1461
output = "Number 2 Digits";
1462
CHECK(input.capitalize() == output);
1463
1464
input = "bytes2var";
1465
output = "Bytes 2 Var";
1466
CHECK(input.capitalize() == output);
1467
1468
input = "linear2db";
1469
output = "Linear 2 Db";
1470
CHECK(input.capitalize() == output);
1471
1472
input = "vector3";
1473
output = "Vector 3";
1474
CHECK(input.capitalize() == output);
1475
1476
input = "sha256";
1477
output = "Sha 256";
1478
CHECK(input.capitalize() == output);
1479
1480
input = "PascalCase";
1481
output = "Pascal Case";
1482
CHECK(input.capitalize() == output);
1483
1484
input = "PascalPascalCase";
1485
output = "Pascal Pascal Case";
1486
CHECK(input.capitalize() == output);
1487
1488
input = "snake_case";
1489
output = "Snake Case";
1490
CHECK(input.capitalize() == output);
1491
1492
input = "snake_snake_case";
1493
output = "Snake Snake Case";
1494
CHECK(input.capitalize() == output);
1495
1496
input = "kebab-case";
1497
output = "Kebab Case";
1498
CHECK(input.capitalize() == output);
1499
1500
input = "kebab-kebab-case";
1501
output = "Kebab Kebab Case";
1502
CHECK(input.capitalize() == output);
1503
1504
input = "sha256sum";
1505
output = "Sha 256 Sum";
1506
CHECK(input.capitalize() == output);
1507
1508
input = "cat2dog";
1509
output = "Cat 2 Dog";
1510
CHECK(input.capitalize() == output);
1511
1512
input = "function(name)";
1513
output = "Function(name)";
1514
CHECK(input.capitalize() == output);
1515
1516
input = "snake_case_function(snake_case_arg)";
1517
output = "Snake Case Function(snake Case Arg)";
1518
CHECK(input.capitalize() == output);
1519
1520
input = "snake_case_function( snake_case_arg )";
1521
output = "Snake Case Function( Snake Case Arg )";
1522
CHECK(input.capitalize() == output);
1523
1524
input = "kebab-case-function( kebab-case-arg )";
1525
output = "Kebab Case Function( Kebab Case Arg )";
1526
CHECK(input.capitalize() == output);
1527
1528
input = "kebab_case_function( kebab_case_arg )";
1529
output = "Kebab Case Function( Kebab Case Arg )";
1530
CHECK(input.capitalize() == output);
1531
1532
input = U"словоСлово_слово слово";
1533
output = U"Слово Слово Слово Слово";
1534
CHECK(input.capitalize() == output);
1535
1536
input = U"λέξηΛέξη_λέξη λέξη";
1537
output = U"Λέξη Λέξη Λέξη Λέξη";
1538
CHECK(input.capitalize() == output);
1539
1540
input = U"բառԲառ_բառ բառ";
1541
output = U"Բառ Բառ Բառ Բառ";
1542
CHECK(input.capitalize() == output);
1543
}
1544
1545
struct StringCasesTestCase {
1546
const char32_t *input;
1547
const char32_t *camel_case;
1548
const char32_t *pascal_case;
1549
const char32_t *snake_case;
1550
const char32_t *kebab_case;
1551
};
1552
1553
TEST_CASE("[String] Checking case conversion methods") {
1554
StringCasesTestCase test_cases[] = {
1555
/* clang-format off */
1556
{ U"2D", U"2d", U"2d", U"2d", U"2d" },
1557
{ U"2d", U"2d", U"2d", U"2d", U"2d" },
1558
{ U"2db", U"2Db", U"2Db", U"2_db", U"2-db" },
1559
{ U"Vector3", U"vector3", U"Vector3", U"vector_3", U"vector-3" },
1560
{ U"sha256", U"sha256", U"Sha256", U"sha_256", U"sha-256" },
1561
{ U"Node2D", U"node2d", U"Node2d", U"node_2d", U"node-2d" },
1562
{ U"RichTextLabel", U"richTextLabel", U"RichTextLabel", U"rich_text_label", U"rich-text-label" },
1563
{ U"HTML5", U"html5", U"Html5", U"html_5", U"html-5" },
1564
{ U"Node2DPosition", U"node2dPosition", U"Node2dPosition", U"node_2d_position", U"node-2d-position" },
1565
{ U"Number2Digits", U"number2Digits", U"Number2Digits", U"number_2_digits", U"number-2-digits" },
1566
{ U"get_property_list", U"getPropertyList", U"GetPropertyList", U"get_property_list", U"get-property-list" },
1567
{ U"get_camera_2d", U"getCamera2d", U"GetCamera2d", U"get_camera_2d", U"get-camera-2d" },
1568
{ U"_physics_process", U"physicsProcess", U"PhysicsProcess", U"_physics_process", U"-physics-process" },
1569
{ U"bytes2var", U"bytes2Var", U"Bytes2Var", U"bytes_2_var", U"bytes-2-var" },
1570
{ U"linear2db", U"linear2Db", U"Linear2Db", U"linear_2_db", U"linear-2-db" },
1571
{ U"sha256sum", U"sha256Sum", U"Sha256Sum", U"sha_256_sum", U"sha-256-sum" },
1572
{ U"camelCase", U"camelCase", U"CamelCase", U"camel_case", U"camel-case" },
1573
{ U"PascalCase", U"pascalCase", U"PascalCase", U"pascal_case", U"pascal-case" },
1574
{ U"snake_case", U"snakeCase", U"SnakeCase", U"snake_case", U"snake-case" },
1575
{ U"kebab-case", U"kebabCase", U"KebabCase", U"kebab_case", U"kebab-case" },
1576
{ U"Test TEST test", U"testTestTest", U"TestTestTest", U"test_test_test", U"test-test-test" },
1577
{ U"словоСлово_слово слово", U"словоСловоСловоСлово", U"СловоСловоСловоСлово", U"слово_слово_слово_слово", U"слово-слово-слово-слово" },
1578
{ U"λέξηΛέξη_λέξη λέξη", U"λέξηΛέξηΛέξηΛέξη", U"ΛέξηΛέξηΛέξηΛέξη", U"λέξη_λέξη_λέξη_λέξη", U"λέξη-λέξη-λέξη-λέξη" },
1579
{ U"բառԲառ_բառ բառ", U"բառԲառԲառԲառ", U"ԲառԲառԲառԲառ", U"բառ_բառ_բառ_բառ", U"բառ-բառ-բառ-բառ" },
1580
{ nullptr, nullptr, nullptr, nullptr, nullptr },
1581
/* clang-format on */
1582
};
1583
1584
int idx = 0;
1585
while (test_cases[idx].input != nullptr) {
1586
String input = test_cases[idx].input;
1587
CHECK(input.to_camel_case() == test_cases[idx].camel_case);
1588
CHECK(input.to_pascal_case() == test_cases[idx].pascal_case);
1589
CHECK(input.to_snake_case() == test_cases[idx].snake_case);
1590
CHECK(input.to_kebab_case() == test_cases[idx].kebab_case);
1591
idx++;
1592
}
1593
}
1594
1595
TEST_CASE("[String] Checking string is empty when it should be") {
1596
bool state = true;
1597
bool success;
1598
1599
String a = "";
1600
success = a[0] == 0;
1601
if (!success) {
1602
state = false;
1603
}
1604
String b = "Godot";
1605
success = b[b.size()] == 0;
1606
if (!success) {
1607
state = false;
1608
}
1609
const String c = "";
1610
success = c[0] == 0;
1611
if (!success) {
1612
state = false;
1613
}
1614
1615
const String d = "Godot";
1616
success = d[d.size()] == 0;
1617
if (!success) {
1618
state = false;
1619
}
1620
1621
CHECK(state);
1622
}
1623
1624
TEST_CASE("[String] lstrip and rstrip") {
1625
#define STRIP_TEST(x) \
1626
{ \
1627
bool success = x; \
1628
state = state && success; \
1629
}
1630
1631
bool state = true;
1632
1633
// strip none
1634
STRIP_TEST(String("abc").lstrip("") == "abc");
1635
STRIP_TEST(String("abc").rstrip("") == "abc");
1636
// strip one
1637
STRIP_TEST(String("abc").lstrip("a") == "bc");
1638
STRIP_TEST(String("abc").rstrip("c") == "ab");
1639
// strip lots
1640
STRIP_TEST(String("bababbababccc").lstrip("ab") == "ccc");
1641
STRIP_TEST(String("aaabcbcbcbbcbbc").rstrip("cb") == "aaa");
1642
// strip empty string
1643
STRIP_TEST(String("").lstrip("") == "");
1644
STRIP_TEST(String("").rstrip("") == "");
1645
// strip to empty string
1646
STRIP_TEST(String("abcabcabc").lstrip("bca") == "");
1647
STRIP_TEST(String("abcabcabc").rstrip("bca") == "");
1648
// don't strip wrong end
1649
STRIP_TEST(String("abc").lstrip("c") == "abc");
1650
STRIP_TEST(String("abca").lstrip("a") == "bca");
1651
STRIP_TEST(String("abc").rstrip("a") == "abc");
1652
STRIP_TEST(String("abca").rstrip("a") == "abc");
1653
// in utf-8 "¿" (\u00bf) has the same first byte as "µ" (\u00b5)
1654
// and the same second as "ÿ" (\u00ff)
1655
STRIP_TEST(String::utf8("¿").lstrip(String::utf8("µÿ")) == String::utf8("¿"));
1656
STRIP_TEST(String::utf8("¿").rstrip(String::utf8("µÿ")) == String::utf8("¿"));
1657
STRIP_TEST(String::utf8("µ¿ÿ").lstrip(String::utf8("µÿ")) == String::utf8("¿ÿ"));
1658
STRIP_TEST(String::utf8("µ¿ÿ").rstrip(String::utf8("µÿ")) == String::utf8("µ¿"));
1659
1660
// the above tests repeated with additional superfluous strip chars
1661
1662
// strip none
1663
STRIP_TEST(String("abc").lstrip("qwjkl") == "abc");
1664
STRIP_TEST(String("abc").rstrip("qwjkl") == "abc");
1665
// strip one
1666
STRIP_TEST(String("abc").lstrip("qwajkl") == "bc");
1667
STRIP_TEST(String("abc").rstrip("qwcjkl") == "ab");
1668
// strip lots
1669
STRIP_TEST(String("bababbababccc").lstrip("qwabjkl") == "ccc");
1670
STRIP_TEST(String("aaabcbcbcbbcbbc").rstrip("qwcbjkl") == "aaa");
1671
// strip empty string
1672
STRIP_TEST(String("").lstrip("qwjkl") == "");
1673
STRIP_TEST(String("").rstrip("qwjkl") == "");
1674
// strip to empty string
1675
STRIP_TEST(String("abcabcabc").lstrip("qwbcajkl") == "");
1676
STRIP_TEST(String("abcabcabc").rstrip("qwbcajkl") == "");
1677
// don't strip wrong end
1678
STRIP_TEST(String("abc").lstrip("qwcjkl") == "abc");
1679
STRIP_TEST(String("abca").lstrip("qwajkl") == "bca");
1680
STRIP_TEST(String("abc").rstrip("qwajkl") == "abc");
1681
STRIP_TEST(String("abca").rstrip("qwajkl") == "abc");
1682
// in utf-8 "¿" (\u00bf) has the same first byte as "µ" (\u00b5)
1683
// and the same second as "ÿ" (\u00ff)
1684
STRIP_TEST(String::utf8("¿").lstrip(String::utf8("qwaµÿjkl")) == String::utf8("¿"));
1685
STRIP_TEST(String::utf8("¿").rstrip(String::utf8("qwaµÿjkl")) == String::utf8("¿"));
1686
STRIP_TEST(String::utf8("µ¿ÿ").lstrip(String::utf8("qwaµÿjkl")) == String::utf8("¿ÿ"));
1687
STRIP_TEST(String::utf8("µ¿ÿ").rstrip(String::utf8("qwaµÿjkl")) == String::utf8("µ¿"));
1688
1689
CHECK(state);
1690
1691
#undef STRIP_TEST
1692
}
1693
1694
TEST_CASE("[String] Ensuring empty string into extend_utf8 passes empty string") {
1695
String empty;
1696
CHECK(empty.append_utf8(nullptr, -1) == ERR_INVALID_DATA);
1697
}
1698
1699
TEST_CASE("[String] Cyrillic to_lower()") {
1700
String upper = U"АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ";
1701
String lower = U"абвгдеёжзийклмнопрстуфхцчшщъыьэюя";
1702
1703
String test = upper.to_lower();
1704
1705
bool state = test == lower;
1706
1707
CHECK(state);
1708
}
1709
1710
TEST_CASE("[String] Count and countn functionality") {
1711
String s = String("");
1712
MULTICHECK_STRING_EQ(s, count, "Test", 0);
1713
1714
s = "Test";
1715
MULTICHECK_STRING_EQ(s, count, "", 0);
1716
1717
s = "Test";
1718
MULTICHECK_STRING_EQ(s, count, "test", 0);
1719
1720
s = "Test";
1721
MULTICHECK_STRING_EQ(s, count, "TEST", 0);
1722
1723
s = "TEST";
1724
MULTICHECK_STRING_EQ(s, count, "TEST", 1);
1725
1726
s = "Test";
1727
MULTICHECK_STRING_EQ(s, count, "Test", 1);
1728
1729
s = "aTest";
1730
MULTICHECK_STRING_EQ(s, count, "Test", 1);
1731
1732
s = "Testa";
1733
MULTICHECK_STRING_EQ(s, count, "Test", 1);
1734
1735
s = "TestTestTest";
1736
MULTICHECK_STRING_EQ(s, count, "Test", 3);
1737
1738
s = "TestTestTest";
1739
MULTICHECK_STRING_EQ(s, count, "TestTest", 1);
1740
1741
s = "TestGodotTestGodotTestGodot";
1742
MULTICHECK_STRING_EQ(s, count, "Test", 3);
1743
1744
s = "TestTestTestTest";
1745
MULTICHECK_STRING_INT_INT_EQ(s, count, "Test", 4, 8, 1);
1746
1747
s = "TestTestTestTest";
1748
MULTICHECK_STRING_INT_INT_EQ(s, count, "Test", 4, 12, 2);
1749
1750
s = "TestTestTestTest";
1751
MULTICHECK_STRING_INT_INT_EQ(s, count, "Test", 4, 16, 3);
1752
1753
s = "TestTestTestTest";
1754
MULTICHECK_STRING_INT_EQ(s, count, "Test", 4, 3);
1755
1756
s = "Test";
1757
MULTICHECK_STRING_EQ(s, countn, "test", 1);
1758
1759
s = "Test";
1760
MULTICHECK_STRING_EQ(s, countn, "TEST", 1);
1761
1762
s = "testTest-Testatest";
1763
MULTICHECK_STRING_EQ(s, countn, "tEst", 4);
1764
1765
s = "testTest-TeStatest";
1766
MULTICHECK_STRING_INT_INT_EQ(s, countn, "tEsT", 4, 16, 2);
1767
}
1768
1769
TEST_CASE("[String] Bigrams") {
1770
String s = "abcd";
1771
Vector<String> bigr = s.bigrams();
1772
1773
CHECK(bigr.size() == 3);
1774
CHECK(bigr[0] == "ab");
1775
CHECK(bigr[1] == "bc");
1776
CHECK(bigr[2] == "cd");
1777
}
1778
1779
TEST_CASE("[String] c-escape/unescape") {
1780
String s = "\\1\a2\b\f3\n45\r6\t7\v8\'9\?0\"";
1781
CHECK(s.c_escape().c_unescape() == s);
1782
}
1783
1784
TEST_CASE("[String] indent") {
1785
static const char *input[] = {
1786
"",
1787
"aaa\nbbb",
1788
"\tcontains\n\tindent",
1789
"empty\n\nline",
1790
};
1791
static const char *expected[] = {
1792
"",
1793
"\taaa\n\tbbb",
1794
"\t\tcontains\n\t\tindent",
1795
"\tempty\n\n\tline",
1796
};
1797
1798
for (int i = 0; i < 3; i++) {
1799
CHECK(String(input[i]).indent("\t") == expected[i]);
1800
}
1801
}
1802
1803
TEST_CASE("[String] dedent") {
1804
String s = " aaa\n bbb";
1805
String t = "aaa\nbbb";
1806
CHECK(s.dedent() == t);
1807
}
1808
1809
TEST_CASE("[String] Path functions") {
1810
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" };
1811
static const char *base_dir[8] = { "C:\\Godot\\project", "/Godot/project", "../Godot/project", "Godot", "C:\\", "res://", "user://", "/" };
1812
static const char *base_name[8] = { "C:\\Godot\\project\\test", "/Godot/project/test", "../Godot/project/test", "Godot\\test", "C:\\test", "res://test", "user://test", "/" };
1813
static const char *ext[8] = { "tscn", "xscn", "scn", "doc", "", "", "", "test" };
1814
static const char *file[8] = { "test.tscn", "test.xscn", "test.scn", "test.doc", "test.", "test", "test", ".test" };
1815
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" };
1816
static const bool abs[8] = { true, true, false, false, true, true, true, true };
1817
1818
for (int i = 0; i < 8; i++) {
1819
CHECK(String(path[i]).get_base_dir() == base_dir[i]);
1820
CHECK(String(path[i]).get_basename() == base_name[i]);
1821
CHECK(String(path[i]).get_extension() == ext[i]);
1822
CHECK(String(path[i]).get_file() == file[i]);
1823
CHECK(String(path[i]).is_absolute_path() == abs[i]);
1824
CHECK(String(path[i]).is_relative_path() != abs[i]);
1825
CHECK(String(path[i]).simplify_path() == String(simplified[i]));
1826
CHECK(String(path[i]).simplify_path().get_base_dir().path_join(file[i]) == String(path[i]).simplify_path());
1827
}
1828
1829
CHECK(String("res://test.png").has_extension("png"));
1830
CHECK(String("res://test.PNG").has_extension("png"));
1831
CHECK_FALSE(String("res://test.png").has_extension("jpg"));
1832
CHECK_FALSE(String("res://test.png/README").has_extension("png"));
1833
CHECK_FALSE(String("res://test.").has_extension("png"));
1834
CHECK_FALSE(String("res://test").has_extension("png"));
1835
1836
static const char *file_name[3] = { "test.tscn", "test://.xscn", "?tes*t.scn" };
1837
static const bool valid[3] = { true, false, false };
1838
for (int i = 0; i < 3; i++) {
1839
CHECK(String(file_name[i]).is_valid_filename() == valid[i]);
1840
}
1841
1842
CHECK(String("res://texture.png") == String("res://folder/../folder/../texture.png").simplify_path());
1843
CHECK(String("res://texture.png") == String("res://folder/sub/../../texture.png").simplify_path());
1844
CHECK(String("res://../../texture.png") == String("res://../../texture.png").simplify_path());
1845
}
1846
1847
TEST_CASE("[String] hash") {
1848
String a = "Test";
1849
String b = "Test";
1850
String c = "West";
1851
CHECK(a.hash() == b.hash());
1852
CHECK(a.hash() != c.hash());
1853
1854
CHECK(a.hash64() == b.hash64());
1855
CHECK(a.hash64() != c.hash64());
1856
}
1857
1858
TEST_CASE("[String] uri_encode/unescape") {
1859
String s = "Godot Engine:'docs'";
1860
String t = "Godot%20Engine%3A%27docs%27";
1861
1862
String x1 = "T%C4%93%C5%A1t";
1863
static const uint8_t u8str[] = { 0x54, 0xC4, 0x93, 0xC5, 0xA1, 0x74, 0x00 };
1864
String x2 = String::utf8((const char *)u8str);
1865
String x3 = U"Tēšt";
1866
String x4 = U"file+name";
1867
1868
CHECK(x1.uri_decode() == x2);
1869
CHECK(x1.uri_decode() == x3);
1870
CHECK((x1 + x3).uri_decode() == (x2 + x3)); // Mixed unicode and URL encoded string, e.g. GTK+ bookmark.
1871
CHECK(x2.uri_encode() == x1);
1872
CHECK(x3.uri_encode() == x1);
1873
1874
CHECK(s.uri_encode() == t);
1875
CHECK(t.uri_decode() == s);
1876
CHECK(x4.uri_file_decode() == x4);
1877
CHECK(x4.uri_decode() == U"file name");
1878
}
1879
1880
TEST_CASE("[String] xml_escape/unescape") {
1881
String s = "\"Test\" <test@test&'test'>";
1882
CHECK(s.xml_escape(true).xml_unescape() == s);
1883
CHECK(s.xml_escape(false).xml_unescape() == s);
1884
}
1885
1886
TEST_CASE("[String] xml_unescape") {
1887
// Named entities
1888
String input = "&quot;&amp;&apos;&lt;&gt;";
1889
CHECK(input.xml_unescape() == "\"&\'<>");
1890
1891
// Numeric entities
1892
input = "&#x41;&#66;";
1893
CHECK(input.xml_unescape() == "AB");
1894
1895
input = "&#0;&x#0;More text";
1896
String result = input.xml_unescape();
1897
// Didn't put in a leading NUL and terminate the string
1898
CHECK(input.length() > 0);
1899
CHECK(input[0] != '\0');
1900
// Entity should be left as-is if invalid
1901
CHECK(input.xml_unescape() == input);
1902
1903
// Check near char32_t range
1904
input = "&#xFFFFFFFF;";
1905
result = input.xml_unescape();
1906
CHECK(result.length() == 1);
1907
CHECK(result[0] == 0xFFFFFFFF);
1908
input = "&#4294967295;";
1909
result = input.xml_unescape();
1910
CHECK(result.length() == 1);
1911
CHECK(result[0] == 0xFFFFFFFF);
1912
1913
// Check out of range of char32_t
1914
input = "&#xFFFFFFFFF;";
1915
CHECK(input.xml_unescape() == input);
1916
input = "&#4294967296;";
1917
CHECK(input.xml_unescape() == input);
1918
1919
// Shouldn't consume without ending in a ';'
1920
input = "&#66";
1921
CHECK(input.xml_unescape() == input);
1922
input = "&#x41";
1923
CHECK(input.xml_unescape() == input);
1924
1925
// Invalid characters should make the entity ignored
1926
input = "&#x41SomeIrrelevantText;";
1927
CHECK(input.xml_unescape() == input);
1928
input = "&#66SomeIrrelevantText;";
1929
CHECK(input.xml_unescape() == input);
1930
}
1931
1932
TEST_CASE("[String] Strip escapes") {
1933
String s = "\t\tTest Test\r\n Test";
1934
CHECK(s.strip_escapes() == "Test Test Test");
1935
}
1936
1937
TEST_CASE("[String] Similarity") {
1938
String a = "Test";
1939
String b = "West";
1940
String c = "Toad";
1941
CHECK(a.similarity(b) > a.similarity(c));
1942
}
1943
1944
TEST_CASE("[String] Strip edges") {
1945
String s = "\t Test Test ";
1946
CHECK(s.strip_edges(true, false) == "Test Test ");
1947
CHECK(s.strip_edges(false, true) == "\t Test Test");
1948
CHECK(s.strip_edges(true, true) == "Test Test");
1949
}
1950
1951
TEST_CASE("[String] Trim") {
1952
String s = "aaaTestbbb";
1953
MULTICHECK_STRING_EQ(s, trim_prefix, "aaa", "Testbbb");
1954
MULTICHECK_STRING_EQ(s, trim_prefix, "Test", s);
1955
MULTICHECK_STRING_EQ(s, trim_prefix, "", s);
1956
MULTICHECK_STRING_EQ(s, trim_prefix, "aaaTestbbb", "");
1957
MULTICHECK_STRING_EQ(s, trim_prefix, "bbb", s);
1958
MULTICHECK_STRING_EQ(s, trim_prefix, "AAA", s);
1959
1960
MULTICHECK_STRING_EQ(s, trim_suffix, "bbb", "aaaTest");
1961
MULTICHECK_STRING_EQ(s, trim_suffix, "Test", s);
1962
MULTICHECK_STRING_EQ(s, trim_suffix, "", s);
1963
MULTICHECK_STRING_EQ(s, trim_suffix, "aaaTestbbb", "");
1964
MULTICHECK_STRING_EQ(s, trim_suffix, "aaa", s);
1965
MULTICHECK_STRING_EQ(s, trim_suffix, "BBB", s);
1966
}
1967
1968
TEST_CASE("[String] Right/Left") {
1969
String s = "aaaTestbbb";
1970
// ^
1971
CHECK(s.right(6) == "estbbb");
1972
CHECK(s.right(-6) == "tbbb");
1973
CHECK(s.left(6) == "aaaTes");
1974
CHECK(s.left(-6) == "aaaT");
1975
}
1976
1977
TEST_CASE("[String] Repeat") {
1978
String s = "abababab";
1979
String x = "ab";
1980
String t = x.repeat(4);
1981
CHECK(t == s);
1982
}
1983
1984
TEST_CASE("[String] Reverse") {
1985
String s = "Abcd";
1986
CHECK(s.reverse() == "dcbA");
1987
}
1988
1989
TEST_CASE("[String] SHA1/SHA256/MD5") {
1990
String s = "Godot";
1991
String sha1 = "a1e91f39b9fce6a9998b14bdbe2aa2b39dc2d201";
1992
static uint8_t sha1_buf[20] = {
1993
0xA1, 0xE9, 0x1F, 0x39, 0xB9, 0xFC, 0xE6, 0xA9, 0x99, 0x8B, 0x14, 0xBD, 0xBE, 0x2A, 0xA2, 0xB3,
1994
0x9D, 0xC2, 0xD2, 0x01
1995
};
1996
String sha256 = "2a02b2443f7985d89d09001086ae3dcfa6eb0f55c6ef170715d42328e16e6cb8";
1997
static uint8_t sha256_buf[32] = {
1998
0x2A, 0x02, 0xB2, 0x44, 0x3F, 0x79, 0x85, 0xD8, 0x9D, 0x09, 0x00, 0x10, 0x86, 0xAE, 0x3D, 0xCF,
1999
0xA6, 0xEB, 0x0F, 0x55, 0xC6, 0xEF, 0x17, 0x07, 0x15, 0xD4, 0x23, 0x28, 0xE1, 0x6E, 0x6C, 0xB8
2000
};
2001
String md5 = "4a336d087aeb0390da10ee2ea7cb87f8";
2002
static uint8_t md5_buf[16] = {
2003
0x4A, 0x33, 0x6D, 0x08, 0x7A, 0xEB, 0x03, 0x90, 0xDA, 0x10, 0xEE, 0x2E, 0xA7, 0xCB, 0x87, 0xF8
2004
};
2005
2006
PackedByteArray buf = s.sha1_buffer();
2007
CHECK(memcmp(sha1_buf, buf.ptr(), 20) == 0);
2008
CHECK(s.sha1_text() == sha1);
2009
2010
buf = s.sha256_buffer();
2011
CHECK(memcmp(sha256_buf, buf.ptr(), 32) == 0);
2012
CHECK(s.sha256_text() == sha256);
2013
2014
buf = s.md5_buffer();
2015
CHECK(memcmp(md5_buf, buf.ptr(), 16) == 0);
2016
CHECK(s.md5_text() == md5);
2017
}
2018
2019
TEST_CASE("[String] Join") {
2020
String comma = ", ";
2021
String empty = "";
2022
Vector<String> parts;
2023
2024
CHECK(comma.join(parts) == "");
2025
CHECK(empty.join(parts) == "");
2026
2027
parts.push_back("One");
2028
CHECK(comma.join(parts) == "One");
2029
CHECK(empty.join(parts) == "One");
2030
2031
parts.push_back("B");
2032
parts.push_back("C");
2033
CHECK(comma.join(parts) == "One, B, C");
2034
CHECK(empty.join(parts) == "OneBC");
2035
2036
parts.push_back("");
2037
CHECK(comma.join(parts) == "One, B, C, ");
2038
CHECK(empty.join(parts) == "OneBC");
2039
}
2040
2041
TEST_CASE("[String] Is_*") {
2042
static const char *data[] = { "-30", "100", "10.1", "10,1", "1e2", "1e-2", "1e2e3", "0xAB", "AB", "Test1", "1Test", "Test*1", "文字", "1E2", "1E-2" };
2043
static bool isnum[] = { true, true, true, false, false, false, false, false, false, false, false, false, false, false, false };
2044
static bool isint[] = { true, true, false, false, false, false, false, false, false, false, false, false, false, false, false };
2045
static bool ishex[] = { true, true, false, false, true, false, true, false, true, false, false, false, false, true, false };
2046
static bool ishex_p[] = { false, false, false, false, false, false, false, true, false, false, false, false, false, false, false };
2047
static bool isflt[] = { true, true, true, false, true, true, false, false, false, false, false, false, false, true, true };
2048
static bool isaid[] = { false, false, false, false, false, false, false, false, true, true, false, false, false, false, false };
2049
static bool isuid[] = { false, false, false, false, false, false, false, false, true, true, false, false, true, false, false };
2050
for (unsigned int i = 0; i < std_size(data); i++) {
2051
String s = String::utf8(data[i]);
2052
CHECK(s.is_numeric() == isnum[i]);
2053
CHECK(s.is_valid_int() == isint[i]);
2054
CHECK(s.is_valid_hex_number(false) == ishex[i]);
2055
CHECK(s.is_valid_hex_number(true) == ishex_p[i]);
2056
CHECK(s.is_valid_float() == isflt[i]);
2057
CHECK(s.is_valid_ascii_identifier() == isaid[i]);
2058
CHECK(s.is_valid_unicode_identifier() == isuid[i]);
2059
}
2060
}
2061
2062
TEST_CASE("[String] humanize_size") {
2063
CHECK(String::humanize_size(1000) == "1000 B");
2064
CHECK(String::humanize_size(1025) == "1.00 KiB");
2065
CHECK(String::humanize_size(1025300) == "1001.2 KiB");
2066
CHECK(String::humanize_size(100523550) == "95.86 MiB");
2067
CHECK(String::humanize_size(5345555000) == "4.97 GiB");
2068
}
2069
2070
TEST_CASE("[String] validate_node_name") {
2071
String numeric_only = "12345";
2072
CHECK(numeric_only.validate_node_name() == "12345");
2073
2074
String name_with_spaces = "Name with spaces";
2075
CHECK(name_with_spaces.validate_node_name() == "Name with spaces");
2076
2077
String name_with_kana = U"Name with kana ゴドツ";
2078
CHECK(name_with_kana.validate_node_name() == U"Name with kana ゴドツ");
2079
2080
String name_with_invalid_chars = "Name with invalid characters :.@%removed!";
2081
CHECK(name_with_invalid_chars.validate_node_name() == "Name with invalid characters ____removed!");
2082
}
2083
2084
TEST_CASE("[String] validate_ascii_identifier") {
2085
String empty_string;
2086
CHECK(empty_string.validate_ascii_identifier() == "_");
2087
2088
String numeric_only = "12345";
2089
CHECK(numeric_only.validate_ascii_identifier() == "_12345");
2090
2091
String name_with_spaces = "Name with spaces";
2092
CHECK(name_with_spaces.validate_ascii_identifier() == "Name_with_spaces");
2093
2094
String name_with_invalid_chars = U"Invalid characters:@*#&世界";
2095
CHECK(name_with_invalid_chars.validate_ascii_identifier() == "Invalid_characters_______");
2096
}
2097
2098
TEST_CASE("[String] validate_unicode_identifier") {
2099
String empty_string;
2100
CHECK(empty_string.validate_unicode_identifier() == "_");
2101
2102
String numeric_only = "12345";
2103
CHECK(numeric_only.validate_unicode_identifier() == "_12345");
2104
2105
String name_with_spaces = "Name with spaces";
2106
CHECK(name_with_spaces.validate_unicode_identifier() == "Name_with_spaces");
2107
2108
String name_with_invalid_chars = U"Invalid characters:@*#&世界";
2109
CHECK(name_with_invalid_chars.validate_unicode_identifier() == U"Invalid_characters_____世界");
2110
}
2111
2112
TEST_CASE("[String] Variant indexed get") {
2113
Variant s = String("abcd");
2114
bool valid = false;
2115
bool oob = true;
2116
2117
String r = s.get_indexed(1, valid, oob);
2118
2119
CHECK(valid);
2120
CHECK_FALSE(oob);
2121
CHECK_EQ(r, String("b"));
2122
}
2123
2124
TEST_CASE("[String] Variant validated indexed get") {
2125
Variant s = String("abcd");
2126
2127
Variant::ValidatedIndexedGetter getter = Variant::get_member_validated_indexed_getter(Variant::STRING);
2128
2129
Variant r;
2130
bool oob = true;
2131
getter(&s, 1, &r, &oob);
2132
2133
CHECK_FALSE(oob);
2134
CHECK_EQ(r, String("b"));
2135
}
2136
2137
TEST_CASE("[String] Variant ptr indexed get") {
2138
String s("abcd");
2139
2140
Variant::PTRIndexedGetter getter = Variant::get_member_ptr_indexed_getter(Variant::STRING);
2141
2142
String r;
2143
getter(&s, 1, &r);
2144
2145
CHECK_EQ(r, String("b"));
2146
}
2147
2148
TEST_CASE("[String] Variant indexed set") {
2149
Variant s = String("abcd");
2150
bool valid = false;
2151
bool oob = true;
2152
2153
s.set_indexed(1, String("z"), valid, oob);
2154
2155
CHECK(valid);
2156
CHECK_FALSE(oob);
2157
CHECK_EQ(s, String("azcd"));
2158
}
2159
2160
TEST_CASE("[String] Variant validated indexed set") {
2161
Variant s = String("abcd");
2162
2163
Variant::ValidatedIndexedSetter setter = Variant::get_member_validated_indexed_setter(Variant::STRING);
2164
2165
Variant v = String("z");
2166
bool oob = true;
2167
setter(&s, 1, &v, &oob);
2168
2169
CHECK_FALSE(oob);
2170
CHECK_EQ(s, String("azcd"));
2171
}
2172
2173
TEST_CASE("[String] Variant ptr indexed set") {
2174
String s("abcd");
2175
2176
Variant::PTRIndexedSetter setter = Variant::get_member_ptr_indexed_setter(Variant::STRING);
2177
2178
String v("z");
2179
setter(&s, 1, &v);
2180
2181
CHECK_EQ(s, String("azcd"));
2182
}
2183
2184
TEST_CASE("[String][URL] Parse URL") {
2185
#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) \
2186
if (true) { \
2187
int port; \
2188
String url(m_url_to_parse), schema, host, path, fragment; \
2189
\
2190
CHECK_EQ(url.parse_url(schema, host, port, path, fragment), m_expected_error); \
2191
CHECK_EQ(schema, m_expected_schema); \
2192
CHECK_EQ(host, m_expected_host); \
2193
CHECK_EQ(path, m_expected_path); \
2194
CHECK_EQ(fragment, m_expected_fragment); \
2195
CHECK_EQ(port, m_expected_port); \
2196
} else \
2197
((void)0)
2198
2199
// All elements.
2200
CHECK_URL("https://www.example.com:8080/path/to/file.html#fragment", "https://", "www.example.com", 8080, "/path/to/file.html", "fragment", Error::OK);
2201
2202
// Valid URLs.
2203
CHECK_URL("https://godotengine.org", "https://", "godotengine.org", 0, "", "", Error::OK);
2204
CHECK_URL("https://godotengine.org/", "https://", "godotengine.org", 0, "/", "", Error::OK);
2205
CHECK_URL("godotengine.org/", "", "godotengine.org", 0, "/", "", Error::OK);
2206
CHECK_URL("HTTPS://godotengine.org/", "https://", "godotengine.org", 0, "/", "", Error::OK);
2207
CHECK_URL("https://GODOTENGINE.ORG/", "https://", "godotengine.org", 0, "/", "", Error::OK);
2208
CHECK_URL("http://godotengine.org", "http://", "godotengine.org", 0, "", "", Error::OK);
2209
CHECK_URL("https://godotengine.org:8080", "https://", "godotengine.org", 8080, "", "", Error::OK);
2210
CHECK_URL("https://godotengine.org/blog", "https://", "godotengine.org", 0, "/blog", "", Error::OK);
2211
CHECK_URL("https://godotengine.org/blog/", "https://", "godotengine.org", 0, "/blog/", "", Error::OK);
2212
CHECK_URL("https://docs.godotengine.org/en/stable", "https://", "docs.godotengine.org", 0, "/en/stable", "", Error::OK);
2213
CHECK_URL("https://docs.godotengine.org/en/stable/", "https://", "docs.godotengine.org", 0, "/en/stable/", "", Error::OK);
2214
CHECK_URL("https://me:[email protected]", "https://", "godotengine.org", 0, "", "", Error::OK);
2215
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);
2216
2217
// Scheme vs Fragment.
2218
CHECK_URL("google.com/#goto=http://redirect_url/", "", "google.com", 0, "/", "goto=http://redirect_url/", Error::OK);
2219
2220
// Invalid URLs.
2221
2222
// Invalid Scheme.
2223
CHECK_URL("https_://godotengine.org", "", "https_", 0, "//godotengine.org", "", Error::ERR_INVALID_PARAMETER);
2224
2225
// Multiple ports.
2226
CHECK_URL("https://godotengine.org:8080:433", "https://", "", 0, "", "", Error::ERR_INVALID_PARAMETER);
2227
// Missing ] on literal IPv6.
2228
CHECK_URL("https://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210/ipv6", "https://", "", 0, "/ipv6", "", Error::ERR_INVALID_PARAMETER);
2229
// Missing host.
2230
CHECK_URL("https:///blog", "https://", "", 0, "/blog", "", Error::ERR_INVALID_PARAMETER);
2231
// Invalid ports.
2232
CHECK_URL("https://godotengine.org:notaport", "https://", "godotengine.org", 0, "", "", Error::ERR_INVALID_PARAMETER);
2233
CHECK_URL("https://godotengine.org:-8080", "https://", "godotengine.org", -8080, "", "", Error::ERR_INVALID_PARAMETER);
2234
CHECK_URL("https://godotengine.org:88888", "https://", "godotengine.org", 88888, "", "", Error::ERR_INVALID_PARAMETER);
2235
2236
#undef CHECK_URL
2237
}
2238
} // namespace TestString
2239
2240