| 1 | import strconv |
| 2 | |
| 3 | struct StrInt { // test struct |
| 4 | str_value string |
| 5 | int_value int |
| 6 | } |
| 7 | |
| 8 | // test what should be caught by atoi_common_check |
| 9 | fn test_common_check() { |
| 10 | // Parsing of these strings should fail on all types. |
| 11 | ko := [ |
| 12 | '', // Empty string |
| 13 | '-', // Only sign |
| 14 | '+', // Only sign |
| 15 | '_', // Only Underscore |
| 16 | '_10', // Start with underscore |
| 17 | '+_10', // Start with underscore after sign. |
| 18 | '-_16', // Start with underscore after sign. |
| 19 | '123_', // End with underscore |
| 20 | ] |
| 21 | |
| 22 | for v in ko { |
| 23 | if r := strconv.atoi(v) { |
| 24 | // These conversions should fail so force assertion ! |
| 25 | assert false, 'The string "${v}" should not succeed or be considered as valid ${r}).' |
| 26 | } else { |
| 27 | // println('Parsing fails as it should for : "${v}') |
| 28 | assert true |
| 29 | } |
| 30 | } |
| 31 | } |
| 32 | |
| 33 | // Test things accepted, and rejected in atoi_common function. |
| 34 | fn test_atoi_common() { |
| 35 | // Parsing of theses value should succeed on all types. |
| 36 | ok := [ |
| 37 | StrInt{'1', 1}, |
| 38 | StrInt{'-1', -1}, |
| 39 | StrInt{'0', 0}, |
| 40 | StrInt{'+0', 0}, |
| 41 | StrInt{'-0', 0}, |
| 42 | StrInt{'-0_00', 0}, |
| 43 | StrInt{'+0_00', 0}, |
| 44 | StrInt{'+1', 1}, |
| 45 | StrInt{'+123', 123}, |
| 46 | StrInt{'-1_2_1', -121}, |
| 47 | StrInt{'00000006', 6}, |
| 48 | StrInt{'0_0_0_0_0_0_0_6', 6}, |
| 49 | ] |
| 50 | |
| 51 | // Check that extracted int value matches its string. |
| 52 | for v in ok { |
| 53 | // println('Parsing ${v.str_value} should equals ${v.int_value}') |
| 54 | assert strconv.atoi(v.str_value)! == v.int_value |
| 55 | } |
| 56 | |
| 57 | ko := [// Parsing of these strings should fail on all types. |
| 58 | '-3__1', // Two consecutives underscore. |
| 59 | '-3_1A', // Non radix 10 char. |
| 60 | 'A42', // Non radix 10 char. |
| 61 | ] |
| 62 | |
| 63 | for v in ko { |
| 64 | if r := strconv.atoi(v) { |
| 65 | // These conversions should fail so force assertion ! |
| 66 | assert false, 'The string ${v} int extraction should not succeed or be considered as valid ${r}).' |
| 67 | } else { |
| 68 | // println('Parsing fails as it should for : "${v}') |
| 69 | assert true |
| 70 | } |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | // performs numeric (bounds) tests over int type. |
| 75 | fn test_atoi() { |
| 76 | ok := [ |
| 77 | StrInt{'1', 1}, |
| 78 | StrInt{'-1', -1}, |
| 79 | StrInt{'0', 0}, |
| 80 | StrInt{'+3_14159', 314159}, |
| 81 | StrInt{'-1_00_1', -1001}, |
| 82 | StrInt{'-1_024', -1024}, |
| 83 | StrInt{'123_456_789', 123456789}, |
| 84 | StrInt{'00000006', 6}, |
| 85 | StrInt{'0_0_0_0_0_0_0_6', 6}, |
| 86 | StrInt{'2147483647', max_i32}, |
| 87 | StrInt{'-2147483648', min_i32}, |
| 88 | ] |
| 89 | |
| 90 | // Check that extracted int value matches its string. |
| 91 | for v in ok { |
| 92 | // println('Parsing ${v.str_value} should equals ${v.int_value}') |
| 93 | assert strconv.atoi(v.str_value)! == v.int_value |
| 94 | } |
| 95 | |
| 96 | // Parsing of these values should fail ! |
| 97 | ko := [ |
| 98 | '-2147483649', // 32bits underflow by 1. |
| 99 | '+2147483648', // 32 bit overflow by 1. |
| 100 | '+3147483648', // 32 bit overflow by a lot. |
| 101 | '-2147244836470', // Large underflow. |
| 102 | '+86842255899621148766244', |
| 103 | ] |
| 104 | |
| 105 | for v in ko { |
| 106 | if r := strconv.atoi(v) { |
| 107 | // These conversions should fail so force assertion ! |
| 108 | assert false, 'The string ${v} int extraction should not succeed or be considered as valid ${r}).' |
| 109 | } else { |
| 110 | // println('Parsing fails as it should for : "${v}') |
| 111 | assert true |
| 112 | } |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | // performs numeric (bounds) tests over i8 type. |
| 117 | fn test_atoi8() { |
| 118 | struct StrI8 { // Inner test struct |
| 119 | str_value string |
| 120 | int_value i8 |
| 121 | } |
| 122 | |
| 123 | ok := [ |
| 124 | StrI8{'0', 0}, // All kind of zeroes |
| 125 | StrI8{'+0', 0}, |
| 126 | StrI8{'-0', 0}, |
| 127 | StrI8{'-0_00', 0}, |
| 128 | StrI8{'+0_00', 0}, |
| 129 | StrI8{'1', 1}, |
| 130 | StrI8{'+1', 1}, |
| 131 | StrI8{'-1', -1}, |
| 132 | StrI8{'+123', 123}, |
| 133 | StrI8{'-1_2_1', -121}, |
| 134 | StrI8{'0_0_0_0_0_0_0_6', 6}, |
| 135 | StrI8{'127', max_i8}, |
| 136 | StrI8{'-128', min_i8}, |
| 137 | ] |
| 138 | |
| 139 | // Check that extracted int value matches its string. |
| 140 | for v in ok { |
| 141 | // println('Parsing ${v.str_value} should equals ${v.int_value}') |
| 142 | assert strconv.atoi8(v.str_value)! == v.int_value |
| 143 | } |
| 144 | |
| 145 | // Parsing of these values should fail ! |
| 146 | ko := [ |
| 147 | '-129', // i8 bits underflow by 1. |
| 148 | '+128', // i8 bit overflow by 1. |
| 149 | '+256', // i8 overflow with value equal to max u8. |
| 150 | '+3147483648', // i8 bit overflow by a lot. |
| 151 | '-4836470', // Large i8 underflow. |
| 152 | ] |
| 153 | |
| 154 | for v in ko { |
| 155 | if r := strconv.atoi8(v) { |
| 156 | // These conversions should fail so force assertion ! |
| 157 | assert false, 'The string ${v} int extraction should not succeed or be considered as valid ${r}).' |
| 158 | } else { |
| 159 | // println('Parsing fails as it should for : "${v}') |
| 160 | assert true |
| 161 | } |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | // performs numeric (bounds) tests over i16 type. |
| 166 | fn test_atoi16() { |
| 167 | struct StrI16 { // Inner test struct |
| 168 | str_value string |
| 169 | int_value i16 |
| 170 | } |
| 171 | |
| 172 | ok := [ |
| 173 | StrI16{'0', 0}, // All kind of zeroes |
| 174 | StrI16{'+0', 0}, |
| 175 | StrI16{'-0', 0}, |
| 176 | StrI16{'-0_00', 0}, |
| 177 | StrI16{'+0_00', 0}, |
| 178 | StrI16{'1', 1}, |
| 179 | StrI16{'+1', 1}, |
| 180 | StrI16{'-1', -1}, |
| 181 | StrI16{'+123', 123}, |
| 182 | StrI16{'-1_2_1', -121}, |
| 183 | StrI16{'0_0_0_0_0_0_0_6', 6}, |
| 184 | StrI16{'32767', max_i16}, |
| 185 | StrI16{'-32768', min_i16}, |
| 186 | ] |
| 187 | |
| 188 | // Check that extracted int value matches its string. |
| 189 | for v in ok { |
| 190 | // println('Parsing ${v.str_value} should equals ${v.int_value}') |
| 191 | assert strconv.atoi16(v.str_value)! == v.int_value |
| 192 | } |
| 193 | |
| 194 | // Parsing of these values should fail ! |
| 195 | ko := [ |
| 196 | '-32769', // i16 bits underflow by 1. |
| 197 | '+32768', // i16 bit overflow by 1. |
| 198 | '+45_000', // i16 bit overflow by a lot. |
| 199 | '65536', // i16 overflow with value equal to u16 max. |
| 200 | '-483_647_909', // Large i16 underflow. |
| 201 | ] |
| 202 | |
| 203 | for v in ko { |
| 204 | if r := strconv.atoi16(v) { |
| 205 | // These conversions should fail so force assertion ! |
| 206 | assert false, 'The string ${v} int extraction should not succeed or be considered as valid ${r}).' |
| 207 | } else { |
| 208 | // println('Parsing fails as it should for : "${v}') |
| 209 | assert true |
| 210 | } |
| 211 | } |
| 212 | } |
| 213 | |
| 214 | // performs numeric (bounds) tests over i32 type. This test is redundant with atoi |
| 215 | // which performs same on int (actually 32bits). In the future, int COULD be mapped |
| 216 | // on arch with (e.g 64bits). That's why this test exists. |
| 217 | fn test_atoi32() { |
| 218 | struct StrI32 { // Inner test struct |
| 219 | str_value string |
| 220 | int_value i32 |
| 221 | } |
| 222 | |
| 223 | ok := [ |
| 224 | StrI32{'0', 0}, // All kind of zeroes |
| 225 | StrI32{'+0', 0}, |
| 226 | StrI32{'-0', 0}, |
| 227 | StrI32{'-0_00', 0}, |
| 228 | StrI32{'+0_00', 0}, |
| 229 | StrI32{'1', 1}, |
| 230 | StrI32{'+1', 1}, |
| 231 | StrI32{'-1', -1}, |
| 232 | StrI32{'+123', 123}, |
| 233 | StrI32{'-1_2_1', -121}, |
| 234 | StrI32{'0_0_0_0_0_0_0_6', 6}, |
| 235 | StrI32{'2147483647', max_i32}, |
| 236 | StrI32{'-2147483648', min_i32}, |
| 237 | ] |
| 238 | |
| 239 | // Check that extracted int value matches its string. |
| 240 | for v in ok { |
| 241 | // println('Parsing ${v.str_value} should equals ${v.int_value}') |
| 242 | assert strconv.atoi32(v.str_value)! == v.int_value |
| 243 | } |
| 244 | |
| 245 | // Parsing of these values should fail ! |
| 246 | ko := [ |
| 247 | '-2147483649', // i32 bits underflow by 1. |
| 248 | '+2147483648', // i32 bit overflow by 1. |
| 249 | '+4294967295', // Large Overflow but equal to u32 max. |
| 250 | '-483_647_909_912_754', // Large i32 underflow. |
| 251 | ] |
| 252 | |
| 253 | for v in ko { |
| 254 | if r := strconv.atoi32(v) { |
| 255 | // These conversions should fail so force assertion ! |
| 256 | assert false, 'The string ${v} int extraction should not succeed or be considered as valid ${r}).' |
| 257 | } else { |
| 258 | // println('Parsing fails as it should for : "${v}') |
| 259 | assert true |
| 260 | } |
| 261 | } |
| 262 | } |
| 263 | |
| 264 | fn test_atoi64() { |
| 265 | struct StrI64 { // Inner test struct |
| 266 | str_value string |
| 267 | int_value i64 |
| 268 | } |
| 269 | |
| 270 | ok := [ |
| 271 | StrI64{'0', 0}, // All kind of zeroes |
| 272 | StrI64{'+0', 0}, |
| 273 | StrI64{'-0', 0}, |
| 274 | StrI64{'-0_00', 0}, |
| 275 | StrI64{'+0_00', 0}, |
| 276 | StrI64{'1', 1}, |
| 277 | StrI64{'+1', 1}, |
| 278 | StrI64{'-1', -1}, |
| 279 | StrI64{'+123', 123}, |
| 280 | StrI64{'-1_2_1', -121}, |
| 281 | StrI64{'0_0_0_0_0_0_0_6', 6}, |
| 282 | StrI64{'9223372036854775807', max_i64}, |
| 283 | StrI64{'-9223372036854775808', min_i64}, |
| 284 | ] |
| 285 | |
| 286 | // Check that extracted int value matches its string. |
| 287 | for v in ok { |
| 288 | // println('Parsing ${v.str_value} should equals ${v.int_value}') |
| 289 | assert strconv.atoi64(v.str_value)! == v.int_value |
| 290 | } |
| 291 | |
| 292 | // Parsing of these values should fail ! |
| 293 | ko := [ |
| 294 | '-9223372036854775809', // i64 bits underflow by 1. |
| 295 | '+9223372036854775808', // i64 bit overflow by 1. |
| 296 | '+18446744073709551615', // Large Overflow but equal to u64 max. |
| 297 | '-483647909912754123456789', // Large i64 underflow. |
| 298 | ] |
| 299 | |
| 300 | for v in ko { |
| 301 | if r := strconv.atoi64(v) { |
| 302 | // These conversions should fail so force assertion ! |
| 303 | assert false, 'The string ${v} int extraction should not succeed or be considered as valid ${r}).' |
| 304 | } else { |
| 305 | // println('Parsing fails as it should for : "${v}') |
| 306 | assert true |
| 307 | } |
| 308 | } |
| 309 | } |
| 310 | |
| 311 | fn test_parse_int() { |
| 312 | // symbols coverage |
| 313 | assert strconv.parse_int('1234567890', 10, 32)! == 1234567890 |
| 314 | assert strconv.parse_int('19aAbBcCdDeEfF', 16, 64)! == 0x19aAbBcCdDeEfF |
| 315 | // Different bases |
| 316 | assert strconv.parse_int('16', 16, 0)! == 0x16 |
| 317 | assert strconv.parse_int('16', 8, 0)! == 0o16 |
| 318 | assert strconv.parse_int('11', 2, 0)! == 3 |
| 319 | // Different bit sizes |
| 320 | assert strconv.parse_int('127', 10, 8)! == 127 |
| 321 | assert strconv.parse_int('128', 10, 8)! == 127 |
| 322 | assert strconv.parse_int('32767', 10, 16)! == 32767 |
| 323 | assert strconv.parse_int('32768', 10, 16)! == 32767 |
| 324 | assert strconv.parse_int('2147483647', 10, 32)! == 2147483647 |
| 325 | assert strconv.parse_int('2147483648', 10, 32)! == 2147483647 |
| 326 | assert strconv.parse_int('9223372036854775807', 10, 64)! == 9223372036854775807 |
| 327 | assert strconv.parse_int('9223372036854775808', 10, 64)! == 9223372036854775807 |
| 328 | assert strconv.parse_int('baobab', 36, 64)! == 683058467 |
| 329 | // Invalid bit sizes |
| 330 | if x := strconv.parse_int('123', 10, -1) { |
| 331 | println(x) |
| 332 | assert false |
| 333 | } else { |
| 334 | assert true |
| 335 | } |
| 336 | if x := strconv.parse_int('123', 10, 65) { |
| 337 | println(x) |
| 338 | assert false |
| 339 | } else { |
| 340 | assert true |
| 341 | } |
| 342 | } |
| 343 | |
| 344 | fn test_common_parse_int_error_on_high_digit() { |
| 345 | assert strconv.common_parse_int('9223372036854775808', 0, 64, false, true) or { 1 } == 1 |
| 346 | assert strconv.common_parse_int('-9223372036854775809', 0, 64, false, true) or { 1 } == 1 |
| 347 | assert strconv.common_parse_int('9223372036854775808', 0, 64, false, false) or { 1 } == max_i64 |
| 348 | assert strconv.common_parse_int('-9223372036854775809', 0, 64, false, false) or { 1 } == min_i64 |
| 349 | } |
| 350 | |
| 351 | fn test_common_parse_uint2() { |
| 352 | mut result, mut error := strconv.common_parse_uint2('1', 10, 8) |
| 353 | assert result == 1 |
| 354 | assert error == 0 |
| 355 | result, error = strconv.common_parse_uint2('123', 10, 8) |
| 356 | assert result == 123 |
| 357 | assert error == 0 |
| 358 | result, error = strconv.common_parse_uint2('123', 10, 65) |
| 359 | assert result == 0 |
| 360 | assert error == -2 |
| 361 | result, error = strconv.common_parse_uint2('123', 10, -1) |
| 362 | assert result == 0 |
| 363 | assert error == -2 |
| 364 | result, error = strconv.common_parse_uint2('', 10, 8) |
| 365 | assert result == 0 |
| 366 | assert error == 1 |
| 367 | result, error = strconv.common_parse_uint2('1a', 10, 8) |
| 368 | assert result == 1 |
| 369 | assert error == 2 |
| 370 | result, error = strconv.common_parse_uint2('12a', 10, 8) |
| 371 | assert result == 12 |
| 372 | assert error == 3 |
| 373 | result, error = strconv.common_parse_uint2('123a', 10, 8) |
| 374 | assert result == 123 |
| 375 | assert error == 4 |
| 376 | } |
| 377 | |
| 378 | fn test_common_parse_uint2_fail() { |
| 379 | mut ascii_characters := [' ', '!', '"', '#', '\$', '%', '&', "'", '(', ')', '*', '+', ',', |
| 380 | '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', |
| 381 | '}', '~'] |
| 382 | mut special_characters := [':', ';', '<', '=', '>', '?', '@', 'X', 'Y', 'Z', '[', '\\', ']', |
| 383 | '^', '_', '`'] |
| 384 | |
| 385 | num0, err0 := strconv.common_parse_uint2('1Ab', 16, 32) |
| 386 | assert num0 == 427 |
| 387 | assert err0 == 0 |
| 388 | |
| 389 | for ch in ascii_characters { |
| 390 | // println("ch: [${ch}]") |
| 391 | txt_str := '${ch[0]:c}12Ab' |
| 392 | num, err := strconv.common_parse_uint2(txt_str, 16, 32) |
| 393 | assert err != 0 |
| 394 | } |
| 395 | |
| 396 | for ch in special_characters { |
| 397 | // println("ch: [${ch}]") |
| 398 | txt_str := '${ch[0]:c}12Ab' |
| 399 | num, err := strconv.common_parse_uint2(txt_str, 16, 32) |
| 400 | assert err != 0 |
| 401 | } |
| 402 | } |
| 403 | |
| 404 | fn test_common_parse_uint2_compatibility() { |
| 405 | test_list := [ |
| 406 | '1234,1234', |
| 407 | '1_234,1234', |
| 408 | '1_2_34,1234', |
| 409 | '_12__34,1', |
| 410 | '12__34,1', |
| 411 | '_1234,1', |
| 412 | '1234_,1', |
| 413 | '0x1234,4660', |
| 414 | '0x_1234,4660', |
| 415 | '0x1_234,4660', |
| 416 | '0x1_2_3_4,4660', |
| 417 | '0_x1234,1', |
| 418 | '0x1234_,1', |
| 419 | '0o1234,668', |
| 420 | '0o_1234,668', |
| 421 | '0o1_234,668', |
| 422 | '0o1_2_3_4,668', |
| 423 | '0_o1234,1', |
| 424 | '0o1234_,1', |
| 425 | '0b111,7', |
| 426 | '0b_111,7', |
| 427 | '0b1_11,7', |
| 428 | '0b1_1_1,7', |
| 429 | '0_b111,1', |
| 430 | '0b111_,1', |
| 431 | '0xa,10', |
| 432 | '0xA,10', |
| 433 | '0xf,15', |
| 434 | '0xf,15', |
| 435 | '0_xf,1', |
| 436 | '0x_0_0_f_,1', |
| 437 | '0x_0_0__f,1', |
| 438 | '0x_0_0_f,15', |
| 439 | ] |
| 440 | |
| 441 | for tst in test_list { |
| 442 | query := tst.split(',') |
| 443 | mut a0 := strconv.common_parse_uint(query[0], 0, 32, true, true) or { 1 } |
| 444 | // println("${a0} => ${query[1]}") |
| 445 | assert a0.str() == query[1] |
| 446 | } |
| 447 | } |
| 448 | |