NO-OP: name-change (_list and _lock are also used in other places)
[ardour.git] / libs / pbd / test / string_convert_test.cc
1 #include "string_convert_test.h"
2
3 #include <stdio.h>
4 #include <string.h>
5
6 #include <limits>
7 #include <cassert>
8
9 #include <pthread.h>
10
11 #include <glib.h>
12
13 #include "pbd/string_convert.h"
14
15 using namespace PBD;
16 using namespace std;
17
18 CPPUNIT_TEST_SUITE_REGISTRATION (StringConvertTest);
19
20 namespace {
21
22 class LocaleGuard {
23 public:
24         // RAII class that sets the global C locale and then resets it to its
25         // previous setting when going out of scope
26         LocaleGuard (const std::string& locale)
27         {
28                 m_previous_locale = setlocale (LC_ALL, NULL);
29
30                 CPPUNIT_ASSERT (m_previous_locale != NULL);
31
32                 const char* new_locale = setlocale (LC_ALL, locale.c_str ());
33
34                 if (new_locale == NULL) {
35                         std::cerr << "Failed to set locale to : " << locale << std::endl;
36                 }
37
38                 CPPUNIT_ASSERT (new_locale != NULL);
39         }
40
41         ~LocaleGuard ()
42         {
43                 CPPUNIT_ASSERT (setlocale (LC_ALL, m_previous_locale) != NULL);
44         }
45
46 private:
47         const char* m_previous_locale;
48 };
49
50 static
51 bool
52 check_decimal_mark_is_comma ()
53 {
54         char buf[32];
55         double const dnum = 12345.67890;
56         snprintf (buf, sizeof(buf), "%.12g", dnum);
57         bool found = (strchr (buf, ',') != NULL);
58         return found;
59 }
60
61 static std::vector<std::string> get_locale_list ()
62 {
63         std::vector<std::string> locales;
64
65         locales.push_back(""); // User preferred locale
66
67 #ifdef PLATFORM_WINDOWS
68         locales.push_back("French_France.1252"); // must be first
69         locales.push_back("Dutch_Netherlands.1252");
70         locales.push_back("Italian_Italy.1252");
71         locales.push_back("Farsi_Iran.1256");
72         locales.push_back("Chinese_China.936");
73         locales.push_back("Czech_Czech Republic.1250");
74 #else
75         locales.push_back("fr_FR"); // French France
76         locales.push_back("fr_FR.UTF-8");
77         locales.push_back("de_DE"); // German Germany
78         locales.push_back("de_DE.UTF-8");
79         locales.push_back("nl_NL"); // Dutch - Netherlands
80         locales.push_back("nl_NL.UTF-8");
81         locales.push_back("it_IT"); // Italian
82         locales.push_back("fa_IR"); // Farsi Iran
83         locales.push_back("zh_CN"); // Chinese
84         locales.push_back("cs_CZ"); // Czech
85 #endif
86         return locales;
87 }
88
89 static std::vector<std::string> get_supported_locales ()
90 {
91         std::vector<std::string> locales = get_locale_list ();
92         std::vector<std::string> supported_locales;
93
94         const char * m_orig_locale = setlocale (LC_ALL, NULL);
95
96         CPPUNIT_ASSERT (m_orig_locale != NULL);
97
98         std::cerr << std::endl << "Original locale: " << m_orig_locale << std::endl;
99
100         for (std::vector<std::string>::const_iterator it = locales.begin(); it != locales.end(); ++it) {
101
102                 const char* locale = it->c_str();
103                 const char* new_locale = setlocale (LC_ALL, locale);
104
105                 if (new_locale == NULL) {
106                         std::cerr << "Unable to set locale : " << locale << ", may not be installed." << std::endl;
107                         continue;
108                 }
109
110                 if (*it != new_locale) {
111                         // Setting the locale may be successful but locale has a different
112                         // (or longer) name.
113                         if (it->empty()) {
114                                 std::cerr << "User preferred locale is : " << new_locale << std::endl;
115                         } else {
116                                 std::cerr << "locale : " << locale << ", has name : " << new_locale << std::endl;
117                         }
118                 }
119
120                 std::cerr << "Adding locale : " << new_locale << " to test locales" << std::endl;
121
122                 supported_locales.push_back (*it);
123         }
124
125         if (setlocale (LC_ALL, m_orig_locale) == NULL) {
126                 std::cerr << "ERROR: Unable to restore original locale " << m_orig_locale
127                           << ", further tests may be invalid." << std::endl;
128         }
129
130         return supported_locales;
131 }
132
133 static std::vector<std::string> get_test_locales ()
134 {
135         static std::vector<std::string> locales = get_supported_locales ();
136         return locales;
137 }
138
139 static bool get_locale_with_comma_decimal_mark (std::string& locale_str) {
140         std::vector<std::string> locales = get_test_locales ();
141
142         for (std::vector<std::string>::const_iterator it = locales.begin(); it != locales.end(); ++it) {
143                 LocaleGuard guard (*it);
144                 if (check_decimal_mark_is_comma ()) {
145                         locale_str = *it;
146                         return true;
147                 }
148         }
149         return false;
150 }
151
152 } // anon namespace
153
154 void
155 StringConvertTest::test_required_locales ()
156 {
157         std::string locale_str;
158         CPPUNIT_ASSERT(get_locale_with_comma_decimal_mark(locale_str));
159 }
160
161 static const std::string MAX_INT16_STR ("32767");
162 static const std::string MIN_INT16_STR ("-32768");
163
164 typedef void (*TestFunctionType)(void);
165
166 void
167 test_function_for_locales (TestFunctionType test_func)
168 {
169         const std::vector<std::string> locales = get_test_locales();
170
171         for (std::vector<std::string>::const_iterator ci = locales.begin ();
172              ci != locales.end ();
173              ++ci) {
174                 LocaleGuard guard (*ci);
175                 test_func ();
176         }
177 }
178
179 void
180 _test_int16_conversion ()
181 {
182         string str;
183         CPPUNIT_ASSERT (int16_to_string (numeric_limits<int16_t>::max (), str));
184         CPPUNIT_ASSERT_EQUAL (MAX_INT16_STR, str);
185
186         int16_t val = 0;
187         CPPUNIT_ASSERT (string_to_int16 (str, val));
188         CPPUNIT_ASSERT_EQUAL (numeric_limits<int16_t>::max (), val);
189
190         CPPUNIT_ASSERT (int16_to_string (numeric_limits<int16_t>::min (), str));
191         CPPUNIT_ASSERT_EQUAL (MIN_INT16_STR, str);
192
193         CPPUNIT_ASSERT (string_to_int16 (str, val));
194         CPPUNIT_ASSERT_EQUAL (numeric_limits<int16_t>::min (), val);
195
196         // test the string_to/to_string templates
197         int16_t max = numeric_limits<int16_t>::max ();
198         CPPUNIT_ASSERT_EQUAL (max, string_to<int16_t>(to_string (max)));
199
200         int16_t min = numeric_limits<int16_t>::min ();
201         CPPUNIT_ASSERT_EQUAL (min, string_to<int16_t>(to_string (min)));
202 }
203
204 void
205 StringConvertTest::test_int16_conversion ()
206 {
207         test_function_for_locales(&_test_int16_conversion);
208 }
209
210 static const std::string MAX_UINT16_STR("65535");
211 static const std::string MIN_UINT16_STR("0");
212
213 void
214 _test_uint16_conversion ()
215 {
216         string str;
217         CPPUNIT_ASSERT (uint16_to_string (numeric_limits<uint16_t>::max (), str));
218         CPPUNIT_ASSERT_EQUAL (MAX_UINT16_STR, str);
219
220         uint16_t val = 0;
221         CPPUNIT_ASSERT (string_to_uint16 (str, val));
222         CPPUNIT_ASSERT_EQUAL (numeric_limits<uint16_t>::max (), val);
223
224         CPPUNIT_ASSERT (uint16_to_string (numeric_limits<uint16_t>::min (), str));
225         CPPUNIT_ASSERT_EQUAL (MIN_UINT16_STR, str);
226
227         CPPUNIT_ASSERT (string_to_uint16 (str, val));
228         CPPUNIT_ASSERT_EQUAL (numeric_limits<uint16_t>::min (), val);
229
230         // test the string_to/to_string templates
231         uint16_t max = numeric_limits<uint16_t>::max ();
232         CPPUNIT_ASSERT_EQUAL (max, string_to<uint16_t>(to_string (max)));
233
234         uint16_t min = numeric_limits<uint16_t>::min ();
235         CPPUNIT_ASSERT_EQUAL (min, string_to<uint16_t>(to_string (min)));
236 }
237
238 void
239 StringConvertTest::test_uint16_conversion ()
240 {
241         test_function_for_locales(&_test_uint16_conversion);
242 }
243
244 static const std::string MAX_INT32_STR ("2147483647");
245 static const std::string MIN_INT32_STR ("-2147483648");
246
247 void
248 _test_int32_conversion ()
249 {
250         string str;
251         CPPUNIT_ASSERT (int32_to_string (numeric_limits<int32_t>::max (), str));
252         CPPUNIT_ASSERT_EQUAL (MAX_INT32_STR, str);
253
254         int32_t val = 0;
255         CPPUNIT_ASSERT (string_to_int32 (str, val));
256         CPPUNIT_ASSERT_EQUAL (numeric_limits<int32_t>::max (), val);
257
258         CPPUNIT_ASSERT (int32_to_string (numeric_limits<int32_t>::min (), str));
259         CPPUNIT_ASSERT_EQUAL (MIN_INT32_STR, str);
260
261         CPPUNIT_ASSERT (string_to_int32 (str, val));
262         CPPUNIT_ASSERT_EQUAL (numeric_limits<int32_t>::min (), val);
263
264         // test the string_to/to_string templates
265         int32_t max = numeric_limits<int32_t>::max ();
266         CPPUNIT_ASSERT_EQUAL (max, string_to<int32_t>(to_string (max)));
267
268         int32_t min = numeric_limits<int32_t>::min ();
269         CPPUNIT_ASSERT_EQUAL (min, string_to<int32_t>(to_string (min)));
270 }
271
272 void
273 StringConvertTest::test_int32_conversion ()
274 {
275         test_function_for_locales(&_test_int32_conversion);
276 }
277
278 static const std::string MAX_UINT32_STR("4294967295");
279 static const std::string MIN_UINT32_STR("0");
280
281 void
282 _test_uint32_conversion ()
283 {
284         string str;
285         CPPUNIT_ASSERT (uint32_to_string (numeric_limits<uint32_t>::max (), str));
286         CPPUNIT_ASSERT_EQUAL (MAX_UINT32_STR, str);
287
288         uint32_t val = 0;
289         CPPUNIT_ASSERT (string_to_uint32 (str, val));
290         CPPUNIT_ASSERT_EQUAL (numeric_limits<uint32_t>::max (), val);
291
292         CPPUNIT_ASSERT (uint32_to_string (numeric_limits<uint32_t>::min (), str));
293         CPPUNIT_ASSERT_EQUAL (MIN_UINT32_STR, str);
294
295         CPPUNIT_ASSERT (string_to_uint32 (str, val));
296         CPPUNIT_ASSERT_EQUAL (numeric_limits<uint32_t>::min (), val);
297
298         // test the string_to/to_string templates
299         uint32_t max = numeric_limits<uint32_t>::max ();
300         CPPUNIT_ASSERT_EQUAL (max, string_to<uint32_t>(to_string (max)));
301
302         uint32_t min = numeric_limits<uint32_t>::min ();
303         CPPUNIT_ASSERT_EQUAL (min, string_to<uint32_t>(to_string (min)));
304 }
305
306 void
307 StringConvertTest::test_uint32_conversion ()
308 {
309         test_function_for_locales(&_test_uint32_conversion);
310 }
311
312 static const std::string MAX_INT64_STR ("9223372036854775807");
313 static const std::string MIN_INT64_STR ("-9223372036854775808");
314
315 void
316 _test_int64_conversion ()
317 {
318         string str;
319         CPPUNIT_ASSERT (int64_to_string (numeric_limits<int64_t>::max (), str));
320         CPPUNIT_ASSERT_EQUAL (MAX_INT64_STR, str);
321
322         int64_t val = 0;
323         CPPUNIT_ASSERT (string_to_int64 (str, val));
324         CPPUNIT_ASSERT_EQUAL (numeric_limits<int64_t>::max (), val);
325
326         CPPUNIT_ASSERT (int64_to_string (numeric_limits<int64_t>::min (), str));
327         CPPUNIT_ASSERT_EQUAL (MIN_INT64_STR, str);
328
329         CPPUNIT_ASSERT (string_to_int64 (str, val));
330         CPPUNIT_ASSERT_EQUAL (numeric_limits<int64_t>::min (), val);
331
332         // test the string_to/to_string templates
333         int64_t max = numeric_limits<int64_t>::max ();
334         CPPUNIT_ASSERT_EQUAL (max, string_to<int64_t>(to_string (max)));
335
336         int64_t min = numeric_limits<int64_t>::min ();
337         CPPUNIT_ASSERT_EQUAL (min, string_to<int64_t>(to_string (min)));
338 }
339
340 void
341 StringConvertTest::test_int64_conversion ()
342 {
343         test_function_for_locales(&_test_int64_conversion);
344 }
345
346 static const std::string MAX_UINT64_STR ("18446744073709551615");
347 static const std::string MIN_UINT64_STR ("0");
348
349 void
350 _test_uint64_conversion ()
351 {
352         string str;
353         CPPUNIT_ASSERT (uint64_to_string (numeric_limits<uint64_t>::max (), str));
354         CPPUNIT_ASSERT_EQUAL (MAX_UINT64_STR, str);
355
356         uint64_t val = 0;
357         CPPUNIT_ASSERT (string_to_uint64 (str, val));
358         CPPUNIT_ASSERT_EQUAL (numeric_limits<uint64_t>::max (), val);
359
360         CPPUNIT_ASSERT (uint64_to_string (numeric_limits<uint64_t>::min (), str));
361         CPPUNIT_ASSERT_EQUAL (MIN_UINT64_STR, str);
362
363         CPPUNIT_ASSERT (string_to_uint64 (str, val));
364         CPPUNIT_ASSERT_EQUAL (numeric_limits<uint64_t>::min (), val);
365
366         // test the string_to/to_string templates
367         uint64_t max = numeric_limits<uint64_t>::max ();
368         CPPUNIT_ASSERT_EQUAL (max, string_to<uint64_t>(to_string (max)));
369
370         uint64_t min = numeric_limits<uint64_t>::min ();
371         CPPUNIT_ASSERT_EQUAL (min, string_to<uint64_t>(to_string (min)));
372 }
373
374 void
375 StringConvertTest::test_uint64_conversion ()
376 {
377         test_function_for_locales(&_test_uint64_conversion);
378 }
379
380 static const std::string POS_INFINITY_STR ("infinity");
381 static const std::string NEG_INFINITY_STR ("-infinity");
382 static const std::string POS_INFINITY_CAPS_STR ("INFINITY");
383 static const std::string NEG_INFINITY_CAPS_STR ("-INFINITY");
384 static const std::string POS_INF_STR ("inf");
385 static const std::string NEG_INF_STR ("-inf");
386 static const std::string POS_INF_CAPS_STR ("INF");
387 static const std::string NEG_INF_CAPS_STR ("-INF");
388
389 static
390 std::vector<std::string>
391 _pos_infinity_strings ()
392 {
393         std::vector<std::string> vec;
394         vec.push_back (POS_INFINITY_STR);
395         vec.push_back (POS_INFINITY_CAPS_STR);
396         vec.push_back (POS_INF_STR);
397         vec.push_back (POS_INF_CAPS_STR);
398         return vec;
399 }
400
401 static
402 std::vector<std::string>
403 _neg_infinity_strings ()
404 {
405         std::vector<std::string> vec;
406         vec.push_back (NEG_INFINITY_STR);
407         vec.push_back (NEG_INFINITY_CAPS_STR);
408         vec.push_back (NEG_INF_STR);
409         vec.push_back (NEG_INF_CAPS_STR);
410         return vec;
411 }
412
413 template <class FloatType>
414 void
415 _test_infinity_conversion ()
416 {
417         const FloatType pos_infinity = numeric_limits<FloatType>::infinity ();
418         const FloatType neg_infinity = -numeric_limits<FloatType>::infinity ();
419
420         // Check float -> string
421         string str;
422         CPPUNIT_ASSERT (to_string<FloatType> (pos_infinity, str));
423         CPPUNIT_ASSERT_EQUAL (POS_INF_STR, str);
424
425         CPPUNIT_ASSERT (to_string<FloatType> (neg_infinity, str));
426         CPPUNIT_ASSERT_EQUAL (NEG_INF_STR, str);
427
428         // Check string -> float for all supported string representations of "INFINITY"
429         std::vector<std::string> pos_inf_strings = _pos_infinity_strings ();
430
431         for (std::vector<std::string>::const_iterator i = pos_inf_strings.begin ();
432              i != pos_inf_strings.end (); ++i) {
433                 FloatType pos_infinity_arg;
434                 CPPUNIT_ASSERT (string_to<FloatType> (*i, pos_infinity_arg));
435                 CPPUNIT_ASSERT_EQUAL (pos_infinity_arg, pos_infinity);
436         }
437
438         // Check string -> float for all supported string representations of "-INFINITY"
439         std::vector<std::string> neg_inf_strings = _neg_infinity_strings ();
440
441         for (std::vector<std::string>::const_iterator i = neg_inf_strings.begin ();
442              i != neg_inf_strings.end (); ++i) {
443                 FloatType neg_infinity_arg;
444                 CPPUNIT_ASSERT (string_to<FloatType> (*i, neg_infinity_arg));
445                 CPPUNIT_ASSERT_EQUAL (neg_infinity_arg, neg_infinity);
446         }
447
448         // Check round-trip equality
449         CPPUNIT_ASSERT_EQUAL (pos_infinity, string_to<FloatType> (to_string<FloatType> (pos_infinity)));
450         CPPUNIT_ASSERT_EQUAL (neg_infinity, string_to<FloatType> (to_string<FloatType> (neg_infinity)));
451 }
452
453 static const std::string MAX_FLOAT_WIN ("3.4028234663852886e+038");
454 static const std::string MIN_FLOAT_WIN ("1.1754943508222875e-038");
455 static const std::string MAX_FLOAT_STR ("3.4028234663852886e+38");
456 static const std::string MIN_FLOAT_STR ("1.1754943508222875e-38");
457
458 void
459 _test_float_conversion ()
460 {
461         // check float to string and back again for min and max float values
462         string str;
463         CPPUNIT_ASSERT (float_to_string (numeric_limits<float>::max (), str));
464 #ifdef PLATFORM_WINDOWS
465         CPPUNIT_ASSERT_EQUAL (MAX_FLOAT_WIN, str);
466 #else
467         CPPUNIT_ASSERT_EQUAL (MAX_FLOAT_STR, str);
468 #endif
469
470         float val = 0.0f;
471         CPPUNIT_ASSERT (string_to_float (str, val));
472         CPPUNIT_ASSERT_DOUBLES_EQUAL (
473             numeric_limits<float>::max (), val, numeric_limits<float>::epsilon ());
474
475         CPPUNIT_ASSERT (float_to_string (numeric_limits<float>::min (), str));
476 #ifdef PLATFORM_WINDOWS
477         CPPUNIT_ASSERT_EQUAL (MIN_FLOAT_WIN, str);
478 #else
479         CPPUNIT_ASSERT_EQUAL (MIN_FLOAT_STR, str);
480 #endif
481
482         CPPUNIT_ASSERT (string_to_float (str, val));
483         CPPUNIT_ASSERT_DOUBLES_EQUAL (
484             numeric_limits<float>::min (), val, numeric_limits<float>::epsilon ());
485
486         // test the string_to/to_string templates
487         float max = numeric_limits<float>::max ();
488         CPPUNIT_ASSERT_EQUAL (max, string_to<float>(to_string<float> (max)));
489
490         float min = numeric_limits<float>::min ();
491         CPPUNIT_ASSERT_EQUAL (min, string_to<float>(to_string<float> (min)));
492
493 // check that parsing the windows float string representation with the
494 // difference in the exponent part parses correctly on other platforms
495 // and vice versa
496 #ifdef PLATFORM_WINDOWS
497         CPPUNIT_ASSERT (string_to_float (MAX_FLOAT_STR, val));
498         CPPUNIT_ASSERT_DOUBLES_EQUAL (
499             numeric_limits<float>::max (), val, numeric_limits<float>::epsilon ());
500
501         CPPUNIT_ASSERT (string_to_float (MIN_FLOAT_STR, val));
502         CPPUNIT_ASSERT_DOUBLES_EQUAL (
503             numeric_limits<float>::min (), val, numeric_limits<float>::epsilon ());
504 #else
505         CPPUNIT_ASSERT (string_to_float (MAX_FLOAT_WIN, val));
506         CPPUNIT_ASSERT_DOUBLES_EQUAL (
507             numeric_limits<float>::max (), val, numeric_limits<float>::epsilon ());
508
509         CPPUNIT_ASSERT (string_to_float (MIN_FLOAT_WIN, val));
510         CPPUNIT_ASSERT_DOUBLES_EQUAL (
511             numeric_limits<float>::min (), val, numeric_limits<float>::epsilon ());
512 #endif
513
514         _test_infinity_conversion<float>();
515 }
516
517 void
518 StringConvertTest::test_float_conversion ()
519 {
520         test_function_for_locales(&_test_float_conversion);
521 }
522
523 static const std::string MAX_DOUBLE_STR ("1.7976931348623157e+308");
524 static const std::string MIN_DOUBLE_STR ("2.2250738585072014e-308");
525
526 void
527 _test_double_conversion ()
528 {
529         string str;
530         CPPUNIT_ASSERT (double_to_string (numeric_limits<double>::max (), str));
531         CPPUNIT_ASSERT_EQUAL (MAX_DOUBLE_STR, str);
532
533         double val = 0.0;
534         CPPUNIT_ASSERT (string_to_double (str, val));
535         CPPUNIT_ASSERT_DOUBLES_EQUAL (
536             numeric_limits<double>::max (), val, numeric_limits<double>::epsilon ());
537
538         CPPUNIT_ASSERT (double_to_string (numeric_limits<double>::min (), str));
539         CPPUNIT_ASSERT_EQUAL (MIN_DOUBLE_STR, str);
540
541         CPPUNIT_ASSERT (string_to_double (str, val));
542         CPPUNIT_ASSERT_DOUBLES_EQUAL (
543             numeric_limits<double>::min (), val, numeric_limits<double>::epsilon ());
544
545         // test that overflow fails
546         CPPUNIT_ASSERT (!string_to_double ("1.8e+308", val));
547         // test that underflow fails
548         CPPUNIT_ASSERT (!string_to_double ("2.4e-310", val));
549
550         // test the string_to/to_string templates
551         double max = numeric_limits<double>::max ();
552         CPPUNIT_ASSERT_EQUAL (max, string_to<double>(to_string<double> (max)));
553
554         double min = numeric_limits<double>::min ();
555         CPPUNIT_ASSERT_EQUAL (min, string_to<double>(to_string<double> (min)));
556
557         _test_infinity_conversion<double>();
558 }
559
560 void
561 StringConvertTest::test_double_conversion ()
562 {
563         test_function_for_locales(&_test_double_conversion);
564 }
565
566 // we have to use these as CPPUNIT_ASSERT_EQUAL won't accept char arrays
567 static const std::string BOOL_TRUE_STR ("1");
568 static const std::string BOOL_FALSE_STR ("0");
569
570 void
571 StringConvertTest::test_bool_conversion ()
572 {
573         string str;
574
575         // check the normal case for true/false
576         CPPUNIT_ASSERT (bool_to_string (true, str));
577         CPPUNIT_ASSERT_EQUAL (BOOL_TRUE_STR, str);
578
579         bool val = false;
580         CPPUNIT_ASSERT (string_to_bool (str, val));
581         CPPUNIT_ASSERT_EQUAL (val, true);
582
583         CPPUNIT_ASSERT (bool_to_string (false, str));
584         CPPUNIT_ASSERT_EQUAL (BOOL_FALSE_STR, str);
585
586         val = true;
587         CPPUNIT_ASSERT (string_to_bool (str, val));
588         CPPUNIT_ASSERT_EQUAL (val, false);
589
590         // now check the other accepted values for true and false
591         // when converting from a string to bool
592
593         val = false;
594         CPPUNIT_ASSERT (string_to_bool ("1", val));
595         CPPUNIT_ASSERT_EQUAL (val, true);
596
597         val = true;
598         CPPUNIT_ASSERT (string_to_bool ("0", val));
599         CPPUNIT_ASSERT_EQUAL (val, false);
600
601         val = false;
602         CPPUNIT_ASSERT (string_to_bool ("Y", val));
603         CPPUNIT_ASSERT_EQUAL (val, true);
604
605         val = true;
606         CPPUNIT_ASSERT (string_to_bool ("N", val));
607         CPPUNIT_ASSERT_EQUAL (val, false);
608
609         val = false;
610         CPPUNIT_ASSERT (string_to_bool ("y", val));
611         CPPUNIT_ASSERT_EQUAL (val, true);
612
613         val = true;
614         CPPUNIT_ASSERT (string_to_bool ("n", val));
615         CPPUNIT_ASSERT_EQUAL (val, false);
616
617         // test some junk
618         CPPUNIT_ASSERT (!string_to_bool ("01234someYNtrueyesno junk0123", val));
619
620         // test the string_to/to_string templates
621         CPPUNIT_ASSERT_EQUAL (true, string_to<bool>(to_string (true)));
622
623         CPPUNIT_ASSERT_EQUAL (false, string_to<bool>(to_string (false)));
624 }
625
626 namespace {
627
628 bool
629 check_int_convert ()
630 {
631         int32_t num = g_random_int ();
632         return (num == string_to<int32_t>(to_string (num)));
633 }
634
635 bool
636 check_float_convert ()
637 {
638         float num = (float)g_random_double ();
639         return (num == string_to<float>(to_string<float> (num)));
640 }
641
642 bool
643 check_double_convert ()
644 {
645         double num = g_random_double ();
646         return (num == string_to<double>(to_string<double> (num)));
647 }
648
649 static const int s_iter_count = 1000000;
650
651 void*
652 check_int_convert_thread(void*)
653 {
654         for (int n = 0; n < s_iter_count; n++) {
655                 assert (check_int_convert ());
656         }
657         return NULL;
658 }
659
660 void*
661 check_float_convert_thread(void*)
662 {
663         for (int n = 0; n < s_iter_count; n++) {
664                 assert (check_float_convert ());
665         }
666         return NULL;
667 }
668
669 void*
670 check_double_convert_thread(void*)
671 {
672         for (int n = 0; n < s_iter_count; n++) {
673                 assert (check_double_convert ());
674         }
675         return NULL;
676 }
677
678 static const double s_test_double = 31459.265359;
679
680 void*
681 check_decimal_mark_is_comma_thread (void*)
682 {
683         for (int n = 0; n < s_iter_count; n++) {
684                 assert (check_decimal_mark_is_comma ());
685         }
686
687         return NULL;
688 }
689
690 } // anon namespace
691
692 // Perform the test in the French locale as the format for decimals is
693 // different and a comma is used as a decimal point. Test that this has no
694 // impact on the string conversions which are expected to be the same as in the
695 // C locale.
696 void
697 StringConvertTest::test_convert_thread_safety ()
698 {
699         std::string locale_str;
700
701         CPPUNIT_ASSERT(get_locale_with_comma_decimal_mark(locale_str));
702
703         LocaleGuard guard (locale_str);
704
705         CPPUNIT_ASSERT (check_int_convert ());
706         CPPUNIT_ASSERT (check_float_convert ());
707         CPPUNIT_ASSERT (check_double_convert ());
708         CPPUNIT_ASSERT (check_decimal_mark_is_comma ());
709
710         pthread_t convert_int_thread;
711         pthread_t convert_float_thread;
712         pthread_t convert_double_thread;
713         pthread_t fr_printf_thread;
714
715         CPPUNIT_ASSERT (
716             pthread_create (
717                 &convert_int_thread, NULL, check_int_convert_thread, NULL) == 0);
718         CPPUNIT_ASSERT (
719             pthread_create (
720                 &convert_float_thread, NULL, check_float_convert_thread, NULL) == 0);
721         CPPUNIT_ASSERT (
722             pthread_create (
723                 &convert_double_thread, NULL, check_double_convert_thread, NULL) == 0);
724         CPPUNIT_ASSERT (
725             pthread_create (&fr_printf_thread, NULL, check_decimal_mark_is_comma_thread, NULL) ==
726             0);
727
728         void* return_value;
729
730         CPPUNIT_ASSERT (pthread_join (convert_int_thread, &return_value) == 0);
731         CPPUNIT_ASSERT (pthread_join (convert_float_thread, &return_value) == 0);
732         CPPUNIT_ASSERT (pthread_join (convert_double_thread, &return_value) == 0);
733         CPPUNIT_ASSERT (pthread_join (fr_printf_thread, &return_value) == 0);
734 }