]> git.lizzy.rs Git - minetest.git/blob - src/unittest/test_utilities.cpp
Disable Prometheus in singleplayer mode
[minetest.git] / src / unittest / test_utilities.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
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.
9
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.
14
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.
18 */
19
20 #include "test.h"
21
22 #include <cmath>
23 #include "util/enriched_string.h"
24 #include "util/numeric.h"
25 #include "util/string.h"
26 #include "util/base64.h"
27
28 class TestUtilities : public TestBase {
29 public:
30         TestUtilities() { TestManager::registerTestModule(this); }
31         const char *getName() { return "TestUtilities"; }
32
33         void runTests(IGameDef *gamedef);
34
35         void testAngleWrapAround();
36         void testWrapDegrees_0_360_v3f();
37         void testLowercase();
38         void testTrim();
39         void testIsYes();
40         void testRemoveStringEnd();
41         void testUrlEncode();
42         void testUrlDecode();
43         void testPadString();
44         void testStartsWith();
45         void testStrEqual();
46         void testStrToIntConversion();
47         void testStringReplace();
48         void testStringAllowed();
49         void testAsciiPrintableHelper();
50         void testUTF8();
51         void testRemoveEscapes();
52         void testWrapRows();
53         void testEnrichedString();
54         void testIsNumber();
55         void testIsPowerOfTwo();
56         void testMyround();
57         void testStringJoin();
58         void testEulerConversion();
59         void testBase64();
60         void testSanitizeDirName();
61 };
62
63 static TestUtilities g_test_instance;
64
65 void TestUtilities::runTests(IGameDef *gamedef)
66 {
67         TEST(testAngleWrapAround);
68         TEST(testWrapDegrees_0_360_v3f);
69         TEST(testLowercase);
70         TEST(testTrim);
71         TEST(testIsYes);
72         TEST(testRemoveStringEnd);
73         TEST(testUrlEncode);
74         TEST(testUrlDecode);
75         TEST(testPadString);
76         TEST(testStartsWith);
77         TEST(testStrEqual);
78         TEST(testStrToIntConversion);
79         TEST(testStringReplace);
80         TEST(testStringAllowed);
81         TEST(testAsciiPrintableHelper);
82         TEST(testUTF8);
83         TEST(testRemoveEscapes);
84         TEST(testWrapRows);
85         TEST(testEnrichedString);
86         TEST(testIsNumber);
87         TEST(testIsPowerOfTwo);
88         TEST(testMyround);
89         TEST(testStringJoin);
90         TEST(testEulerConversion);
91         TEST(testBase64);
92         TEST(testSanitizeDirName);
93 }
94
95 ////////////////////////////////////////////////////////////////////////////////
96
97 inline float ref_WrapDegrees180(float f)
98 {
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);
102         if (value < 0)
103                 value += 360;
104         return value - 180;
105 }
106
107
108 inline float ref_WrapDegrees_0_360(float f)
109 {
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);
113         if (value < 0)
114                 value += 360;
115         return value < 0 ? value + 360 : value;
116 }
117
118
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);
124
125     for (float f = -720; f <= -360; f += 0.25) {
126         UASSERT(std::fabs(modulo360f(f) - modulo360f(f + 360)) < 0.001);
127     }
128
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);
135     }
136
137 }
138
139 void TestUtilities::testWrapDegrees_0_360_v3f()
140 {
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)
145         UASSERT(r.Y == 0.0f)
146         UASSERT(r.Z == 0.0f)
147     }
148
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));
152         UASSERT(r.X == 0.0f)
153         UASSERT(r.Y >= 0.0f && r.Y < 360.0f)
154         UASSERT(r.Z == 0.0f)
155     }
156
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));
160         UASSERT(r.X == 0.0f)
161         UASSERT(r.Y == 0.0f)
162         UASSERT(r.Z >= 0.0f && r.Z < 360.0f)
163         }
164
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)
173             }
174         }
175     }
176 }
177
178
179 void TestUtilities::testLowercase()
180 {
181         UASSERT(lowercase("Foo bAR") == "foo bar");
182         UASSERT(lowercase("eeeeeeaaaaaaaaaaaààààà") == "eeeeeeaaaaaaaaaaaààààà");
183         UASSERT(lowercase("MINETEST-powa") == "minetest-powa");
184 }
185
186
187 void TestUtilities::testTrim()
188 {
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");
195 }
196
197
198 void TestUtilities::testIsYes()
199 {
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);
207 }
208
209
210 void TestUtilities::testRemoveStringEnd()
211 {
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) == "");
217 }
218
219
220 void TestUtilities::testUrlEncode()
221 {
222         UASSERT(urlencode("\"Aardvarks lurk, OK?\"")
223                         == "%22Aardvarks%20lurk%2C%20OK%3F%22");
224 }
225
226
227 void TestUtilities::testUrlDecode()
228 {
229         UASSERT(urldecode("%22Aardvarks%20lurk%2C%20OK%3F%22")
230                         == "\"Aardvarks lurk, OK?\"");
231 }
232
233
234 void TestUtilities::testPadString()
235 {
236         UASSERT(padStringRight("hello", 8) == "hello   ");
237 }
238
239 void TestUtilities::testStartsWith()
240 {
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);
251 }
252
253 void TestUtilities::testStrEqual()
254 {
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));
257 }
258
259
260 void TestUtilities::testStrToIntConversion()
261 {
262         UASSERT(mystoi("123", 0, 1000) == 123);
263         UASSERT(mystoi("123", 0, 10) == 10);
264 }
265
266
267 void TestUtilities::testStringReplace()
268 {
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");
276 }
277
278
279 void TestUtilities::testStringAllowed()
280 {
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);
285 }
286
287 void TestUtilities::testAsciiPrintableHelper()
288 {
289         UASSERT(IS_ASCII_PRINTABLE_CHAR('e') == true);
290         UASSERT(IS_ASCII_PRINTABLE_CHAR('\0') == false);
291
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.
295         int ch = 331;
296         UASSERT(IS_ASCII_PRINTABLE_CHAR(ch) == false);
297 }
298
299 void TestUtilities::testUTF8()
300 {
301         UASSERT(utf8_to_wide("¤") == L"¤");
302
303         UASSERT(wide_to_utf8(L"¤") == "¤");
304
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("-ä-")),
309                 "-ä-");
310         UASSERTEQ(std::string, wide_to_utf8(utf8_to_wide("-\xF0\xA0\x80\x8B-")),
311                 "-\xF0\xA0\x80\x8B-");
312
313 }
314
315 void TestUtilities::testRemoveEscapes()
316 {
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");
330 }
331
332 void TestUtilities::testWrapRows()
333 {
334         UASSERT(wrap_rows("12345678",4) == "1234\n5678");
335         // test that wrap_rows doesn't wrap inside multibyte sequences
336         {
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>");
344         };
345         {
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>");
352         }
353 }
354
355 void TestUtilities::testEnrichedString()
356 {
357         EnrichedString str(L"Test bar");
358         irr::video::SColor color(0xFF, 0, 0, 0xFF);
359
360         UASSERT(str.substr(1, 3).getString() == L"est");
361         str += L" BUZZ";
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
371 }
372
373 void TestUtilities::testIsNumber()
374 {
375         UASSERT(is_number("123") == true);
376         UASSERT(is_number("") == false);
377         UASSERT(is_number("123a") == false);
378 }
379
380
381 void TestUtilities::testIsPowerOfTwo()
382 {
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);
391         }
392         UASSERT(is_power_of_two(U32_MAX) == false);
393 }
394
395 void TestUtilities::testMyround()
396 {
397         UASSERT(myround(4.6f) == 5);
398         UASSERT(myround(1.2f) == 1);
399         UASSERT(myround(-3.1f) == -3);
400         UASSERT(myround(-6.5f) == -7);
401 }
402
403 void TestUtilities::testStringJoin()
404 {
405         std::vector<std::string> input;
406         UASSERT(str_join(input, ",") == "");
407
408         input.emplace_back("one");
409         UASSERT(str_join(input, ",") == "one");
410
411         input.emplace_back("two");
412         UASSERT(str_join(input, ",") == "one,two");
413
414         input.emplace_back("three");
415         UASSERT(str_join(input, ",") == "one,two,three");
416
417         input[1] = "";
418         UASSERT(str_join(input, ",") == "one,,three");
419
420         input[1] = "two";
421         UASSERT(str_join(input, " and ") == "one and two and three");
422 }
423
424
425 static bool within(const f32 value1, const f32 value2, const f32 precision)
426 {
427         return std::fabs(value1 - value2) <= precision;
428 }
429
430 static bool within(const v3f &v1, const v3f &v2, const f32 precision)
431 {
432         return within(v1.X, v2.X, precision) && within(v1.Y, v2.Y, precision)
433                 && within(v1.Z, v2.Z, precision);
434 }
435
436 static bool within(const core::matrix4 &m1, const core::matrix4 &m2,
437                 const f32 precision)
438 {
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))
443                         return false;
444         return true;
445 }
446
447 static bool roundTripsDeg(const v3f &v, const f32 precision)
448 {
449         core::matrix4 m;
450         setPitchYawRoll(m, v);
451         return within(v, getPitchYawRoll(m), precision);
452 }
453
454 void TestUtilities::testEulerConversion()
455 {
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;
463         v3f v1, v2;
464         core::matrix4 m1, m2;
465         const f32 *M1 = m1.pointer();
466         const f32 *M2 = m2.pointer();
467
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));
478
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));
489
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);
505
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));
513
514         UASSERT(within(M1[4], M2[9], tolL));
515         UASSERT(within(M1[5], M2[10], tolL));
516         UASSERT(within(M1[6], M2[8], tolL));
517
518         UASSERT(within(M1[8], M2[1], tolL));
519         UASSERT(within(M1[9], M2[2], tolL));
520         UASSERT(within(M1[10], M2[0], tolL));
521
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));
526
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));
535 }
536
537 void TestUtilities::testBase64()
538 {
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);
552
553         // Test empty string
554         UASSERT(base64_is_valid("") == true);
555
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);
582
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);
604
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);
618
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);
625 }
626
627
628 void TestUtilities::testSanitizeDirName()
629 {
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$_");
638 }