mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
Loading...
Searching...
No Matches
stringUtils_test.cpp
Go to the documentation of this file.
1/** \file stringUtils_test.cpp
2 * \brief Tests string-conversion utilities.
3 */
4#include "../../catch2/catch.hpp"
5
6#define MX_NO_ERROR_REPORTS
7
9
10#include <cmath>
11#include <vector>
12
13using namespace Catch::Matchers;
14
15using namespace mx::ioutils;
16using namespace mx;
17
18namespace unitTest
19{
20namespace ioutilsTest
21{
22namespace stringUtilsTest
23{
24
25/// Converting strings to numbers
26/**
27 * \ingroup stringUtils_unit_tests
28 */
29TEST_CASE( "Converting strings to numbers", "[ioutils::stringUtils]" )
30{
31 SECTION( "a string char" )
32 {
33 SECTION( "string valid, positive, no error check" )
34 {
35 char val = stoT<char>( "5" );
36 REQUIRE( static_cast<int>( val ) == 5 );
37 }
38
39 SECTION( "string valid, negative, w/ error check" )
40 {
41 mx::error_t errc;
42 char val = stoT<char>( "-5", &errc );
43 REQUIRE( static_cast<int>( val ) == -5 );
44 REQUIRE( errc == mx::error_t::noerror );
45 }
46
47 SECTION( "positive overflow, w/ error check" )
48 {
49 mx::error_t errc;
50 char val = stoT<char>( "128", &errc );
51 REQUIRE( static_cast<int>( val ) == std::numeric_limits<char>::max() );
52 REQUIRE( errc == mx::error_t::erange );
53 }
54
55 SECTION( "negative overflow, w/ error check" )
56 {
57 mx::error_t errc;
58 char val = stoT<char>( "-129", &errc );
59 REQUIRE( static_cast<int>( val ) == std::numeric_limits<char>::lowest() );
60 REQUIRE( errc == mx::error_t::erange );
61 }
62
63 SECTION( "invalid string, w/ error check" )
64 {
65 mx::error_t errc;
66 char val = stoT<char>( "!", &errc );
67 REQUIRE( static_cast<int>( val ) == 0 );
68 REQUIRE( errc == mx::error_t::invalidarg );
69 }
70
71 SECTION( "string valid w/ leading spaces, negative, w/ error check" )
72 {
73 mx::error_t errc;
74 char val = stoT<char>( " -5", &errc );
75 REQUIRE( static_cast<int>( val ) == -5 );
76 REQUIRE( errc == mx::error_t::noerror );
77 }
78 }
79
80 SECTION( "a string unsigned char" )
81 {
82 SECTION( "string valid, positive, no error check" )
83 {
84 unsigned char val = stoT<unsigned char>( "200" );
85 REQUIRE( static_cast<int>( val ) == 200 );
86 }
87
88 SECTION( "string valid, w/ error check" )
89 {
90 mx::error_t errc;
91 unsigned char val = stoT<unsigned char>( "100", &errc );
92 REQUIRE( static_cast<int>( val ) == 100 );
93 REQUIRE( errc == mx::error_t::noerror );
94 }
95
96 SECTION( "positive overflow, w/ error check" )
97 {
98 mx::error_t errc;
99 unsigned char val = stoT<unsigned char>( "256", &errc );
100 REQUIRE( static_cast<int>( val ) == std::numeric_limits<unsigned char>::max() );
101 REQUIRE( errc == mx::error_t::erange );
102 }
103
104 SECTION( "invalid string, w/ error check" )
105 {
106 mx::error_t errc;
107 unsigned char val = stoT<unsigned char>( "*", &errc );
108 REQUIRE( static_cast<int>( val ) == 0 );
109 REQUIRE( errc == mx::error_t::invalidarg );
110 }
111 }
112
113 SECTION( "a string short" )
114 {
115 SECTION( "string valid, positive, no error check" )
116 {
117 short val = stoT<short>( "500" );
118 REQUIRE( static_cast<int>( val ) == 500 );
119 }
120
121 SECTION( "string valid, negative, w/ error check" )
122 {
123 mx::error_t errc;
124 short val = stoT<short>( "-1024", &errc );
125 REQUIRE( static_cast<int>( val ) == -1024 );
126 REQUIRE( errc == mx::error_t::noerror );
127 }
128
129 SECTION( "positive overflow, w/ error check" )
130 {
131 mx::error_t errc;
132 short val = stoT<short>( "47000", &errc );
133 REQUIRE( static_cast<int>( val ) == std::numeric_limits<short>::max() );
134 REQUIRE( errc == mx::error_t::erange );
135 }
136
137 SECTION( "negative overflow, w/ error check" )
138 {
139 mx::error_t errc;
140 short val = stoT<short>( "-37000", &errc );
141 REQUIRE( static_cast<int>( val ) == std::numeric_limits<short>::lowest() );
142 REQUIRE( errc == mx::error_t::erange );
143 }
144
145 SECTION( "invalid string, w/ error check" )
146 {
147 mx::error_t errc;
148 short val = stoT<short>( "-", &errc );
149 REQUIRE( static_cast<int>( val ) == 0 );
150 REQUIRE( errc == mx::error_t::invalidarg );
151 }
152 }
153
154 SECTION( "a string unsigned short" )
155 {
156 SECTION( "string valid, positive, no error check" )
157 {
158 unsigned short val = stoT<unsigned short>( "20000" );
159 REQUIRE( static_cast<int>( val ) == 20000 );
160 }
161
162 SECTION( "string valid, w/ error check" )
163 {
164 mx::error_t errc;
165 unsigned short val = stoT<unsigned short>( "65000", &errc );
166 REQUIRE( static_cast<int>( val ) == 65000 );
167 REQUIRE( errc == mx::error_t::noerror );
168 }
169
170 SECTION( "positive overflow, w/ error check" )
171 {
172 mx::error_t errc;
173 unsigned short val = stoT<unsigned short>( "70000", &errc );
174 REQUIRE( static_cast<int>( val ) == std::numeric_limits<unsigned short>::max() );
175 REQUIRE( errc == mx::error_t::erange );
176 }
177
178 SECTION( "invalid string, w/ error check" )
179 {
180 mx::error_t errc;
181 unsigned short val = stoT<unsigned short>( "#", &errc );
182 REQUIRE( static_cast<int>( val ) == 0 );
183 REQUIRE( errc == mx::error_t::invalidarg );
184 }
185 }
186
187 SECTION( "a string int" )
188 {
189 SECTION( "string valid, positive, no error check" )
190 {
191 int val = stoT<int>( "1000000" );
192 REQUIRE( static_cast<int>( val ) == 1000000 );
193 }
194
195 SECTION( "string valid, negative, w/ error check" )
196 {
197 mx::error_t errc;
198 int val = stoT<int>( "-2000000", &errc );
199 REQUIRE( static_cast<int>( val ) == -2000000 );
200 REQUIRE( errc == mx::error_t::noerror );
201 }
202
203 SECTION( "positive overflow, w/ error check" )
204 {
205 mx::error_t errc;
206 int val = stoT<int>( "3000000000", &errc );
207 REQUIRE( static_cast<int>( val ) == std::numeric_limits<int>::max() );
208 REQUIRE( errc == mx::error_t::erange );
209 }
210
211 SECTION( "negative overflow, w/ error check" )
212 {
213 mx::error_t errc;
214 int val = stoT<int>( "-2147483650", &errc );
215 REQUIRE( static_cast<int>( val ) == std::numeric_limits<int>::lowest() );
216 REQUIRE( errc == mx::error_t::erange );
217 }
218
219 SECTION( "invalid string, w/ error check" )
220 {
221 mx::error_t errc;
222 int val = stoT<int>( " w", &errc );
223 REQUIRE( static_cast<int>( val ) == 0 );
224 REQUIRE( errc == mx::error_t::invalidarg );
225 }
226 }
227
228 SECTION( "a string unsigned int" )
229 {
230 SECTION( "string valid, positive, no error check" )
231 {
232 unsigned int val = stoT<unsigned int>( "4000000000" );
233 REQUIRE( static_cast<unsigned int>( val ) == 4000000000 );
234 }
235
236 SECTION( "string valid, w/ error check" )
237 {
238 mx::error_t errc;
239 unsigned int val = stoT<unsigned int>( "2", &errc );
240 REQUIRE( static_cast<unsigned int>( val ) == 2 );
241 REQUIRE( errc == mx::error_t::noerror );
242 }
243
244 SECTION( "positive overflow, w/ error check" )
245 {
246 mx::error_t errc;
247 unsigned int val = stoT<unsigned int>( "6000000000", &errc );
248 REQUIRE( static_cast<unsigned int>( val ) == std::numeric_limits<unsigned int>::max() );
249 REQUIRE( errc == mx::error_t::erange );
250 }
251
252 SECTION( "invalid string, w/ error check" )
253 {
254 mx::error_t errc;
255 unsigned int val = stoT<unsigned int>( "?8", &errc );
256 REQUIRE( static_cast<unsigned int>( val ) == 0 );
257 REQUIRE( errc == mx::error_t::invalidarg );
258 }
259 }
260
261 SECTION( "a string long" )
262 {
263 SECTION( "string valid, positive, no error check" )
264 {
265 long val = stoT<long>( "1000000" );
266 REQUIRE( static_cast<long>( val ) == 1000000 );
267 }
268
269 SECTION( "string valid, negative, w/ error check" )
270 {
271 mx::error_t errc;
272 long val = stoT<long>( "-2000000", &errc );
273 REQUIRE( static_cast<long>( val ) == -2000000 );
274 REQUIRE( errc == mx::error_t::noerror );
275 }
276
277 SECTION( "positive overflow, w/ error check" )
278 {
279 mx::error_t errc;
280 long val = stoT<long>( "9223372036854775808", &errc );
281 REQUIRE( static_cast<long>( val ) == std::numeric_limits<long>::max() );
282 REQUIRE( errc == mx::error_t::erange );
283 }
284
285 SECTION( "negative overflow, w/ error check" )
286 {
287 mx::error_t errc;
288 long val = stoT<long>( "-9223372036854775809", &errc );
289 REQUIRE( static_cast<long>( val ) == std::numeric_limits<long>::lowest() );
290 REQUIRE( errc == mx::error_t::erange );
291 }
292
293 SECTION( "invalid string, w/ error check" )
294 {
295 mx::error_t errc;
296 long val = stoT<long>( " w8", &errc );
297 REQUIRE( static_cast<long>( val ) == 0 );
298 REQUIRE( errc == mx::error_t::invalidarg );
299 }
300 }
301
302 SECTION( "a string unsigned long" )
303 {
304 SECTION( "string valid, positive, no error check" )
305 {
306 unsigned long val = stoT<unsigned long>( "400000000000" );
307 REQUIRE( static_cast<unsigned long>( val ) == 400000000000 );
308 }
309
310 SECTION( "string valid, w/ error check" )
311 {
312 mx::error_t errc;
313 unsigned long val = stoT<unsigned long>( "16", &errc );
314 REQUIRE( static_cast<unsigned long>( val ) == 16 );
315 REQUIRE( errc == mx::error_t::noerror );
316 }
317
318 SECTION( "positive overflow, w/ error check" )
319 {
320 mx::error_t errc;
321 unsigned long val = stoT<unsigned long>( "18523372036854775808", &errc );
322 REQUIRE( static_cast<unsigned long>( val ) == std::numeric_limits<unsigned long>::max() );
323 REQUIRE( errc == mx::error_t::erange );
324 }
325
326 SECTION( "invalid string, w/ error check" )
327 {
328 mx::error_t errc;
329 unsigned long val = stoT<unsigned long>( "?8", &errc );
330 REQUIRE( static_cast<unsigned long>( val ) == 0 );
331 REQUIRE( errc == mx::error_t::invalidarg );
332 }
333 }
334
335 SECTION( "a string long long" )
336 {
337 SECTION( "string valid, positive, no error check" )
338 {
339 long long val = stoT<long long>( "1000052" );
340 REQUIRE( static_cast<long long>( val ) == 1000052 );
341 }
342
343 SECTION( "string valid, negative, w/ error check" )
344 {
345 mx::error_t errc;
346 long long val = stoT<long long>( "-2300000", &errc );
347 REQUIRE( static_cast<long long>( val ) == -2300000 );
348 REQUIRE( errc == mx::error_t::noerror );
349 }
350
351 SECTION( "positive overflow, w/ error check" )
352 {
353 mx::error_t errc;
354 long long val = stoT<long long>( "9223372036854775808", &errc );
355 REQUIRE( static_cast<long long>( val ) == std::numeric_limits<long long>::max() );
356 REQUIRE( errc == mx::error_t::erange );
357 }
358
359 SECTION( "negative overflow, w/ error check" )
360 {
361 mx::error_t errc;
362 long long val = stoT<long long>( "-9223372036854775809", &errc );
363 REQUIRE( static_cast<long long>( val ) == std::numeric_limits<long long>::lowest() );
364 REQUIRE( errc == mx::error_t::erange );
365 }
366
367 SECTION( "invalid string, w/ error check" )
368 {
369 mx::error_t errc;
370 long long val = stoT<long long>( "-..8", &errc );
371 REQUIRE( static_cast<long long>( val ) == 0 );
372 REQUIRE( errc == mx::error_t::invalidarg );
373 }
374 }
375
376 SECTION( "a string unsigned long long" )
377 {
378 SECTION( "string valid, positive, no error check" )
379 {
380 unsigned long long val = stoT<unsigned long long>( "400000000000" );
381 REQUIRE( static_cast<unsigned long long>( val ) == 400000000000 );
382 }
383
384 SECTION( "string valid, w/ error check" )
385 {
386 mx::error_t errc;
387 unsigned long long val = stoT<unsigned long long>( "16", &errc );
388 REQUIRE( static_cast<unsigned long long>( val ) == 16 );
389 REQUIRE( errc == mx::error_t::noerror );
390 }
391
392 SECTION( "positive overflow, w/ error check" )
393 {
394 mx::error_t errc;
395 unsigned long long val = stoT<unsigned long long>( "18523372036854775808", &errc );
396 REQUIRE( static_cast<unsigned long long>( val ) == std::numeric_limits<unsigned long long>::max() );
397 REQUIRE( errc == mx::error_t::erange );
398 }
399
400 SECTION( "invalid string, w/ error check" )
401 {
402 mx::error_t errc;
403 unsigned long long val = stoT<unsigned long long>( "++9.2", &errc );
404 REQUIRE( static_cast<unsigned long long>( val ) == 0 );
405 REQUIRE( errc == mx::error_t::invalidarg );
406 }
407 }
408
409 SECTION( "a string bool" )
410 {
411 SECTION( "string valid, true, no error check" )
412 {
413 bool val = stoT<bool>( "true" );
414 REQUIRE( static_cast<bool>( val ) == true );
415 }
416
417 SECTION( "string valid, false, w/ error check" )
418 {
419 mx::error_t errc;
420 bool val = stoT<bool>( "false", &errc );
421 REQUIRE( static_cast<bool>( val ) == false );
422 REQUIRE( errc == mx::error_t::noerror );
423 }
424
425 SECTION( "string valid, t, w/ error check" )
426 {
427 mx::error_t errc;
428 bool val = stoT<bool>( "t", &errc );
429 REQUIRE( static_cast<bool>( val ) == true );
430 REQUIRE( errc == mx::error_t::noerror );
431 }
432
433 SECTION( "string valid, f, w/ error check" )
434 {
435 mx::error_t errc;
436 bool val = stoT<bool>( "false", &errc );
437 REQUIRE( static_cast<bool>( val ) == false );
438 REQUIRE( errc == mx::error_t::noerror );
439 }
440
441 SECTION( "string valid, 1, w/ error check" )
442 {
443 mx::error_t errc;
444 bool val = stoT<bool>( "1", &errc );
445 REQUIRE( static_cast<bool>( val ) == true );
446 REQUIRE( errc == mx::error_t::noerror );
447 }
448
449 SECTION( "string valid, 0, w/ error check" )
450 {
451 mx::error_t errc;
452 bool val = stoT<bool>( "0", &errc );
453 REQUIRE( static_cast<bool>( val ) == false );
454 REQUIRE( errc == mx::error_t::noerror );
455 }
456
457 SECTION( "string valid, number > 1, w/ error check" )
458 {
459 mx::error_t errc;
460 bool val = stoT<bool>( "237", &errc );
461 REQUIRE( static_cast<bool>( val ) == true );
462 REQUIRE( errc == mx::error_t::noerror );
463 }
464
465 SECTION( "string valid, -0, w/ error check" )
466 {
467 mx::error_t errc;
468 bool val = stoT<bool>( "-0.2", &errc );
469 REQUIRE( static_cast<bool>( val ) == false );
470 REQUIRE( errc == mx::error_t::noerror );
471 }
472
473 SECTION( "invalid string, w/ error check" )
474 {
475 mx::error_t errc;
476 bool val = stoT<bool>( "Xtrue", &errc );
477 REQUIRE( static_cast<bool>( val ) == 0 );
478 REQUIRE( errc == mx::error_t::invalidarg );
479 }
480
481 SECTION( "string valid w/ leading spaces, t, w/ error check" )
482 {
483 mx::error_t errc;
484 bool val = stoT<bool>( " t", &errc );
485 REQUIRE( static_cast<bool>( val ) == true );
486 REQUIRE( errc == mx::error_t::noerror );
487 }
488 }
489
490 SECTION( "a string float" )
491 {
492 SECTION( "string valid, positive, no error check" )
493 {
494 float val = stoT<float>( "2.567" );
495 REQUIRE_THAT( val, WithinRel( static_cast<float>( 2.567 ), std::numeric_limits<float>::epsilon() ) );
496 }
497
498 SECTION( "string valid, negative, w/ error check" )
499 {
500 mx::error_t errc;
501 float val = stoT<float>( "-2300000.897", &errc );
502 REQUIRE_THAT( val, WithinRel( static_cast<float>( -2300000.897 ), std::numeric_limits<float>::epsilon() ) );
503 REQUIRE( errc == mx::error_t::noerror );
504 }
505
506 SECTION( "positive overflow, w/ error check" )
507 {
508 mx::error_t errc;
509 float val = stoT<float>( "1e55", &errc );
510 // inf comparison won't work
511 REQUIRE( errc == mx::error_t::erange );
512 }
513
514 SECTION( "negative overflow, w/ error check" )
515 {
516 mx::error_t errc;
517 float val = stoT<float>( "-1e55", &errc );
518 // inf comparison won't work
519 REQUIRE( errc == mx::error_t::erange );
520 }
521
522 SECTION( "invalid string, w/ error check" )
523 {
524 mx::error_t errc;
525 float val = stoT<float>( "r5", &errc );
526 REQUIRE( static_cast<float>( val ) == 0 );
527 REQUIRE( errc == mx::error_t::invalidarg );
528 }
529
530 SECTION( "string valid w/ leading spaces, negative, w/ error check" )
531 {
532 mx::error_t errc;
533 float val = stoT<float>( " -2300000.897", &errc );
534 REQUIRE_THAT( val, WithinRel( static_cast<float>( -2300000.897 ), std::numeric_limits<float>::epsilon() ) );
535 REQUIRE( errc == mx::error_t::noerror );
536 }
537 }
538
539 SECTION( "a string double" )
540 {
541 SECTION( "string valid, positive, no error check" )
542 {
543 double val = stoT<double>( "22.2567" );
544 REQUIRE_THAT( val, WithinRel( static_cast<double>( 22.2567 ), std::numeric_limits<double>::epsilon() ) );
545 }
546
547 SECTION( "string valid, negative, w/ error check" )
548 {
549 mx::error_t errc;
550 double val = stoT<double>( "-2300000.897987", &errc );
551 REQUIRE_THAT( val,
552 WithinRel( static_cast<double>( -2300000.897987 ), std::numeric_limits<double>::epsilon() ) );
553 REQUIRE( errc == mx::error_t::noerror );
554 }
555
556 SECTION( "positive overflow, w/ error check" )
557 {
558 mx::error_t errc;
559 double val = stoT<double>( "1e400", &errc );
560 // inf comparison won't work
561 REQUIRE( errc == mx::error_t::erange );
562 }
563
564 SECTION( "negative overflow, w/ error check" )
565 {
566 mx::error_t errc;
567 double val = stoT<double>( "-1e400", &errc );
568 // inf comparison won't work
569 REQUIRE( errc == mx::error_t::erange );
570 }
571
572 SECTION( "invalid string, w/ error check" )
573 {
574 mx::error_t errc;
575 double val = stoT<double>( "+d", &errc );
576 REQUIRE( static_cast<double>( val ) == 0 );
577 REQUIRE( errc == mx::error_t::invalidarg );
578 }
579 }
580
581 SECTION( "a string long double" )
582 {
583 // These long double values don't seem quite right, should be able to use epsilon.
584 // new catch2 might work better
585 SECTION( "string valid, positive, no error check" )
586 {
587 long double val = stoT<long double>( "22.2567" );
588 REQUIRE_THAT( static_cast<double>( val ), Catch::Matchers::WithinRel( 22.2567, 1e-9 ) );
589 }
590
591 SECTION( "string valid, negative, w/ error check" )
592 {
593 mx::error_t errc;
594 long double val = stoT<long double>( "-2300000.897987", &errc );
595 REQUIRE_THAT( static_cast<double>( val ), Catch::Matchers::WithinRel( -2300000.897987, 1e-9 ) );
596 REQUIRE( errc == mx::error_t::noerror );
597 }
598
599 SECTION( "positive overflow, w/ error check" )
600 {
601 mx::error_t errc;
602 long double val = stoT<long double>( "1e10000", &errc );
603 // inf comparison won't work
604 REQUIRE( errc == mx::error_t::erange );
605 }
606
607 SECTION( "negative overflow, w/ error check" )
608 {
609 mx::error_t errc;
610 long double val = stoT<long double>( "-1e10000", &errc );
611 // inf comparison won't work
612 REQUIRE( errc == mx::error_t::erange );
613 }
614
615 SECTION( "invalid string, w/ error check" )
616 {
617 mx::error_t errc;
618 long double val = stoT<long double>( "+d", &errc );
619 REQUIRE( static_cast<long double>( val ) == 0 );
620 REQUIRE( errc == mx::error_t::invalidarg );
621 }
622 }
623}
624
625/** \brief Verifies numeric vector parsing for the delimiter forms used by HCI reduction headers.
626 *
627 * \ingroup stringUtils_unit_tests
628 */
629TEST_CASE( "Parsing delimited numeric vectors", "[ioutils::stringUtils::parseStringVector]" )
630{
631 std::vector<int> modes{ 99 };
632 parseStringVector( modes, "5,10,15", "," );
633 REQUIRE( modes == std::vector<int>{ 5, 10, 15 } );
634
635 std::vector<double> bounds{ 99.0 };
636 parseStringVector( bounds, "-1.5,0,2.25", "," );
637 REQUIRE( bounds == std::vector<double>{ -1.5, 0.0, 2.25 } );
638
639 std::vector<int> defaultDelimiter;
640 parseStringVector( defaultDelimiter, "1,2,3" );
641 REQUIRE( defaultDelimiter == std::vector<int>{ 1, 2, 3 } );
642
643 std::vector<int> characterDelimiter;
644 parseStringVector( characterDelimiter, "4;5;6", ';' );
645 REQUIRE( characterDelimiter == std::vector<int>{ 4, 5, 6 } );
646
647 std::vector<int> delimiterSet;
648 parseStringVector( delimiterSet, "1:2 3", ": " );
649 REQUIRE( delimiterSet == std::vector<int>{ 1, 2, 3 } );
650
651 parseStringVector( delimiterSet, "42", "," );
652 REQUIRE( delimiterSet == std::vector<int>{ 42 } );
653}
654
655} // namespace stringUtilsTest
656} // namespace ioutilsTest
657} // namespace unitTest
error_t
The mxlib error codes.
Definition error_t.hpp:26
@ noerror
No error has occurred.
Definition error_t.hpp:27
@ erange
Numerical result out of range (ERANGE).
Definition error_t.hpp:138
@ invalidarg
An argument was invalid.
Definition error_t.hpp:29
TEST_CASE("Converting strings to numbers", "[ioutils::stringUtils]")
Converting strings to numbers.
void parseStringVector(std::vector< typeT > &v, const std::string &s, char delim=',')
Parses a string into a vector of tokens delimited by a character.
typeT stoT(const std::string &str, error_t *errc=nullptr)
Convert a string to a numerical value.
The mxlib c++ namespace.
Definition mxlib.hpp:37
Utilities for working with strings.