3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "util/enriched_string.h"
24 #include "util/numeric.h"
25 #include "util/string.h"
26 #include "util/base64.h"
28 class TestUtilities : public TestBase {
30 TestUtilities() { TestManager::registerTestModule(this); }
31 const char *getName() { return "TestUtilities"; }
33 void runTests(IGameDef *gamedef);
35 void testAngleWrapAround();
36 void testWrapDegrees_0_360_v3f();
40 void testRemoveStringEnd();
44 void testStartsWith();
46 void testStrToIntConversion();
47 void testStringReplace();
48 void testStringAllowed();
49 void testAsciiPrintableHelper();
51 void testRemoveEscapes();
53 void testEnrichedString();
55 void testIsPowerOfTwo();
57 void testStringJoin();
58 void testEulerConversion();
60 void testSanitizeDirName();
63 static TestUtilities g_test_instance;
65 void TestUtilities::runTests(IGameDef *gamedef)
67 TEST(testAngleWrapAround);
68 TEST(testWrapDegrees_0_360_v3f);
72 TEST(testRemoveStringEnd);
78 TEST(testStrToIntConversion);
79 TEST(testStringReplace);
80 TEST(testStringAllowed);
81 TEST(testAsciiPrintableHelper);
83 TEST(testRemoveEscapes);
85 TEST(testEnrichedString);
87 TEST(testIsPowerOfTwo);
90 TEST(testEulerConversion);
92 TEST(testSanitizeDirName);
95 ////////////////////////////////////////////////////////////////////////////////
97 inline float ref_WrapDegrees180(float f)
99 // This is a slower alternative to the wrapDegrees_180() function;
100 // used as a reference for testing
101 float value = fmodf(f + 180, 360);
108 inline float ref_WrapDegrees_0_360(float f)
110 // This is a slower alternative to the wrapDegrees_0_360() function;
111 // used as a reference for testing
112 float value = fmodf(f, 360);
115 return value < 0 ? value + 360 : value;
119 void TestUtilities::testAngleWrapAround() {
120 UASSERT(fabs(modulo360f(100.0) - 100.0) < 0.001);
121 UASSERT(fabs(modulo360f(720.5) - 0.5) < 0.001);
122 UASSERT(fabs(modulo360f(-0.5) - (-0.5)) < 0.001);
123 UASSERT(fabs(modulo360f(-365.5) - (-5.5)) < 0.001);
125 for (float f = -720; f <= -360; f += 0.25) {
126 UASSERT(std::fabs(modulo360f(f) - modulo360f(f + 360)) < 0.001);
129 for (float f = -1440; f <= 1440; f += 0.25) {
130 UASSERT(std::fabs(modulo360f(f) - fmodf(f, 360)) < 0.001);
131 UASSERT(std::fabs(wrapDegrees_180(f) - ref_WrapDegrees180(f)) < 0.001);
132 UASSERT(std::fabs(wrapDegrees_0_360(f) - ref_WrapDegrees_0_360(f)) < 0.001);
133 UASSERT(wrapDegrees_0_360(
134 std::fabs(wrapDegrees_180(f) - wrapDegrees_0_360(f))) < 0.001);
139 void TestUtilities::testWrapDegrees_0_360_v3f()
141 // only x test with little step
142 for (float x = -720.f; x <= 720; x += 0.05) {
143 v3f r = wrapDegrees_0_360_v3f(v3f(x, 0, 0));
144 UASSERT(r.X >= 0.0f && r.X < 360.0f)
149 // only y test with little step
150 for (float y = -720.f; y <= 720; y += 0.05) {
151 v3f r = wrapDegrees_0_360_v3f(v3f(0, y, 0));
153 UASSERT(r.Y >= 0.0f && r.Y < 360.0f)
157 // only z test with little step
158 for (float z = -720.f; z <= 720; z += 0.05) {
159 v3f r = wrapDegrees_0_360_v3f(v3f(0, 0, z));
162 UASSERT(r.Z >= 0.0f && r.Z < 360.0f)
165 // test the whole coordinate translation
166 for (float x = -720.f; x <= 720; x += 2.5) {
167 for (float y = -720.f; y <= 720; y += 2.5) {
168 for (float z = -720.f; z <= 720; z += 2.5) {
169 v3f r = wrapDegrees_0_360_v3f(v3f(x, y, z));
170 UASSERT(r.X >= 0.0f && r.X < 360.0f)
171 UASSERT(r.Y >= 0.0f && r.Y < 360.0f)
172 UASSERT(r.Z >= 0.0f && r.Z < 360.0f)
179 void TestUtilities::testLowercase()
181 UASSERT(lowercase("Foo bAR") == "foo bar");
182 UASSERT(lowercase("eeeeeeaaaaaaaaaaaààààà") == "eeeeeeaaaaaaaaaaaààààà");
183 UASSERT(lowercase("MINETEST-powa") == "minetest-powa");
187 void TestUtilities::testTrim()
189 UASSERT(trim("") == "");
190 UASSERT(trim("dirt_with_grass") == "dirt_with_grass");
191 UASSERT(trim("\n \t\r Foo bAR \r\n\t\t ") == "Foo bAR");
192 UASSERT(trim("\n \t\r \r\n\t\t ") == "");
193 UASSERT(trim(" a") == "a");
194 UASSERT(trim("a ") == "a");
198 void TestUtilities::testIsYes()
200 UASSERT(is_yes("YeS") == true);
201 UASSERT(is_yes("") == false);
202 UASSERT(is_yes("FAlse") == false);
203 UASSERT(is_yes("-1") == true);
204 UASSERT(is_yes("0") == false);
205 UASSERT(is_yes("1") == true);
206 UASSERT(is_yes("2") == true);
210 void TestUtilities::testRemoveStringEnd()
212 const char *ends[] = {"abc", "c", "bc", "", NULL};
213 UASSERT(removeStringEnd("abc", ends) == "");
214 UASSERT(removeStringEnd("bc", ends) == "b");
215 UASSERT(removeStringEnd("12c", ends) == "12");
216 UASSERT(removeStringEnd("foo", ends) == "");
220 void TestUtilities::testUrlEncode()
222 UASSERT(urlencode("\"Aardvarks lurk, OK?\"")
223 == "%22Aardvarks%20lurk%2C%20OK%3F%22");
227 void TestUtilities::testUrlDecode()
229 UASSERT(urldecode("%22Aardvarks%20lurk%2C%20OK%3F%22")
230 == "\"Aardvarks lurk, OK?\"");
234 void TestUtilities::testPadString()
236 UASSERT(padStringRight("hello", 8) == "hello ");
239 void TestUtilities::testStartsWith()
241 UASSERT(str_starts_with(std::string(), std::string()) == true);
242 UASSERT(str_starts_with(std::string("the sharp pickaxe"),
243 std::string()) == true);
244 UASSERT(str_starts_with(std::string("the sharp pickaxe"),
245 std::string("the")) == true);
246 UASSERT(str_starts_with(std::string("the sharp pickaxe"),
247 std::string("The")) == false);
248 UASSERT(str_starts_with(std::string("the sharp pickaxe"),
249 std::string("The"), true) == true);
250 UASSERT(str_starts_with(std::string("T"), std::string("The")) == false);
253 void TestUtilities::testStrEqual()
255 UASSERT(str_equal(utf8_to_wide("abc"), utf8_to_wide("abc")));
256 UASSERT(str_equal(utf8_to_wide("ABC"), utf8_to_wide("abc"), true));
260 void TestUtilities::testStrToIntConversion()
262 UASSERT(mystoi("123", 0, 1000) == 123);
263 UASSERT(mystoi("123", 0, 10) == 10);
267 void TestUtilities::testStringReplace()
269 std::string test_str;
270 test_str = "Hello there";
271 str_replace(test_str, "there", "world");
272 UASSERT(test_str == "Hello world");
273 test_str = "ThisAisAaAtest";
274 str_replace(test_str, 'A', ' ');
275 UASSERT(test_str == "This is a test");
279 void TestUtilities::testStringAllowed()
281 UASSERT(string_allowed("hello", "abcdefghijklmno") == true);
282 UASSERT(string_allowed("123", "abcdefghijklmno") == false);
283 UASSERT(string_allowed_blacklist("hello", "123") == true);
284 UASSERT(string_allowed_blacklist("hello123", "123") == false);
287 void TestUtilities::testAsciiPrintableHelper()
289 UASSERT(IS_ASCII_PRINTABLE_CHAR('e') == true);
290 UASSERT(IS_ASCII_PRINTABLE_CHAR('\0') == false);
292 // Ensures that there is no cutting off going on...
293 // If there were, 331 would be cut to 75 in this example
294 // and 73 is a valid ASCII char.
296 UASSERT(IS_ASCII_PRINTABLE_CHAR(ch) == false);
299 void TestUtilities::testUTF8()
301 UASSERT(utf8_to_wide("¤") == L"¤");
303 UASSERT(wide_to_utf8(L"¤") == "¤");
305 UASSERTEQ(std::string, wide_to_utf8(utf8_to_wide("")), "");
306 UASSERTEQ(std::string, wide_to_utf8(utf8_to_wide("the shovel dug a crumbly node!")),
307 "the shovel dug a crumbly node!");
308 UASSERTEQ(std::string, wide_to_utf8(utf8_to_wide("-ä-")),
310 UASSERTEQ(std::string, wide_to_utf8(utf8_to_wide("-\xF0\xA0\x80\x8B-")),
311 "-\xF0\xA0\x80\x8B-");
315 void TestUtilities::testRemoveEscapes()
317 UASSERT(unescape_enriched<wchar_t>(
318 L"abc\x1bXdef") == L"abcdef");
319 UASSERT(unescape_enriched<wchar_t>(
320 L"abc\x1b(escaped)def") == L"abcdef");
321 UASSERT(unescape_enriched<wchar_t>(
322 L"abc\x1b((escaped with parenthesis\\))def") == L"abcdef");
323 UASSERT(unescape_enriched<wchar_t>(
324 L"abc\x1b(incomplete") == L"abc");
325 UASSERT(unescape_enriched<wchar_t>(
326 L"escape at the end\x1b") == L"escape at the end");
327 // Nested escapes not supported
328 UASSERT(unescape_enriched<wchar_t>(
329 L"abc\x1b(outer \x1b(inner escape)escape)def") == L"abcescape)def");
332 void TestUtilities::testWrapRows()
334 UASSERT(wrap_rows("12345678",4) == "1234\n5678");
335 // test that wrap_rows doesn't wrap inside multibyte sequences
337 const unsigned char s[] = {
338 0x2f, 0x68, 0x6f, 0x6d, 0x65, 0x2f, 0x72, 0x61, 0x70, 0x74, 0x6f,
339 0x72, 0x2f, 0xd1, 0x82, 0xd0, 0xb5, 0xd1, 0x81, 0xd1, 0x82, 0x2f,
340 0x6d, 0x69, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x62, 0x69,
341 0x6e, 0x2f, 0x2e, 0x2e, 0};
342 std::string str((char *)s);
343 UASSERT(utf8_to_wide(wrap_rows(str, 20)) != L"<invalid UTF-8 string>");
346 const unsigned char s[] = {
347 0x74, 0x65, 0x73, 0x74, 0x20, 0xd1, 0x82, 0xd0, 0xb5, 0xd1, 0x81,
348 0xd1, 0x82, 0x20, 0xd1, 0x82, 0xd0, 0xb5, 0xd1, 0x81, 0xd1, 0x82,
349 0x20, 0xd1, 0x82, 0xd0, 0xb5, 0xd1, 0x81, 0xd1, 0x82, 0};
350 std::string str((char *)s);
351 UASSERT(utf8_to_wide(wrap_rows(str, 8)) != L"<invalid UTF-8 string>");
355 void TestUtilities::testEnrichedString()
357 EnrichedString str(L"Test bar");
358 irr::video::SColor color(0xFF, 0, 0, 0xFF);
360 UASSERT(str.substr(1, 3).getString() == L"est");
362 UASSERT(str.substr(9, std::string::npos).getString() == L"BUZZ");
363 str.setDefaultColor(color); // Blue foreground
364 UASSERT(str.getColors()[5] == color);
365 // Green background, then white and yellow text
366 str = L"\x1b(b@#0F0)Regular \x1b(c@#FF0)yellow";
367 UASSERT(str.getColors()[2] == 0xFFFFFFFF);
368 str.setDefaultColor(color); // Blue foreground
369 UASSERT(str.getColors()[13] == 0xFFFFFF00); // Still yellow text
370 UASSERT(str.getBackground() == 0xFF00FF00); // Green background
373 void TestUtilities::testIsNumber()
375 UASSERT(is_number("123") == true);
376 UASSERT(is_number("") == false);
377 UASSERT(is_number("123a") == false);
381 void TestUtilities::testIsPowerOfTwo()
383 UASSERT(is_power_of_two(0) == false);
384 UASSERT(is_power_of_two(1) == true);
385 UASSERT(is_power_of_two(2) == true);
386 UASSERT(is_power_of_two(3) == false);
387 for (int exponent = 2; exponent <= 31; ++exponent) {
388 UASSERT(is_power_of_two((1U << exponent) - 1) == false);
389 UASSERT(is_power_of_two((1U << exponent)) == true);
390 UASSERT(is_power_of_two((1U << exponent) + 1) == false);
392 UASSERT(is_power_of_two(U32_MAX) == false);
395 void TestUtilities::testMyround()
397 UASSERT(myround(4.6f) == 5);
398 UASSERT(myround(1.2f) == 1);
399 UASSERT(myround(-3.1f) == -3);
400 UASSERT(myround(-6.5f) == -7);
403 void TestUtilities::testStringJoin()
405 std::vector<std::string> input;
406 UASSERT(str_join(input, ",") == "");
408 input.emplace_back("one");
409 UASSERT(str_join(input, ",") == "one");
411 input.emplace_back("two");
412 UASSERT(str_join(input, ",") == "one,two");
414 input.emplace_back("three");
415 UASSERT(str_join(input, ",") == "one,two,three");
418 UASSERT(str_join(input, ",") == "one,,three");
421 UASSERT(str_join(input, " and ") == "one and two and three");
425 static bool within(const f32 value1, const f32 value2, const f32 precision)
427 return std::fabs(value1 - value2) <= precision;
430 static bool within(const v3f &v1, const v3f &v2, const f32 precision)
432 return within(v1.X, v2.X, precision) && within(v1.Y, v2.Y, precision)
433 && within(v1.Z, v2.Z, precision);
436 static bool within(const core::matrix4 &m1, const core::matrix4 &m2,
439 const f32 *M1 = m1.pointer();
440 const f32 *M2 = m2.pointer();
441 for (int i = 0; i < 16; i++)
442 if (! within(M1[i], M2[i], precision))
447 static bool roundTripsDeg(const v3f &v, const f32 precision)
450 setPitchYawRoll(m, v);
451 return within(v, getPitchYawRoll(m), precision);
454 void TestUtilities::testEulerConversion()
456 // This test may fail on non-IEEE systems.
457 // Low tolerance is 4 ulp(1.0) for binary floats with 24 bit mantissa.
458 // (ulp = unit in the last place; ulp(1.0) = 2^-23).
459 const f32 tolL = 4.76837158203125e-7f;
460 // High tolerance is 2 ulp(180.0), needed for numbers in degrees.
461 // ulp(180.0) = 2^-16
462 const f32 tolH = 3.0517578125e-5f;
464 core::matrix4 m1, m2;
465 const f32 *M1 = m1.pointer();
466 const f32 *M2 = m2.pointer();
468 // Check that the radians version and the degrees version
469 // produce the same results. Check also that the conversion
470 // works both ways for these values.
471 v1 = v3f(M_PI/3.0, M_PI/5.0, M_PI/4.0);
472 v2 = v3f(60.0f, 36.0f, 45.0f);
473 setPitchYawRollRad(m1, v1);
474 setPitchYawRoll(m2, v2);
475 UASSERT(within(m1, m2, tolL));
476 UASSERT(within(getPitchYawRollRad(m1), v1, tolL));
477 UASSERT(within(getPitchYawRoll(m2), v2, tolH));
479 // Check the rotation matrix produced.
480 UASSERT(within(M1[0], 0.932004869f, tolL));
481 UASSERT(within(M1[1], 0.353553385f, tolL));
482 UASSERT(within(M1[2], 0.0797927827f, tolL));
483 UASSERT(within(M1[4], -0.21211791f, tolL));
484 UASSERT(within(M1[5], 0.353553355f, tolL));
485 UASSERT(within(M1[6], 0.911046684f, tolL));
486 UASSERT(within(M1[8], 0.293892622f, tolL));
487 UASSERT(within(M1[9], -0.866025448f, tolL));
488 UASSERT(within(M1[10], 0.404508471f, tolL));
490 // Check that the matrix is still homogeneous with no translation
491 UASSERT(M1[3] == 0.0f);
492 UASSERT(M1[7] == 0.0f);
493 UASSERT(M1[11] == 0.0f);
494 UASSERT(M1[12] == 0.0f);
495 UASSERT(M1[13] == 0.0f);
496 UASSERT(M1[14] == 0.0f);
497 UASSERT(M1[15] == 1.0f);
498 UASSERT(M2[3] == 0.0f);
499 UASSERT(M2[7] == 0.0f);
500 UASSERT(M2[11] == 0.0f);
501 UASSERT(M2[12] == 0.0f);
502 UASSERT(M2[13] == 0.0f);
503 UASSERT(M2[14] == 0.0f);
504 UASSERT(M2[15] == 1.0f);
506 // Compare to Irrlicht's results. To be comparable, the
507 // angles must come in a different order and the matrix
508 // elements to compare are different too.
509 m2.setRotationRadians(v3f(v1.Z, v1.X, v1.Y));
510 UASSERT(within(M1[0], M2[5], tolL));
511 UASSERT(within(M1[1], M2[6], tolL));
512 UASSERT(within(M1[2], M2[4], tolL));
514 UASSERT(within(M1[4], M2[9], tolL));
515 UASSERT(within(M1[5], M2[10], tolL));
516 UASSERT(within(M1[6], M2[8], tolL));
518 UASSERT(within(M1[8], M2[1], tolL));
519 UASSERT(within(M1[9], M2[2], tolL));
520 UASSERT(within(M1[10], M2[0], tolL));
522 // Check that Eulers that produce near gimbal-lock still round-trip
523 UASSERT(roundTripsDeg(v3f(89.9999f, 17.f, 0.f), tolH));
524 UASSERT(roundTripsDeg(v3f(89.9999f, 0.f, 19.f), tolH));
525 UASSERT(roundTripsDeg(v3f(89.9999f, 17.f, 19.f), tolH));
527 // Check that Eulers at an angle > 90 degrees may not round-trip...
528 v1 = v3f(90.00001f, 1.f, 1.f);
529 setPitchYawRoll(m1, v1);
530 v2 = getPitchYawRoll(m1);
531 //UASSERT(within(v1, v2, tolL)); // this is typically false
532 // ... however the rotation matrix is the same for both
533 setPitchYawRoll(m2, v2);
534 UASSERT(within(m1, m2, tolL));
537 void TestUtilities::testBase64()
539 // Test character set
540 UASSERT(base64_is_valid("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
541 "abcdefghijklmnopqrstuvwxyz"
542 "0123456789+/") == true);
543 UASSERT(base64_is_valid("/+9876543210"
544 "zyxwvutsrqponmlkjihgfedcba"
545 "ZYXWVUTSRQPONMLKJIHGFEDCBA") == true);
546 UASSERT(base64_is_valid("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
547 "abcdefghijklmnopqrstuvwxyz"
548 "0123456789+.") == false);
549 UASSERT(base64_is_valid("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
550 "abcdefghijklmnopqrstuvwxyz"
551 "0123456789 /") == false);
554 UASSERT(base64_is_valid("") == true);
556 // Test different lengths, with and without padding,
557 // with correct and incorrect padding
558 UASSERT(base64_is_valid("A") == false);
559 UASSERT(base64_is_valid("AA") == true);
560 UASSERT(base64_is_valid("AAA") == true);
561 UASSERT(base64_is_valid("AAAA") == true);
562 UASSERT(base64_is_valid("AAAAA") == false);
563 UASSERT(base64_is_valid("AAAAAA") == true);
564 UASSERT(base64_is_valid("AAAAAAA") == true);
565 UASSERT(base64_is_valid("AAAAAAAA") == true);
566 UASSERT(base64_is_valid("A===") == false);
567 UASSERT(base64_is_valid("AA==") == true);
568 UASSERT(base64_is_valid("AAA=") == true);
569 UASSERT(base64_is_valid("AAAA") == true);
570 UASSERT(base64_is_valid("AAAA====") == false);
571 UASSERT(base64_is_valid("AAAAA===") == false);
572 UASSERT(base64_is_valid("AAAAAA==") == true);
573 UASSERT(base64_is_valid("AAAAAAA=") == true);
574 UASSERT(base64_is_valid("AAAAAAA==") == false);
575 UASSERT(base64_is_valid("AAAAAAA===") == false);
576 UASSERT(base64_is_valid("AAAAAAA====") == false);
577 UASSERT(base64_is_valid("AAAAAAAA") == true);
578 UASSERT(base64_is_valid("AAAAAAAA=") == false);
579 UASSERT(base64_is_valid("AAAAAAAA==") == false);
580 UASSERT(base64_is_valid("AAAAAAAA===") == false);
581 UASSERT(base64_is_valid("AAAAAAAA====") == false);
583 // Test if canonical encoding
584 // Last character limitations, length % 4 == 3
585 UASSERT(base64_is_valid("AAB") == false);
586 UASSERT(base64_is_valid("AAE") == true);
587 UASSERT(base64_is_valid("AAQ") == true);
588 UASSERT(base64_is_valid("AAB=") == false);
589 UASSERT(base64_is_valid("AAE=") == true);
590 UASSERT(base64_is_valid("AAQ=") == true);
591 UASSERT(base64_is_valid("AAAAAAB=") == false);
592 UASSERT(base64_is_valid("AAAAAAE=") == true);
593 UASSERT(base64_is_valid("AAAAAAQ=") == true);
594 // Last character limitations, length % 4 == 2
595 UASSERT(base64_is_valid("AB") == false);
596 UASSERT(base64_is_valid("AE") == false);
597 UASSERT(base64_is_valid("AQ") == true);
598 UASSERT(base64_is_valid("AB==") == false);
599 UASSERT(base64_is_valid("AE==") == false);
600 UASSERT(base64_is_valid("AQ==") == true);
601 UASSERT(base64_is_valid("AAAAAB==") == false);
602 UASSERT(base64_is_valid("AAAAAE==") == false);
603 UASSERT(base64_is_valid("AAAAAQ==") == true);
605 // Extraneous character present
606 UASSERT(base64_is_valid(".") == false);
607 UASSERT(base64_is_valid("A.") == false);
608 UASSERT(base64_is_valid("AA.") == false);
609 UASSERT(base64_is_valid("AAA.") == false);
610 UASSERT(base64_is_valid("AAAA.") == false);
611 UASSERT(base64_is_valid("AAAAA.") == false);
612 UASSERT(base64_is_valid("A.A") == false);
613 UASSERT(base64_is_valid("AA.A") == false);
614 UASSERT(base64_is_valid("AAA.A") == false);
615 UASSERT(base64_is_valid("AAAA.A") == false);
616 UASSERT(base64_is_valid("AAAAA.A") == false);
617 UASSERT(base64_is_valid("\xE1""AAA") == false);
619 // Padding in wrong position
620 UASSERT(base64_is_valid("A=A") == false);
621 UASSERT(base64_is_valid("AA=A") == false);
622 UASSERT(base64_is_valid("AAA=A") == false);
623 UASSERT(base64_is_valid("AAAA=A") == false);
624 UASSERT(base64_is_valid("AAAAA=A") == false);
628 void TestUtilities::testSanitizeDirName()
630 UASSERT(sanitizeDirName("a", "~") == "a");
631 UASSERT(sanitizeDirName(" ", "~") == "__");
632 UASSERT(sanitizeDirName(" a ", "~") == "_a_");
633 UASSERT(sanitizeDirName("COM1", "~") == "~COM1");
634 UASSERT(sanitizeDirName("COM1", ":") == "_COM1");
635 UASSERT(sanitizeDirName("cOm\u00B2", "~") == "~cOm\u00B2");
636 UASSERT(sanitizeDirName("cOnIn$", "~") == "~cOnIn$");
637 UASSERT(sanitizeDirName(" cOnIn$ ", "~") == "_cOnIn$_");