[m-rev.] for post-commit review: improve test coverage of base_string_to_{int, uint}/3
Julien Fischer
jfischer at opturion.com
Wed Aug 19 12:54:15 AEST 2026
Improve test coverage of base_string_to_{int,uint}/3.
The existing test coverage of base_string_to_int/3 is pretty minimal, and that
of base_string_to_uint/3 is almost non-existent. Recent changes have
improved test coverage for the overflow-related aspects of these predicates.
This diff improves test coverage for the non-overflow-related aspects.
tests/hard_coded/Mmakefile:
tests/hard_coded/base_string_to_{int,uint}.{m,exp}:
As above.
Julien.
diff --git a/tests/hard_coded/Mmakefile b/tests/hard_coded/Mmakefile
index 8a5d3a180..cfadcbbb2 100644
--- a/tests/hard_coded/Mmakefile
+++ b/tests/hard_coded/Mmakefile
@@ -775,6 +775,8 @@ ifeq "$(findstring profdeep,$(GRADE))" ""
array_resize \
array_shrink \
array_swap \
+ base_string_to_int \
+ base_string_to_uint \
bit_access_uint16 \
bit_access_uint32 \
bit_access_uint64 \
diff --git a/tests/hard_coded/base_string_to_int.exp
b/tests/hard_coded/base_string_to_int.exp
new file mode 100644
index 000000000..a1395cc88
--- /dev/null
+++ b/tests/hard_coded/base_string_to_int.exp
@@ -0,0 +1,178 @@
+### Empty, blank and sign-only strings ###
+
+base_string_to_int(10, "") ==> no
+base_string_to_int(2, "") ==> no
+base_string_to_int(36, "") ==> no
+base_string_to_int(10, " ") ==> no
+base_string_to_int(2, " ") ==> no
+base_string_to_int(36, " ") ==> no
+base_string_to_int(10, "-") ==> no
+base_string_to_int(10, "+") ==> no
+base_string_to_int(16, "-") ==> no
+base_string_to_int(2, "+") ==> no
+base_string_to_int(10, "--") ==> no
+base_string_to_int(10, "++") ==> no
+base_string_to_int(10, "+-1") ==> no
+base_string_to_int(10, "-+1") ==> no
+base_string_to_int(10, " ") ==> no
+base_string_to_int(10, "\t") ==> no
+base_string_to_int(10, "\n") ==> no
+base_string_to_int(10, " \t ") ==> no
+base_string_to_int(10, "- ") ==> no
+base_string_to_int(10, "+ ") ==> no
+
+### Single digits ###
+
+base_string_to_int(10, "0") ==> 0
+base_string_to_int(10, "7") ==> 7
+base_string_to_int(10, "-7") ==> -7
+base_string_to_int(10, "+7") ==> 7
+base_string_to_int(2, "0") ==> 0
+base_string_to_int(2, "1") ==> 1
+base_string_to_int(8, "7") ==> 7
+base_string_to_int(16, "f") ==> 15
+base_string_to_int(16, "F") ==> 15
+base_string_to_int(36, "z") ==> 35
+base_string_to_int(36, "Z") ==> 35
+
+### Upper and lower case digits ###
+
+base_string_to_int(16, "abcdef") ==> 11259375
+base_string_to_int(16, "ABCDEF") ==> 11259375
+base_string_to_int(16, "AbCdEf") ==> 11259375
+base_string_to_int(16, "-Ff") ==> -255
+base_string_to_int(36, "abz") ==> 13391
+base_string_to_int(36, "ABZ") ==> 13391
+base_string_to_int(36, "AbZ") ==> 13391
+
+### Leading zeros ###
+
+base_string_to_int(10, "00") ==> 0
+base_string_to_int(10, "007") ==> 7
+base_string_to_int(10, "-007") ==> -7
+base_string_to_int(10, "+007") ==> 7
+base_string_to_int(2, "000101") ==> 5
+base_string_to_int(8, "00017") ==> 15
+base_string_to_int(16, "000ff") ==> 255
+base_string_to_int(36, "000z") ==> 35
+base_string_to_int(10, "0000000000000000000000000000000") ==> 0
+base_string_to_int(10, "0000000000000000000000000000001") ==> 1
+
+### Digits not valid in the specified base ###
+
+base_string_to_int(2, "1") ==> 1
+base_string_to_int(2, "2") ==> no
+base_string_to_int(3, "2") ==> 2
+base_string_to_int(3, "3") ==> no
+base_string_to_int(8, "7") ==> 7
+base_string_to_int(8, "8") ==> no
+base_string_to_int(9, "8") ==> 8
+base_string_to_int(9, "9") ==> no
+base_string_to_int(10, "9") ==> 9
+base_string_to_int(10, "a") ==> no
+base_string_to_int(10, "A") ==> no
+base_string_to_int(11, "a") ==> 10
+base_string_to_int(11, "A") ==> 10
+base_string_to_int(11, "b") ==> no
+base_string_to_int(11, "B") ==> no
+base_string_to_int(16, "f") ==> 15
+base_string_to_int(16, "g") ==> no
+base_string_to_int(16, "G") ==> no
+base_string_to_int(17, "g") ==> 16
+base_string_to_int(17, "h") ==> no
+base_string_to_int(35, "y") ==> 34
+base_string_to_int(35, "z") ==> no
+base_string_to_int(35, "Z") ==> no
+base_string_to_int(36, "z") ==> 35
+base_string_to_int(36, "Z") ==> 35
+base_string_to_int(2, "102") ==> no
+base_string_to_int(8, "178") ==> no
+base_string_to_int(16, "1fg") ==> no
+
+### Non-digit characters ###
+
+base_string_to_int(10, " 1") ==> no
+base_string_to_int(10, "1 ") ==> no
+base_string_to_int(10, "1 2") ==> no
+base_string_to_int(10, "1.0") ==> no
+base_string_to_int(10, "1,000") ==> no
+base_string_to_int(10, "1_000") ==> no
+base_string_to_int(10, "1e5") ==> no
+base_string_to_int(16, "0x10") ==> no
+base_string_to_int(10, "\t1") ==> no
+base_string_to_int(10, "1\n") ==> no
+base_string_to_int(36, "1!") ==> no
+base_string_to_int(10, "3") ==> no
+
+### Signs in the wrong place ###
+
+base_string_to_int(10, "1-") ==> no
+base_string_to_int(10, "1+") ==> no
+base_string_to_int(10, "1-2") ==> no
+base_string_to_int(10, "12-34") ==> no
+base_string_to_int(10, "--1") ==> no
+base_string_to_int(10, "++1") ==> no
+base_string_to_int(10, "-1-") ==> no
+
+### The string "10" in every base ###
+
+base_string_to_int(2, "10") ==> 2
+base_string_to_int(3, "10") ==> 3
+base_string_to_int(4, "10") ==> 4
+base_string_to_int(5, "10") ==> 5
+base_string_to_int(6, "10") ==> 6
+base_string_to_int(7, "10") ==> 7
+base_string_to_int(8, "10") ==> 8
+base_string_to_int(9, "10") ==> 9
+base_string_to_int(10, "10") ==> 10
+base_string_to_int(11, "10") ==> 11
+base_string_to_int(12, "10") ==> 12
+base_string_to_int(13, "10") ==> 13
+base_string_to_int(14, "10") ==> 14
+base_string_to_int(15, "10") ==> 15
+base_string_to_int(16, "10") ==> 16
+base_string_to_int(17, "10") ==> 17
+base_string_to_int(18, "10") ==> 18
+base_string_to_int(19, "10") ==> 19
+base_string_to_int(20, "10") ==> 20
+base_string_to_int(21, "10") ==> 21
+base_string_to_int(22, "10") ==> 22
+base_string_to_int(23, "10") ==> 23
+base_string_to_int(24, "10") ==> 24
+base_string_to_int(25, "10") ==> 25
+base_string_to_int(26, "10") ==> 26
+base_string_to_int(27, "10") ==> 27
+base_string_to_int(28, "10") ==> 28
+base_string_to_int(29, "10") ==> 29
+base_string_to_int(30, "10") ==> 30
+base_string_to_int(31, "10") ==> 31
+base_string_to_int(32, "10") ==> 32
+base_string_to_int(33, "10") ==> 33
+base_string_to_int(34, "10") ==> 34
+base_string_to_int(35, "10") ==> 35
+base_string_to_int(36, "10") ==> 36
+
+### Invalid bases ###
+
+base_string_to_int(1, "1") ==> exception
+base_string_to_int(0, "1") ==> exception
+base_string_to_int(-1, "1") ==> exception
+base_string_to_int(37, "1") ==> exception
+base_string_to_int(100, "1") ==> exception
+
+### det_base_string_to_int ###
+
+det_base_string_to_int(10, "123") ==> 123
+det_base_string_to_int(16, "ff") ==> 255
+det_base_string_to_int(10, "") ==> exception
+det_base_string_to_int(10, "12a") ==> exception
+det_base_string_to_int(1, "1") ==> exception
+
+### to_int ###
+
+to_int("123") ==> 123
+to_int("-123") ==> -123
+to_int("") ==> no
+to_int("0x10") ==> no
+to_int(" 1") ==> no
+
diff --git a/tests/hard_coded/base_string_to_int.m
b/tests/hard_coded/base_string_to_int.m
new file mode 100644
index 000000000..771767ce0
--- /dev/null
+++ b/tests/hard_coded/base_string_to_int.m
@@ -0,0 +1,253 @@
+%---------------------------------------------------------------------------%
+% vim: ts=4 sw=4 et ft=mercury
+%---------------------------------------------------------------------------%
+%
+% Test the non-overflow behaviour of string.base_string_to_int/3.
+% (The overflow behaviour of this predicate is tested separately, by
+% ../general/test_string_to_int_overflow.m.)
+%
+%---------------------------------------------------------------------------%
+
+:- module base_string_to_int.
+
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is cc_multi.
+
+%---------------------------------------------------------------------------%
+%---------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module exception.
+:- import_module int.
+:- import_module list.
+:- import_module maybe.
+:- import_module string.
+
+%---------------------------------------------------------------------------%
+
+main(!IO) :-
+ header("Empty, blank and sign-only strings", !IO),
+ test(10, "", !IO),
+ test(2, "", !IO),
+ test(36, "", !IO),
+ test(10, " ", !IO),
+ test(2, " ", !IO),
+ test(36, " ", !IO),
+ test(10, "-", !IO),
+ test(10, "+", !IO),
+ test(16, "-", !IO),
+ test(2, "+", !IO),
+ test(10, "--", !IO),
+ test(10, "++", !IO),
+ test(10, "+-1", !IO),
+ test(10, "-+1", !IO),
+ test(10, " ", !IO),
+ test(10, "\t", !IO),
+ test(10, "\n", !IO),
+ test(10, " \t ", !IO),
+ test(10, "- ", !IO),
+ test(10, "+ ", !IO),
+ io.nl(!IO),
+
+ header("Single digits", !IO),
+ test(10, "0", !IO),
+ test(10, "7", !IO),
+ test(10, "-7", !IO),
+ test(10, "+7", !IO),
+ test(2, "0", !IO),
+ test(2, "1", !IO),
+ test(8, "7", !IO),
+ test(16, "f", !IO),
+ test(16, "F", !IO),
+ test(36, "z", !IO),
+ test(36, "Z", !IO),
+ io.nl(!IO),
+
+ header("Upper and lower case digits", !IO),
+ test(16, "abcdef", !IO),
+ test(16, "ABCDEF", !IO),
+ test(16, "AbCdEf", !IO),
+ test(16, "-Ff", !IO),
+ test(36, "abz", !IO),
+ test(36, "ABZ", !IO),
+ test(36, "AbZ", !IO),
+ io.nl(!IO),
+
+ header("Leading zeros", !IO),
+ test(10, "00", !IO),
+ test(10, "007", !IO),
+ test(10, "-007", !IO),
+ test(10, "+007", !IO),
+ test(2, "000101", !IO),
+ test(8, "00017", !IO),
+ test(16, "000ff", !IO),
+ test(36, "000z", !IO),
+ test(10, "0000000000000000000000000000000", !IO),
+ test(10, "0000000000000000000000000000001", !IO),
+ io.nl(!IO),
+
+ header("Digits not valid in the specified base", !IO),
+ test(2, "1", !IO),
+ test(2, "2", !IO),
+ test(3, "2", !IO),
+ test(3, "3", !IO),
+ test(8, "7", !IO),
+ test(8, "8", !IO),
+ test(9, "8", !IO),
+ test(9, "9", !IO),
+ test(10, "9", !IO),
+ test(10, "a", !IO),
+ test(10, "A", !IO),
+ test(11, "a", !IO),
+ test(11, "A", !IO),
+ test(11, "b", !IO),
+ test(11, "B", !IO),
+ test(16, "f", !IO),
+ test(16, "g", !IO),
+ test(16, "G", !IO),
+ test(17, "g", !IO),
+ test(17, "h", !IO),
+ test(35, "y", !IO),
+ test(35, "z", !IO),
+ test(35, "Z", !IO),
+ test(36, "z", !IO),
+ test(36, "Z", !IO),
+ test(2, "102", !IO),
+ test(8, "178", !IO),
+ test(16, "1fg", !IO),
+ io.nl(!IO),
+
+ header("Non-digit characters", !IO),
+ test(10, " 1", !IO),
+ test(10, "1 ", !IO),
+ test(10, "1 2", !IO),
+ test(10, "1.0", !IO),
+ test(10, "1,000", !IO),
+ test(10, "1_000", !IO),
+ test(10, "1e5", !IO),
+ test(16, "0x10", !IO),
+ test(10, "\t1", !IO),
+ test(10, "1\n", !IO),
+ test(36, "1!", !IO),
+ test(10, "\uff13", !IO),
+ io.nl(!IO),
+
+ header("Signs in the wrong place", !IO),
+ test(10, "1-", !IO),
+ test(10, "1+", !IO),
+ test(10, "1-2", !IO),
+ test(10, "12-34", !IO),
+ test(10, "--1", !IO),
+ test(10, "++1", !IO),
+ test(10, "-1-", !IO),
+ io.nl(!IO),
+
+ header("The string \"10\" in every base", !IO),
+ list.foldl(test_10_in_base, 2 .. 36, !IO),
+ io.nl(!IO),
+
+ % base_string_to_int/3 throws an exception if the base is not in 2..36.
+ % It checks the base before it looks at the string, so the string is
+ % irrelevant in these tests.
+
+ header("Invalid bases", !IO),
+ test(1, "1", !IO),
+ test(0, "1", !IO),
+ test(-1, "1", !IO),
+ test(37, "1", !IO),
+ test(100, "1", !IO),
+ io.nl(!IO),
+
+ header("det_base_string_to_int", !IO),
+ test_det(10, "123", !IO),
+ test_det(16, "ff", !IO),
+ test_det(10, "", !IO),
+ test_det(10, "12a", !IO),
+ test_det(1, "1", !IO),
+ io.nl(!IO),
+
+ % to_int/2 is base_string_to_int/3 with the base fixed at 10.
+
+ header("to_int", !IO),
+ test_to_int("123", !IO),
+ test_to_int("-123", !IO),
+ test_to_int("", !IO),
+ test_to_int("0x10", !IO),
+ test_to_int(" 1", !IO),
+ io.nl(!IO).
+
+%---------------------------------------------------------------------------%
+
+:- pred test_10_in_base(int::in, io::di, io::uo) is cc_multi.
+
+test_10_in_base(Base, !IO) :-
+ test(Base, "10", !IO).
+
+%---------------------------------------------------------------------------%
+
+ % test(Base, Str, !IO):
+ %
+ % Test string.base_string_to_int(Base, Str, Int), writing out the call
+ % and then its result: the value of Int if the call succeeds, "no" if
+ % it fails, or "exception" if it throws.
+ %
+:- pred test(int::in, string::in, io::di, io::uo) is cc_multi.
+
+test(Base, Str, !IO) :-
+ io.format("base_string_to_int(%d, %s) ==> ",
+ [i(Base), s(string(Str))], !IO),
+ ( try []
+ ( if string.base_string_to_int(Base, Str, Int0) then
+ MaybeInt = yes(Int0)
+ else
+ MaybeInt = no
+ )
+ then
+ (
+ MaybeInt = yes(Int),
+ io.write_line(Int, !IO)
+ ;
+ MaybeInt = no,
+ io.write_string("no\n", !IO)
+ )
+ catch_any _ ->
+ io.write_string("exception\n", !IO)
+ ).
+
+:- pred test_det(int::in, string::in, io::di, io::uo) is cc_multi.
+
+test_det(Base, Str, !IO) :-
+ io.format("det_base_string_to_int(%d, %s) ==> ",
+ [i(Base), s(string(Str))], !IO),
+ ( try []
+ Int = string.det_base_string_to_int(Base, Str)
+ then
+ io.write_line(Int, !IO)
+ catch_any _ ->
+ io.write_string("exception\n", !IO)
+ ).
+
+:- pred test_to_int(string::in, io::di, io::uo) is det.
+
+test_to_int(Str, !IO) :-
+ io.format("to_int(%s) ==> ", [s(string(Str))], !IO),
+ ( if string.to_int(Str, Int) then
+ io.write_line(Int, !IO)
+ else
+ io.write_string("no\n", !IO)
+ ).
+
+%---------------------------------------------------------------------------%
+
+:- pred header(string::in, io::di, io::uo) is det.
+
+header(Title, !IO) :-
+ io.format("### %s ###\n\n", [s(Title)], !IO).
+
+%---------------------------------------------------------------------------%
+:- end_module base_string_to_int.
+%---------------------------------------------------------------------------%
diff --git a/tests/hard_coded/base_string_to_uint.exp
b/tests/hard_coded/base_string_to_uint.exp
new file mode 100644
index 000000000..fe4ad8bbf
--- /dev/null
+++ b/tests/hard_coded/base_string_to_uint.exp
@@ -0,0 +1,168 @@
+### Empty and blank strings ###
+
+base_string_to_uint(10, "") ==> no
+base_string_to_uint(2, "") ==> no
+base_string_to_uint(36, "") ==> no
+base_string_to_uint(10, " ") ==> no
+base_string_to_uint(10, " ") ==> no
+base_string_to_uint(10, "\t") ==> no
+base_string_to_uint(10, "\n") ==> no
+base_string_to_uint(10, " \t ") ==> no
+base_string_to_uint(2, " ") ==> no
+base_string_to_uint(36, " ") ==> no
+
+### Signs are not accepted ###
+
+base_string_to_uint(10, "-1") ==> no
+base_string_to_uint(10, "+1") ==> no
+base_string_to_uint(10, "-0") ==> no
+base_string_to_uint(10, "+0") ==> no
+base_string_to_uint(10, "-") ==> no
+base_string_to_uint(10, "+") ==> no
+base_string_to_uint(16, "-f") ==> no
+base_string_to_uint(36, "+z") ==> no
+base_string_to_uint(10, "--1") ==> no
+base_string_to_uint(10, "1-") ==> no
+base_string_to_uint(10, "1+") ==> no
+
+### Single digits ###
+
+base_string_to_uint(10, "0") ==> 0u
+base_string_to_uint(10, "7") ==> 7u
+base_string_to_uint(2, "0") ==> 0u
+base_string_to_uint(2, "1") ==> 1u
+base_string_to_uint(8, "7") ==> 7u
+base_string_to_uint(16, "f") ==> 15u
+base_string_to_uint(16, "F") ==> 15u
+base_string_to_uint(36, "z") ==> 35u
+base_string_to_uint(36, "Z") ==> 35u
+
+### Upper and lower case digits ###
+
+base_string_to_uint(16, "abcdef") ==> 11259375u
+base_string_to_uint(16, "ABCDEF") ==> 11259375u
+base_string_to_uint(16, "AbCdEf") ==> 11259375u
+base_string_to_uint(36, "abz") ==> 13391u
+base_string_to_uint(36, "ABZ") ==> 13391u
+base_string_to_uint(36, "AbZ") ==> 13391u
+
+### Leading zeros ###
+
+base_string_to_uint(10, "00") ==> 0u
+base_string_to_uint(10, "007") ==> 7u
+base_string_to_uint(2, "000101") ==> 5u
+base_string_to_uint(8, "00017") ==> 15u
+base_string_to_uint(16, "000ff") ==> 255u
+base_string_to_uint(36, "000z") ==> 35u
+base_string_to_uint(10, "0000000000000000000000000000000") ==> 0u
+base_string_to_uint(10, "0000000000000000000000000000001") ==> 1u
+
+### Digits not valid in the specified base ###
+
+base_string_to_uint(2, "1") ==> 1u
+base_string_to_uint(2, "2") ==> no
+base_string_to_uint(3, "2") ==> 2u
+base_string_to_uint(3, "3") ==> no
+base_string_to_uint(8, "7") ==> 7u
+base_string_to_uint(8, "8") ==> no
+base_string_to_uint(9, "8") ==> 8u
+base_string_to_uint(9, "9") ==> no
+base_string_to_uint(10, "9") ==> 9u
+base_string_to_uint(10, "a") ==> no
+base_string_to_uint(10, "A") ==> no
+base_string_to_uint(11, "a") ==> 10u
+base_string_to_uint(11, "A") ==> 10u
+base_string_to_uint(11, "b") ==> no
+base_string_to_uint(11, "B") ==> no
+base_string_to_uint(16, "f") ==> 15u
+base_string_to_uint(16, "g") ==> no
+base_string_to_uint(16, "G") ==> no
+base_string_to_uint(17, "g") ==> 16u
+base_string_to_uint(17, "h") ==> no
+base_string_to_uint(35, "y") ==> 34u
+base_string_to_uint(35, "z") ==> no
+base_string_to_uint(35, "Z") ==> no
+base_string_to_uint(36, "z") ==> 35u
+base_string_to_uint(36, "Z") ==> 35u
+base_string_to_uint(2, "102") ==> no
+base_string_to_uint(8, "178") ==> no
+base_string_to_uint(16, "1fg") ==> no
+
+### Non-digit characters ###
+
+base_string_to_uint(10, " 1") ==> no
+base_string_to_uint(10, "1 ") ==> no
+base_string_to_uint(10, "1 2") ==> no
+base_string_to_uint(10, "1.0") ==> no
+base_string_to_uint(10, "1,000") ==> no
+base_string_to_uint(10, "1_000") ==> no
+base_string_to_uint(10, "1e5") ==> no
+base_string_to_uint(16, "0x10") ==> no
+base_string_to_uint(10, "\t1") ==> no
+base_string_to_uint(10, "1\n") ==> no
+base_string_to_uint(36, "1!") ==> no
+base_string_to_uint(10, "3") ==> no
+
+### The string "10" in every base ###
+
+base_string_to_uint(2, "10") ==> 2u
+base_string_to_uint(3, "10") ==> 3u
+base_string_to_uint(4, "10") ==> 4u
+base_string_to_uint(5, "10") ==> 5u
+base_string_to_uint(6, "10") ==> 6u
+base_string_to_uint(7, "10") ==> 7u
+base_string_to_uint(8, "10") ==> 8u
+base_string_to_uint(9, "10") ==> 9u
+base_string_to_uint(10, "10") ==> 10u
+base_string_to_uint(11, "10") ==> 11u
+base_string_to_uint(12, "10") ==> 12u
+base_string_to_uint(13, "10") ==> 13u
+base_string_to_uint(14, "10") ==> 14u
+base_string_to_uint(15, "10") ==> 15u
+base_string_to_uint(16, "10") ==> 16u
+base_string_to_uint(17, "10") ==> 17u
+base_string_to_uint(18, "10") ==> 18u
+base_string_to_uint(19, "10") ==> 19u
+base_string_to_uint(20, "10") ==> 20u
+base_string_to_uint(21, "10") ==> 21u
+base_string_to_uint(22, "10") ==> 22u
+base_string_to_uint(23, "10") ==> 23u
+base_string_to_uint(24, "10") ==> 24u
+base_string_to_uint(25, "10") ==> 25u
+base_string_to_uint(26, "10") ==> 26u
+base_string_to_uint(27, "10") ==> 27u
+base_string_to_uint(28, "10") ==> 28u
+base_string_to_uint(29, "10") ==> 29u
+base_string_to_uint(30, "10") ==> 30u
+base_string_to_uint(31, "10") ==> 31u
+base_string_to_uint(32, "10") ==> 32u
+base_string_to_uint(33, "10") ==> 33u
+base_string_to_uint(34, "10") ==> 34u
+base_string_to_uint(35, "10") ==> 35u
+base_string_to_uint(36, "10") ==> 36u
+
+### Invalid bases ###
+
+base_string_to_uint(1, "1") ==> exception
+base_string_to_uint(0, "1") ==> exception
+base_string_to_uint(-1, "1") ==> exception
+base_string_to_uint(37, "1") ==> exception
+base_string_to_uint(100, "1") ==> exception
+
+### det_base_string_to_uint ###
+
+det_base_string_to_uint(10, "123") ==> 123u
+det_base_string_to_uint(16, "ff") ==> 255u
+det_base_string_to_uint(10, "") ==> exception
+det_base_string_to_uint(10, "12a") ==> exception
+det_base_string_to_uint(10, "-1") ==> exception
+det_base_string_to_uint(1, "1") ==> exception
+
+### to_uint ###
+
+to_uint("123") ==> 123u
+to_uint("-123") ==> no
+to_uint("") ==> no
+to_uint("0x10") ==> no
+to_uint(" 1") ==> no
+
diff --git a/tests/hard_coded/base_string_to_uint.m
b/tests/hard_coded/base_string_to_uint.m
new file mode 100644
index 000000000..f2fb928d4
--- /dev/null
+++ b/tests/hard_coded/base_string_to_uint.m
@@ -0,0 +1,244 @@
+%---------------------------------------------------------------------------%
+% vim: ts=4 sw=4 et ft=mercury
+%---------------------------------------------------------------------------%
+%
+% Test the non-overflow behaviour of string.base_string_to_uint/3.
+% (The overflow behaviour of this predicate is tested separately, by
+% string_to_uint_overflow.m.)
+%
+%---------------------------------------------------------------------------%
+
+:- module base_string_to_uint.
+
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is cc_multi.
+
+%---------------------------------------------------------------------------%
+%---------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module exception.
+:- import_module int.
+:- import_module list.
+:- import_module maybe.
+:- import_module string.
+:- import_module uint.
+
+%---------------------------------------------------------------------------%
+
+main(!IO) :-
+ header("Empty and blank strings", !IO),
+ test(10, "", !IO),
+ test(2, "", !IO),
+ test(36, "", !IO),
+ test(10, " ", !IO),
+ test(10, " ", !IO),
+ test(10, "\t", !IO),
+ test(10, "\n", !IO),
+ test(10, " \t ", !IO),
+ test(2, " ", !IO),
+ test(36, " ", !IO),
+ io.nl(!IO),
+
+ header("Signs are not accepted", !IO),
+ test(10, "-1", !IO),
+ test(10, "+1", !IO),
+ test(10, "-0", !IO),
+ test(10, "+0", !IO),
+ test(10, "-", !IO),
+ test(10, "+", !IO),
+ test(16, "-f", !IO),
+ test(36, "+z", !IO),
+ test(10, "--1", !IO),
+ test(10, "1-", !IO),
+ test(10, "1+", !IO),
+ io.nl(!IO),
+
+ header("Single digits", !IO),
+ test(10, "0", !IO),
+ test(10, "7", !IO),
+ test(2, "0", !IO),
+ test(2, "1", !IO),
+ test(8, "7", !IO),
+ test(16, "f", !IO),
+ test(16, "F", !IO),
+ test(36, "z", !IO),
+ test(36, "Z", !IO),
+ io.nl(!IO),
+
+ header("Upper and lower case digits", !IO),
+ test(16, "abcdef", !IO),
+ test(16, "ABCDEF", !IO),
+ test(16, "AbCdEf", !IO),
+ test(36, "abz", !IO),
+ test(36, "ABZ", !IO),
+ test(36, "AbZ", !IO),
+ io.nl(!IO),
+
+ header("Leading zeros", !IO),
+ test(10, "00", !IO),
+ test(10, "007", !IO),
+ test(2, "000101", !IO),
+ test(8, "00017", !IO),
+ test(16, "000ff", !IO),
+ test(36, "000z", !IO),
+ test(10, "0000000000000000000000000000000", !IO),
+ test(10, "0000000000000000000000000000001", !IO),
+ io.nl(!IO),
+
+ header("Digits not valid in the specified base", !IO),
+ test(2, "1", !IO),
+ test(2, "2", !IO),
+ test(3, "2", !IO),
+ test(3, "3", !IO),
+ test(8, "7", !IO),
+ test(8, "8", !IO),
+ test(9, "8", !IO),
+ test(9, "9", !IO),
+ test(10, "9", !IO),
+ test(10, "a", !IO),
+ test(10, "A", !IO),
+ test(11, "a", !IO),
+ test(11, "A", !IO),
+ test(11, "b", !IO),
+ test(11, "B", !IO),
+ test(16, "f", !IO),
+ test(16, "g", !IO),
+ test(16, "G", !IO),
+ test(17, "g", !IO),
+ test(17, "h", !IO),
+ test(35, "y", !IO),
+ test(35, "z", !IO),
+ test(35, "Z", !IO),
+ test(36, "z", !IO),
+ test(36, "Z", !IO),
+ test(2, "102", !IO),
+ test(8, "178", !IO),
+ test(16, "1fg", !IO),
+ io.nl(!IO),
+
+ header("Non-digit characters", !IO),
+ test(10, " 1", !IO),
+ test(10, "1 ", !IO),
+ test(10, "1 2", !IO),
+ test(10, "1.0", !IO),
+ test(10, "1,000", !IO),
+ test(10, "1_000", !IO),
+ test(10, "1e5", !IO),
+ test(16, "0x10", !IO),
+ test(10, "\t1", !IO),
+ test(10, "1\n", !IO),
+ test(36, "1!", !IO),
+ test(10, "\uff13", !IO),
+ io.nl(!IO),
+
+ header("The string \"10\" in every base", !IO),
+ list.foldl(test_10_in_base, 2 .. 36, !IO),
+ io.nl(!IO),
+
+ % base_string_to_uint/3 throws an exception if the base is not in 2..36.
+ % It checks the base before it looks at the string, so the string is
+ % irrelevant in these tests.
+
+ header("Invalid bases", !IO),
+ test(1, "1", !IO),
+ test(0, "1", !IO),
+ test(-1, "1", !IO),
+ test(37, "1", !IO),
+ test(100, "1", !IO),
+ io.nl(!IO),
+
+ header("det_base_string_to_uint", !IO),
+ test_det(10, "123", !IO),
+ test_det(16, "ff", !IO),
+ test_det(10, "", !IO),
+ test_det(10, "12a", !IO),
+ test_det(10, "-1", !IO),
+ test_det(1, "1", !IO),
+ io.nl(!IO),
+
+ % to_uint/2 is base_string_to_uint/3 with the base fixed at 10.
+
+ header("to_uint", !IO),
+ test_to_uint("123", !IO),
+ test_to_uint("-123", !IO),
+ test_to_uint("", !IO),
+ test_to_uint("0x10", !IO),
+ test_to_uint(" 1", !IO),
+ io.nl(!IO).
+
+%---------------------------------------------------------------------------%
+
+:- pred test_10_in_base(int::in, io::di, io::uo) is cc_multi.
+
+test_10_in_base(Base, !IO) :-
+ test(Base, "10", !IO).
+
+%---------------------------------------------------------------------------%
+
+ % test(Base, Str, !IO):
+ %
+ % Test string.base_string_to_uint(Base, Str, UInt), writing out the call
+ % and then its result: the value of UInt if the call succeeds, "no" if
+ % it fails, or "exception" if it throws.
+ %
+:- pred test(int::in, string::in, io::di, io::uo) is cc_multi.
+
+test(Base, Str, !IO) :-
+ io.format("base_string_to_uint(%d, %s) ==> ",
+ [i(Base), s(string(Str))], !IO),
+ ( try []
+ ( if string.base_string_to_uint(Base, Str, UInt0) then
+ MaybeUInt = yes(UInt0)
+ else
+ MaybeUInt = no
+ )
+ then
+ (
+ MaybeUInt = yes(UInt),
+ io.write_line(UInt, !IO)
+ ;
+ MaybeUInt = no,
+ io.write_string("no\n", !IO)
+ )
+ catch_any _ ->
+ io.write_string("exception\n", !IO)
+ ).
+
+:- pred test_det(int::in, string::in, io::di, io::uo) is cc_multi.
+
+test_det(Base, Str, !IO) :-
+ io.format("det_base_string_to_uint(%d, %s) ==> ",
+ [i(Base), s(string(Str))], !IO),
+ ( try []
+ UInt = string.det_base_string_to_uint(Base, Str)
+ then
+ io.write_line(UInt, !IO)
+ catch_any _ ->
+ io.write_string("exception\n", !IO)
+ ).
+
+:- pred test_to_uint(string::in, io::di, io::uo) is det.
+
+test_to_uint(Str, !IO) :-
+ io.format("to_uint(%s) ==> ", [s(string(Str))], !IO),
+ ( if string.to_uint(Str, UInt) then
+ io.write_line(UInt, !IO)
+ else
+ io.write_string("no\n", !IO)
+ ).
+
+%---------------------------------------------------------------------------%
+
+:- pred header(string::in, io::di, io::uo) is det.
+
+header(Title, !IO) :-
+ io.format("### %s ###\n\n", [s(Title)], !IO).
+
+%---------------------------------------------------------------------------%
+:- end_module base_string_to_uint.
+%---------------------------------------------------------------------------%
More information about the reviews
mailing list