1 /*******************************************************************************
2  * tests/string_test.cpp
3  *
4  * Part of tlx - http://panthema.net/tlx
5  *
6  * Copyright (C) 2007-2017 Timo Bingmann <tb@panthema.net>
7  *
8  * All rights reserved. Published under the Boost Software License, Version 1.0
9  ******************************************************************************/
10 
11 #include <random>
12 #include <stdexcept>
13 
14 #include <tlx/die.hpp>
15 #include <tlx/port/setenv.hpp>
16 #include <tlx/string.hpp>
17 #include <tlx/string/appendline.hpp>
18 #include <tlx/string/ssprintf_generic.hpp>
19 
20 //! Returns an initialized unsigned char[] array inside an std::string
21 #define ARRAY_AS_STRING(array) \
22     std::string(reinterpret_cast<const char*>(array), sizeof(array))
23 
24 /*!
25  * Generate a random binary string of given length. Any byte from 0-256 is
26  * equally probable. Uses the pseudo-random number generator from stdlib; take
27  * care to seed it using srand() before calling this function.
28  *
29  * \param size  length of result
30  * \return      random binary string of given length
31  */
32 static inline
random_binary(std::string::size_type size)33 std::string random_binary(std::string::size_type size) {
34     static std::random_device random_device;
35     std::minstd_rand prng(random_device());
36 
37     std::string out;
38     out.resize(size);
39 
40     for (size_t i = 0; i < size; ++i)
41         out[i] = static_cast<unsigned char>(prng() % 256);
42 
43     return out;
44 }
45 
test_appendline()46 static void test_appendline() {
47     std::string input =
48         "abc\n" "def\n" "ghi\n" "jk";
49 
50     std::stringstream ss1(input);
51     std::string line;
52 
53     die_unless(tlx::appendline(ss1, line));
54     die_unequal(line, "abc");
55     die_unless(tlx::appendline(ss1, line));
56     die_unequal(line, "abcdef");
57     die_unless(tlx::appendline(ss1, line));
58     die_unequal(line, "abcdefghi");
59     die_unless(tlx::appendline(ss1, line));
60     die_unequal(line, "abcdefghijk");
61     die_if(tlx::appendline(ss1, line));
62 
63     // add one last newline
64     input += "\n";
65     std::stringstream ss2(input);
66 
67     line.clear();
68     die_unless(tlx::appendline(ss2, line));
69     die_unequal(line, "abc");
70     die_unless(tlx::appendline(ss2, line));
71     die_unequal(line, "abcdef");
72     die_unless(tlx::appendline(ss2, line));
73     die_unequal(line, "abcdefghi");
74     die_unless(tlx::appendline(ss2, line));
75     die_unequal(line, "abcdefghijk");
76     die_if(tlx::appendline(ss2, line));
77 }
78 
test_base64()79 static void test_base64() {
80     // take some static hex data and dump it using base64 encoding, then decode
81     // it again.
82     const unsigned char rand1data[42] = {
83         0x16, 0x35, 0xCA, 0x03, 0x90, 0x6B, 0x47, 0x11,
84         0x85, 0x02, 0xE7, 0x40, 0x9E, 0x3A, 0xCE, 0x43,
85         0x0C, 0x57, 0x3E, 0x35, 0xE7, 0xA6, 0xB2, 0x37,
86         0xEC, 0x6D, 0xF6, 0x68, 0xF6, 0x0E, 0x74, 0x0C,
87         0x44, 0x3F, 0x0F, 0xD4, 0xAA, 0x56, 0xE5, 0x2F,
88         0x58, 0xCC
89     };
90 
91     std::string rand1 = ARRAY_AS_STRING(rand1data);
92 
93     std::string rand1base64 = tlx::base64_encode(rand1);
94 
95     die_unequal(rand1base64,
96                 "FjXKA5BrRxGFAudAnjrOQwxXPjXnprI37G32aPYOdAxEPw/UqlblL1jM");
97 
98     die_unequal(tlx::base64_decode(rand1base64), rand1);
99 
100     // check line-splitting
101     std::string rand1base64lines = tlx::base64_encode(rand1, 16);
102 
103     die_unequal(rand1base64lines,
104                 "FjXKA5BrRxGFAudA\n" "njrOQwxXPjXnprI3\n"
105                 "7G32aPYOdAxEPw/U\n" "qlblL1jM");
106 
107     // take three random binary data string with different sizes and run
108     // the base64 encoding->decoding->checking drill.
109 
110     std::string rand12 = random_binary(12);
111     die_unequal(tlx::base64_decode(tlx::base64_encode(rand12)), rand12);
112 
113     std::string rand13 = random_binary(13);
114     die_unequal(tlx::base64_decode(tlx::base64_encode(rand13)), rand13);
115 
116     std::string rand14 = random_binary(14);
117     die_unequal(tlx::base64_decode(tlx::base64_encode(rand14)), rand14);
118 
119     // run a larger set of random tests
120     for (unsigned int ti = 0; ti < 1000; ++ti)
121     {
122         unsigned int randlen = ti; // rand() % 1000;
123         std::string randstr = random_binary(randlen);
124 
125         die_unequal(
126             tlx::base64_decode(tlx::base64_encode(randstr)), randstr);
127     }
128 
129     die_unless_throws(
130         tlx::base64_decode("FjXKA5!!RxGFAudA"), std::runtime_error);
131 }
132 
test_bitdump()133 static void test_bitdump() {
134     die_unequal(tlx::bitdump_le8("0123"),
135                 "00110000 00110001 00110010 00110011");
136     die_unequal(tlx::bitdump_be8("0123"),
137                 "00001100 10001100 01001100 11001100");
138 
139     die_unequal(tlx::bitdump_le8_type(uint16_t(0x1234)),
140                 "00110100 00010010");
141     die_unequal(tlx::bitdump_be8_type(uint16_t(0x1234)),
142                 "00101100 01001000");
143 }
144 
test_compare_icase()145 static void test_compare_icase() {
146     die_unless(std::string("ABC") != std::string("abc"));
147 
148     die_unless(tlx::equal_icase("ABC", "abc"));
149     die_unless(!tlx::equal_icase("ABC", "abd"));
150     die_unless(!tlx::equal_icase("ABC", "abcedf"));
151 
152     die_unless(std::string("ABC") < std::string("abc"));
153     die_unless(!tlx::less_icase("ABC", "abc"));
154     die_unless(tlx::less_icase("abc", "abcdef"));
155     die_unless(!tlx::less_icase("abcdef", "abcd"));
156 
157     die_unless(tlx::compare_icase("ABC", "abc") == 0);
158     die_unless(tlx::compare_icase("ABC", "abd") < 0);
159     die_unless(tlx::compare_icase("ABC", "abb") > 0);
160 }
161 
test_contains_word()162 static void test_contains_word() {
163     std::string data = "test admin write readall read do";
164 
165     die_unless(tlx::contains_word(data, "test"));
166     die_unless(!tlx::contains_word(data, "testit"));
167 
168     die_unless(tlx::contains_word(data, "read"));
169     die_unless(tlx::contains_word(data, "readall"));
170 
171     die_unless(tlx::contains_word(data, std::string("read")));
172     die_unless(tlx::contains_word(data, std::string("readall")));
173 
174     die_unless(!tlx::contains_word(data, "doit"));
175 }
176 
test_erase_all()177 static void test_erase_all() {
178 
179     die_unequal(
180         tlx::erase_all(" abcdef   ghi jk "), "abcdefghijk");
181 
182     die_unequal(
183         tlx::erase_all("abcdef   ghi jk"), "abcdefghijk");
184 
185     die_unequal(
186         tlx::erase_all(" abcdef   ghi jk ", " bg"), "acdefhijk");
187 
188     die_unequal(
189         tlx::erase_all("abcdef   ghi jk", " bg"), "acdefhijk");
190 
191     std::string s1 = " abcdef   ghi jk ";
192     die_unequal(tlx::erase_all(&s1), "abcdefghijk");
193 
194     std::string s2 = "abcdef   ghi jk";
195     die_unequal(tlx::erase_all(&s2), "abcdefghijk");
196 
197     std::string s3 = " abcdef   ghi jk ";
198     die_unequal(tlx::erase_all(&s3, " bg"), "acdefhijk");
199 
200     std::string s4 = "abcdef   ghi jk";
201     die_unequal(tlx::erase_all(&s4, " bg"), "acdefhijk");
202 }
203 
test_escape_html()204 static void test_escape_html() {
205 
206     die_unequal(
207         tlx::escape_html("hello <tag> \"abc\" & \"def\""),
208         "hello &lt;tag&gt; &quot;abc&quot; &amp; &quot;def&quot;");
209 }
210 
test_escape_uri()211 static void test_escape_uri() {
212 
213     die_unequal(
214         tlx::escape_uri("hello <tag>\""), "hello%20%3Ctag%3E%22");
215 }
216 
test_expand_environment_variables()217 static void test_expand_environment_variables() {
218 
219     tlx::setenv("TEST_1", "def", /* overwrite */ true);
220     tlx::setenv("VAR_2", "uvw", /* overwrite */ true);
221 
222     die_unequal(
223         tlx::expand_environment_variables("abc$TEST_1 ---${VAR_2}xyz"),
224         "abcdef ---uvwxyz");
225 
226     die_unequal(
227         tlx::expand_environment_variables("abc$4TEST_1 -$$--${VAR_2}xyz"),
228         "abc$4TEST_1 -$$--uvwxyz");
229 
230     die_unequal(
231         tlx::expand_environment_variables("abc${NON_EXISTING_VARIABLE}xyz"),
232         "abcxyz");
233 }
234 
test_extract_between()235 static void test_extract_between() {
236     std::string data =
237         "Content-Disposition: form-data; name='testfile'; filename='test.html'";
238 
239     die_unequal(tlx::extract_between(data, "name='", "'"), "testfile");
240     die_unequal(tlx::extract_between(data, "filename='", "'"), "test.html");
241     die_unequal(tlx::extract_between(data, "other='", "'"), "");
242 
243     die_unequal(tlx::extract_between(data, "Name='", "'"), "");
244 }
245 
test_format_si_iec_units()246 static void test_format_si_iec_units() {
247 
248     die_unequal(tlx::format_si_units(33 * 1024 * 1024 * 1024LLU), "35.433 G");
249     die_unequal(tlx::format_iec_units(33 * 1024 * 1024 * 1024LLU), "33.000 Gi");
250 }
251 
test_hash_djb2()252 static void test_hash_djb2() {
253     die_unequal(
254         tlx::hash_djb2("hello hash me"), 0x2DA4090Fu);
255     die_unequal(
256         tlx::hash_djb2(std::string("hello hash me")), 0x2DA4090Fu);
257 }
258 
test_hash_sdbm()259 static void test_hash_sdbm() {
260     die_unequal(
261         tlx::hash_sdbm("hello hash me"), 0x290130BCu);
262     die_unequal(
263         tlx::hash_sdbm(std::string("hello hash me")), 0x290130BCu);
264 }
265 
test_hexdump()266 static void test_hexdump() {
267 
268     // take hex data and dump it into a string, then parse back into array
269     const unsigned char hexdump[8] = {
270         0x8D, 0xE2, 0x85, 0xD4, 0xBF, 0x98, 0xE6, 0x03
271     };
272 
273     std::string hexdata = ARRAY_AS_STRING(hexdump);
274     std::string hexstring = tlx::hexdump(hexdata);
275 
276     die_unequal(hexstring, "8DE285D4BF98E603");
277     die_unequal(tlx::hexdump(hexdump, sizeof(hexdump)), "8DE285D4BF98E603");
278 
279     std::string hexparsed = tlx::parse_hexdump(hexstring);
280     die_unequal(hexparsed, hexdata);
281 
282     // dump random binary string into hex and parse it back
283     std::string rand1 = random_binary(42);
284     die_unequal(tlx::parse_hexdump(tlx::hexdump(rand1)), rand1);
285 
286     // take the first hex list and dump it into source code format, then
287     // compare it with correct data (which was also dumped with
288     // hexdump_sourcecode())
289     std::string hexsource = tlx::hexdump_sourcecode(hexdata, "abc");
290 
291     const unsigned char hexsourcecmp[68] = {
292         0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x75, 0x69,
293         0x6E, 0x74, 0x38, 0x5F, 0x74, 0x20, 0x61, 0x62,
294         0x63, 0x5B, 0x38, 0x5D, 0x20, 0x3D, 0x20, 0x7B,
295         0x0A, 0x30, 0x78, 0x38, 0x44, 0x2C, 0x30, 0x78,
296         0x45, 0x32, 0x2C, 0x30, 0x78, 0x38, 0x35, 0x2C,
297         0x30, 0x78, 0x44, 0x34, 0x2C, 0x30, 0x78, 0x42,
298         0x46, 0x2C, 0x30, 0x78, 0x39, 0x38, 0x2C, 0x30,
299         0x78, 0x45, 0x36, 0x2C, 0x30, 0x78, 0x30, 0x33,
300         0x0A, 0x7D, 0x3B, 0x0A
301     };
302 
303     die_unequal(hexsource, ARRAY_AS_STRING(hexsourcecmp));
304 
305     // test parse_hexdump with illegal strings
306     die_unless_throws(tlx::parse_hexdump("illegal"), std::runtime_error);
307     die_unless_throws(tlx::parse_hexdump("8DE285D4BF98E60"), std::runtime_error);
308 }
309 
test_join()310 static void test_join() {
311     // simple string split and join
312     std::vector<std::string> sv = tlx::split('/', "/usr/bin/test");
313     die_unequal(sv.size(), 4u);
314 
315     die_unequal(tlx::join("--", sv), "--usr--bin--test");
316     die_unequal(tlx::join(";", sv), ";usr;bin;test");
317 
318     std::vector<std::string> sv2;
319     for (unsigned int i = 0; i < 6; ++i)
320         sv2.emplace_back("abc");
321 
322     die_unequal(tlx::join(".", sv2), "abc.abc.abc.abc.abc.abc");
323 }
324 
test_levenshtein()325 static void test_levenshtein() {
326     die_unequal(tlx::levenshtein("Demonstration", "Comparison"), 9u);
327     die_unequal(tlx::levenshtein("Levenshtein", "Distance"), 10u);
328     die_unequal(tlx::levenshtein("Distance", "Distance"), 0u);
329     die_unequal(tlx::levenshtein("Distance", "LVDistance"), 2u);
330 
331     die_unequal(tlx::levenshtein_icase("distance", "DISTANCE"), 0u);
332     die_unequal(tlx::levenshtein_icase("Levenshtein", "Distance"), 10u);
333 
334     die_unequal(tlx::levenshtein_icase("Test this distance", "to this one"), 9u);
335 }
336 
test_parse_si_iec_units()337 static void test_parse_si_iec_units() {
338 
339     uint64_t size;
340     die_unless(tlx::parse_si_iec_units(" 33 GiB ", &size));
341     die_unequal(33 * 1024 * 1024 * 1024LLU, size);
342 
343     die_if(tlx::parse_si_iec_units(" 33 GiBX ", &size));
344 }
345 
test_split()346 static void test_split() {
347     // simple char split
348     std::vector<std::string> sv = tlx::split('/', "/usr/bin/test/");
349 
350     die_unequal(sv.size(), 5u);
351     die_unequal(sv[0], "");
352     die_unequal(sv[1], "usr");
353     die_unequal(sv[2], "bin");
354     die_unequal(sv[3], "test");
355     die_unequal(sv[4], "");
356 
357     sv = tlx::split('/', "/usr/bin/test", 3);
358 
359     die_unequal(sv.size(), 3u);
360     die_unequal(sv[0], "");
361     die_unequal(sv[1], "usr");
362     die_unequal(sv[2], "bin/test");
363 
364     // char split with some strange limits
365     sv = tlx::split('/', "/usr//bin/test", 0);
366     die_unequal(sv.size(), 0u);
367 
368     sv = tlx::split('/', "/usr//bin/test", 1);
369     die_unequal(sv.size(), 1u);
370     die_unequal(sv[0], "/usr//bin/test");
371 
372     // simple str split
373     sv = tlx::split("/", "/usr/bin/test");
374 
375     die_unequal(sv.size(), 4u);
376     die_unequal(sv[0], "");
377     die_unequal(sv[1], "usr");
378     die_unequal(sv[2], "bin");
379     die_unequal(sv[3], "test");
380 
381     sv = tlx::split("/", "/usr/bin/test", 3);
382 
383     die_unequal(sv.size(), 3u);
384     die_unequal(sv[0], "");
385     die_unequal(sv[1], "usr");
386     die_unequal(sv[2], "bin/test");
387 
388     // str split with some strange limits
389     sv = tlx::split("/", "/usr//bin/test", 0);
390     die_unequal(sv.size(), 0u);
391 
392     sv = tlx::split("/", "/usr//bin/test", 1);
393     die_unequal(sv.size(), 1u);
394     die_unequal(sv[0], "/usr//bin/test");
395 
396     // str split with parital needle at end
397     sv = tlx::split("abc", "testabcblahabcabcab");
398     die_unequal(sv.size(), 4u);
399     die_unequal(sv[0], "test");
400     die_unequal(sv[1], "blah");
401     die_unequal(sv[2], "");
402     die_unequal(sv[3], "ab");
403 
404     // str split with "" separator
405     sv = tlx::split("", "abcdef");
406     die_unequal(sv.size(), 6u);
407     die_unequal(sv[0], "a");
408     die_unequal(sv[1], "b");
409     die_unequal(sv[2], "c");
410     die_unequal(sv[3], "d");
411     die_unequal(sv[4], "e");
412     die_unequal(sv[5], "f");
413 
414     /**************************************************************************/
415 
416     // str split with min-limit
417     sv = tlx::split('/', "/usr/bin/test", 2, 2);
418     die_unequal(sv.size(), 2u);
419     die_unequal(sv[0], "");
420     die_unequal(sv[1], "usr/bin/test");
421 
422     // str split with min-limit
423     sv = tlx::split('/', "/usr/bin/test", 5, 5);
424     die_unequal(sv.size(), 5u);
425     die_unequal(sv[0], "");
426     die_unequal(sv[1], "usr");
427     die_unequal(sv[2], "bin");
428     die_unequal(sv[3], "test");
429     die_unequal(sv[4], "");
430 
431     // str split with min-limit
432     sv = tlx::split("/", "/usr/bin/test", 5, 5);
433     die_unequal(sv.size(), 5u);
434     die_unequal(sv[0], "");
435     die_unequal(sv[1], "usr");
436     die_unequal(sv[2], "bin");
437     die_unequal(sv[3], "test");
438     die_unequal(sv[4], "");
439 }
440 
test_split_join_quoted()441 static void test_split_join_quoted() {
442     // simple whitespace split
443     std::vector<std::string> sv = tlx::split_quoted("  ab c df  fdlk f  ");
444 
445     die_unequal(sv.size(), 5u);
446     die_unequal(sv[0], "ab");
447     die_unequal(sv[1], "c");
448     die_unequal(sv[2], "df");
449     die_unequal(sv[3], "fdlk");
450     die_unequal(sv[4], "f");
451 
452     die_unequal(tlx::join_quoted(sv), "ab c df fdlk f");
453 
454     // simple whitespace split
455 
456     sv = tlx::split_quoted("ab c df  fdlk f  ");
457 
458     die_unequal(sv.size(), 5u);
459     die_unequal(sv[0], "ab");
460     die_unequal(sv[1], "c");
461     die_unequal(sv[2], "df");
462     die_unequal(sv[3], "fdlk");
463     die_unequal(sv[4], "f");
464 
465     die_unequal(tlx::join_quoted(sv), "ab c df fdlk f");
466 
467     // simple whitespace split
468 
469     sv = tlx::split_quoted("ab c df  fdlk f");
470 
471     die_unequal(sv.size(), 5u);
472     die_unequal(sv[0], "ab");
473     die_unequal(sv[1], "c");
474     die_unequal(sv[2], "df");
475     die_unequal(sv[3], "fdlk");
476     die_unequal(sv[4], "f");
477 
478     die_unequal(tlx::join_quoted(sv), "ab c df fdlk f");
479 
480     // with quoted entry
481     sv = tlx::split_quoted("ab c \"df  fdlk \" f  ");
482 
483     die_unequal(sv.size(), 4u);
484     die_unequal(sv[0], "ab");
485     die_unequal(sv[1], "c");
486     die_unequal(sv[2], "df  fdlk ");
487     die_unequal(sv[3], "f");
488 
489     die_unequal(tlx::join_quoted(sv), "ab c \"df  fdlk \" f");
490 
491     // with quoted entry containing quote
492     sv = tlx::split_quoted("ab c \"d\\\\f\\n  \\\"fdlk \" f  ");
493 
494     die_unequal(sv.size(), 4u);
495     die_unequal(sv[0], "ab");
496     die_unequal(sv[1], "c");
497     die_unequal(sv[2], "d\\f\n  \"fdlk ");
498     die_unequal(sv[3], "f");
499 
500     die_unequal(tlx::join_quoted(sv), "ab c \"d\\\\f\\n  \\\"fdlk \" f");
501 }
502 
test_split_words()503 static void test_split_words() {
504     // simple whitespace split
505     std::vector<std::string> sv = tlx::split_words("  ab c df  fdlk f  ");
506 
507     die_unequal(sv.size(), 5u);
508     die_unequal(sv[0], "ab");
509     die_unequal(sv[1], "c");
510     die_unequal(sv[2], "df");
511     die_unequal(sv[3], "fdlk");
512     die_unequal(sv[4], "f");
513 
514     sv = tlx::split_words("ab c df  fdlk f  ");
515 
516     die_unequal(sv.size(), 5u);
517     die_unequal(sv[0], "ab");
518     die_unequal(sv[1], "c");
519     die_unequal(sv[2], "df");
520     die_unequal(sv[3], "fdlk");
521     die_unequal(sv[4], "f");
522 
523     sv = tlx::split_words("ab c df  fdlk f");
524 
525     die_unequal(sv.size(), 5u);
526     die_unequal(sv[0], "ab");
527     die_unequal(sv[1], "c");
528     die_unequal(sv[2], "df");
529     die_unequal(sv[3], "fdlk");
530     die_unequal(sv[4], "f");
531 
532     sv = tlx::split_words("");
533     die_unequal(sv.size(), 0u);
534 
535     sv = tlx::split_words("    ");
536     die_unequal(sv.size(), 0u);
537 
538     // whitespace split with limit
539     sv = tlx::split_words("  ab c   df  fdlk f  ", 3);
540 
541     die_unequal(sv.size(), 3u);
542     die_unequal(sv[0], "ab");
543     die_unequal(sv[1], "c");
544     die_unequal(sv[2], "df  fdlk f  ");
545 
546     // whitespace split with some strange limits
547     sv = tlx::split_words("  ab c df  fdlk f  ", 0);
548     die_unequal(sv.size(), 0u);
549 
550     sv = tlx::split_words("  ab c df  fdlk f  ", 1);
551 
552     die_unequal(sv.size(), 1u);
553     die_unequal(sv[0], "ab c df  fdlk f  ");
554 
555     // whitespace split with large limit
556     sv = tlx::split_words("  ab  c  df  fdlk f  ", 10);
557 
558     die_unequal(sv.size(), 5u);
559     die_unequal(sv[0], "ab");
560     die_unequal(sv[1], "c");
561     die_unequal(sv[2], "df");
562     die_unequal(sv[3], "fdlk");
563     die_unequal(sv[4], "f");
564 
565     // whitespace split with limit at exactly the end
566     sv = tlx::split_words("  ab  c  df  fdlk f  ", 5);
567 
568     die_unequal(sv.size(), 5u);
569     die_unequal(sv[0], "ab");
570     die_unequal(sv[1], "c");
571     die_unequal(sv[2], "df");
572     die_unequal(sv[3], "fdlk");
573     die_unequal(sv[4], "f  ");
574 }
575 
test_ssprintf()576 static void test_ssprintf() {
577     die_unequal(
578         tlx::ssprintf("abc %d %s test", 42, "hello"),
579         "abc 42 hello test");
580     die_unequal(
581         tlx::ssnprintf(5, "abc %d %s test", 42, "hello"),
582         "abc 4");
583     die_unequal(
584         tlx::ssnprintf(5, "%d", 42), "42");
585 
586     // "generic" version
587     die_unequal(
588         tlx::ssprintf_generic<std::string>("abc %d %s test", 42, "hello"),
589         "abc 42 hello test");
590     die_unequal(
591         tlx::ssnprintf_generic<std::string>(5, "abc %d %s test", 42, "hello"),
592         "abc 4");
593     die_unequal(
594         tlx::ssnprintf_generic<std::string>(5, "%d", 42), "42");
595 }
596 
test_replace()597 static void test_replace() {
598     // copy variants
599     die_unequal(
600         tlx::replace_first("abcdef abcdef", "abc", "a"), "adef abcdef");
601     die_unequal(
602         tlx::replace_first("abcdef abcdef", "cba", "a"), "abcdef abcdef");
603     die_unequal(
604         tlx::replace_all("abcdef abcdef", "abc", "a"), "adef adef");
605     die_unequal(
606         tlx::replace_all("abcdef abcdef", "cba", "a"), "abcdef abcdef");
607 
608     die_unequal(
609         tlx::replace_first("abcdef abcdef", "a", "aaa"),
610         "aaabcdef abcdef");
611     die_unequal(
612         tlx::replace_all("abcdef abcdef", "a", "aaa"),
613         "aaabcdef aaabcdef");
614 
615     // in-place variants
616     std::string str1 = "abcdef abcdef";
617     std::string str2 = "abcdef abcdef";
618     die_unequal(
619         tlx::replace_first(&str1, "abc", "a"), "adef abcdef");
620     die_unequal(
621         tlx::replace_first(&str2, "cba", "a"), "abcdef abcdef");
622 
623     str1 = "abcdef abcdef";
624     str2 = "abcdef abcdef";
625     die_unequal(
626         tlx::replace_all(&str1, "abc", "a"), "adef adef");
627     die_unequal(
628         tlx::replace_all(&str2, "cba", "a"), "abcdef abcdef");
629 
630     str1 = "abcdef abcdef";
631     str2 = "abcdef abcdef";
632     die_unequal(
633         tlx::replace_first(&str1, "a", "aaa"), "aaabcdef abcdef");
634     die_unequal(
635         tlx::replace_all(&str2, "a", "aaa"), "aaabcdef aaabcdef");
636 }
637 
638 template <typename TypeA, typename TypeB>
test_starts_with_ends_with_template()639 void test_starts_with_ends_with_template() {
640 
641     die_unless(tlx::starts_with(TypeA("abcdef"), TypeB("abc")));
642     die_unless(!tlx::starts_with(TypeA("abcdef"), TypeB("def")));
643     die_unless(tlx::ends_with(TypeA("abcdef"), TypeB("def")));
644     die_unless(!tlx::ends_with(TypeA("abcdef"), TypeB("abc")));
645 
646     die_unless(!tlx::starts_with(TypeA("abcdef"), TypeB("ABC")));
647 
648     die_unless(tlx::starts_with_icase(TypeA("abcdef"), TypeB("ABC")));
649     die_unless(!tlx::starts_with_icase(TypeA("abcdef"), TypeB("DEF")));
650     die_unless(tlx::ends_with_icase(TypeA("abcdef"), TypeB("DEF")));
651     die_unless(!tlx::ends_with_icase(TypeA("abcdef"), TypeB("ABC")));
652 
653     die_unless(tlx::starts_with(TypeA("abcdef"), TypeB("")));
654     die_unless(tlx::ends_with(TypeA("abcdef"), TypeB("")));
655 
656     die_unless(!tlx::starts_with(TypeA(""), TypeB("abc")));
657     die_unless(!tlx::ends_with(TypeA(""), TypeB("abc")));
658 
659     die_unless(tlx::starts_with(TypeA(""), TypeB("")));
660     die_unless(tlx::ends_with(TypeA(""), TypeB("")));
661 }
662 
test_starts_with_ends_with()663 static void test_starts_with_ends_with() {
664     test_starts_with_ends_with_template<const char*, const char*>();
665     test_starts_with_ends_with_template<const char*, std::string>();
666     test_starts_with_ends_with_template<std::string, const char*>();
667     test_starts_with_ends_with_template<std::string, std::string>();
668 }
669 
test_toupper_tolower()670 static void test_toupper_tolower() {
671     // string-copy functions
672     die_unequal(tlx::to_upper(" aBc "), " ABC ");
673     die_unequal(tlx::to_lower(" AbCdEfG "), " abcdefg ");
674 
675     // in-place functions
676     std::string str1 = "  aBc  ";
677     std::string str2 = "AbCdEfGh ";
678 
679     die_unequal(tlx::to_upper(&str1), "  ABC  ");
680     die_unequal(tlx::to_lower(&str2), "abcdefgh ");
681 }
682 
test_trim()683 static void test_trim() {
684     // string-copy functions
685     die_unequal(tlx::trim("  abc  "), "abc");
686     die_unequal(tlx::trim("abc  "), "abc");
687     die_unequal(tlx::trim("  abc"), "abc");
688     die_unequal(tlx::trim("  "), "");
689 
690     die_unequal(tlx::trim_left("  abc  "), "abc  ");
691     die_unequal(tlx::trim_left("abc  "), "abc  ");
692     die_unequal(tlx::trim_left("  "), "");
693 
694     die_unequal(tlx::trim_right("  abc  "), "  abc");
695     die_unequal(tlx::trim_right("  abc"), "  abc");
696     die_unequal(tlx::trim_right("  "), "");
697 
698     // in-place functions
699     std::string str1 = "  abc  ";
700     std::string str2 = "abc  ";
701     std::string str3 = "  ";
702 
703     die_unequal(tlx::trim_left(&str1), "abc  ");
704     die_unequal(tlx::trim_left(&str2), "abc  ");
705     die_unequal(tlx::trim_left(&str3), "");
706 
707     str1 = "  abc  ";
708     str2 = "  abc";
709     str3 = "  ";
710 
711     die_unequal(tlx::trim_right(&str1), "  abc");
712     die_unequal(tlx::trim_right(&str2), "  abc");
713     die_unequal(tlx::trim_right(&str3), "");
714 
715     str1 = "  abc  ";
716     str2 = "  abc";
717     str3 = "abc  ";
718     std::string str4 = "  ";
719 
720     die_unequal(tlx::trim(&str1), "abc");
721     die_unequal(tlx::trim(&str2), "abc");
722     die_unequal(tlx::trim(&str3), "abc");
723     die_unequal(tlx::trim(&str4), "");
724 }
725 
test_word_wrap()726 static void test_word_wrap() {
727 
728     const char* text =
729         "Alice was beginning to get very tired of sitting by her sister on the "
730         "bank, and of having nothing to do: once or twice she had peeped into "
731         "the book her sister was reading, but it had no pictures or "
732         "conversations in it, 'and what is the use of a book,' thought Alice "
733         "'without pictures or  conversations?'\n\nSo she was considering in "
734         "her own mind (as well as she could, for the hot day made her feel "
735         "very sleepy and stupid), whether the pleasure of making a daisy-chain "
736         "would be worth the trouble of getting up and picking the daisies, "
737         "when suddenly a White Rabbit with pink eyes ran close by "
738         "her.\n\nThere was nothing so VERY remarkable in that; nor did Alice "
739         "think it so VERY much out of the way to hear the Rabbit say to "
740         "itself, 'Oh dear! Oh dear! I shall be late!' (when she thought it "
741         "over afterwards, it occurred to her that she ought to have wondered "
742         "at this, but at the time it all seemed quite natural); but when the "
743         "Rabbit actually TOOK A WATCH OUT OF ITS WAISTCOAT-POCKET, and looked "
744         "at it, and then hurried on, Alice started to her feet, for it flashed "
745         "across her mind that she had never before seen a rabbit with either a "
746         "waistcoat-pocket, or a watch to take out of it, and burning with "
747         "curiosity, she ran across the field after it, and fortunately was "
748         "just in time to see it pop down a large rabbit-hole under the "
749         "hedge.\nIn another moment down went Alice after it, never once "
750         "considering how in the world she was to get out again.\n\nThe "
751         "rabbit-hole went straight on like a tunnel for some way, and then  "
752         "dipped suddenly down, so suddenly that Alice had not a moment to "
753         "think about stopping herself before she found herself falling down a "
754         "very deep well.\n\nEither the well was very deep, or she fell very "
755         "slowly, for she had plenty of time as she went down to look about her "
756         "and to wonder what was going to happen next. First, she tried to look "
757         "down and make out what she was coming to, but it was too dark to see "
758         "anything; then she looked at the sides of the well, and noticed that "
759         "they were filled with cupboards and book-shelves; here and there she "
760         "saw maps and pictures hung upon pegs. She took down a jar from one of "
761         "the shelves as she passed; it was labelled 'ORANGE MARMALADE', but to "
762         "her great disappointment it was empty: she did not like to drop the "
763         "jar for fear of killing somebody, so managed to put it into one of "
764         "the cupboards as she fell past it.\n\n'Well!' thought Alice to "
765         "herself, 'after such a fall as this, I shall think nothing of "
766         "tumbling down stairs! How brave they'll all think me at home! Why, I "
767         "wouldn't say anything about it, even if I fell off the top of the "
768         "house!' (Which was very likely true.)";
769 
770     const char* text_correct =
771         "Alice was beginning to get very tired of sitting by her\n"
772         "sister on the bank, and of having nothing to do: once or\n"
773         "twice she had peeped into the book her sister was reading,\n"
774         "but it had no pictures or conversations in it, 'and what is\n"
775         "the use of a book,' thought Alice 'without pictures or \n"
776         "conversations?'\n"
777         "\n"
778         "So she was considering in her own mind (as well as she\n"
779         "could, for the hot day made her feel very sleepy and\n"
780         "stupid), whether the pleasure of making a daisy-chain would\n"
781         "be worth the trouble of getting up and picking the daisies,\n"
782         "when suddenly a White Rabbit with pink eyes ran close by\n"
783         "her.\n"
784         "\n"
785         "There was nothing so VERY remarkable in that; nor did\n"
786         "Alice think it so VERY much out of the way to hear the\n"
787         "Rabbit say to itself, 'Oh dear! Oh dear! I shall be late!'\n"
788         "(when she thought it over afterwards, it occurred to her\n"
789         "that she ought to have wondered at this, but at the time it\n"
790         "all seemed quite natural); but when the Rabbit actually\n"
791         "TOOK A WATCH OUT OF ITS WAISTCOAT-POCKET, and looked at it,\n"
792         "and then hurried on, Alice started to her feet, for it\n"
793         "flashed across her mind that she had never before seen a\n"
794         "rabbit with either a waistcoat-pocket, or a watch to take\n"
795         "out of it, and burning with curiosity, she ran across the\n"
796         "field after it, and fortunately was just in time to see it\n"
797         "pop down a large rabbit-hole under the hedge.\n"
798         "In another moment down went Alice after it, never once\n"
799         "considering how in the world she was to get out again.\n"
800         "\n"
801         "The rabbit-hole went straight on like a tunnel for some\n"
802         "way, and then  dipped suddenly down, so suddenly that Alice\n"
803         "had not a moment to think about stopping herself before she\n"
804         "found herself falling down a very deep well.\n"
805         "\n"
806         "Either the well was very deep, or she fell very slowly,\n"
807         "for she had plenty of time as she went down to look about\n"
808         "her and to wonder what was going to happen next. First, she\n"
809         "tried to look down and make out what she was coming to, but\n"
810         "it was too dark to see anything; then she looked at the\n"
811         "sides of the well, and noticed that they were filled with\n"
812         "cupboards and book-shelves; here and there she saw maps and\n"
813         "pictures hung upon pegs. She took down a jar from one of\n"
814         "the shelves as she passed; it was labelled 'ORANGE\n"
815         "MARMALADE', but to her great disappointment it was empty:\n"
816         "she did not like to drop the jar for fear of killing\n"
817         "somebody, so managed to put it into one of the cupboards as\n"
818         "she fell past it.\n"
819         "\n"
820         "'Well!' thought Alice to herself, 'after such a fall as\n"
821         "this, I shall think nothing of tumbling down stairs! How\n"
822         "brave they'll all think me at home! Why, I wouldn't say\n"
823         "anything about it, even if I fell off the top of the\n"
824         "house!' (Which was very likely true.)";
825 
826     die_unequal(tlx::word_wrap(text, 60), text_correct);
827 
828     const char* long_line =
829         "abc abc abc abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
830         "abcdefghijklmnopqrstuvwxyz xyz xyz abcdefghijklmnopqrstuvwxyz"
831         "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz def def def";
832 
833     const char* long_line_correct =
834         "abc abc abc\n"
835         "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
836         "abcdefghijklmnopqrstuvwxyz\n"
837         "xyz xyz\n"
838         "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
839         "abcdefghijklmnopqrstuvwxyz\n"
840         "def def def";
841 
842     die_unequal(tlx::word_wrap(long_line, 60), long_line_correct);
843 }
844 
main()845 int main() {
846 
847     test_appendline();
848     test_base64();
849     test_bitdump();
850     test_compare_icase();
851     test_contains_word();
852     test_erase_all();
853     test_escape_html();
854     test_escape_uri();
855     test_expand_environment_variables();
856     test_extract_between();
857     test_format_si_iec_units();
858     test_hash_djb2();
859     test_hash_sdbm();
860     test_hexdump();
861     test_join();
862     test_levenshtein();
863     test_parse_si_iec_units();
864     test_replace();
865     test_split();
866     test_split_join_quoted();
867     test_split_words();
868     test_ssprintf();
869     test_starts_with_ends_with();
870     test_toupper_tolower();
871     test_trim();
872     test_word_wrap();
873 
874     return 0;
875 }
876 
877 /******************************************************************************/
878