Skip to content

Commit 7499bd8

Browse files
committed
Oxcaml: add tests (small ints)
1 parent ba50f92 commit 7499bd8

File tree

3 files changed

+710
-0
lines changed

3 files changed

+710
-0
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
(env
2+
(_
3+
(flags
4+
(:standard
5+
(-w -9-27-32)))))
6+
7+
(tests
8+
(names test_int8_u test_int16_u)
9+
(build_if %{oxcaml_supported})
10+
(libraries stdlib_stable stdlib_upstream_compatible)
11+
(modes js wasm))
Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
1+
(* TEST
2+
include stdlib_stable;
3+
flags = "-extension layouts_beta";
4+
*)
5+
6+
(* External declarations for unsigned comparison primitives *)
7+
external unsigned_lt : int16# -> int16# -> bool = "%int16#_unsigned_lessthan"
8+
external unsigned_gt : int16# -> int16# -> bool = "%int16#_unsigned_greaterthan"
9+
10+
module Int16 = Stdlib_stable.Int16
11+
module Int16_u = Stdlib_stable.Int16_u
12+
13+
(* Print all individual successful tests; used for debugging, as it will cause
14+
this test to fail *)
15+
let debug_tests = false
16+
17+
(* Constant seed for repeatable random-testing properties *)
18+
let () = Random.init 42
19+
20+
let to_ocaml_string s = "\"" ^ String.escaped s ^ "\""
21+
22+
type 'a result = {
23+
expected : 'a;
24+
actual : 'a;
25+
equal : 'a -> 'a -> bool;
26+
to_string : 'a -> string
27+
}
28+
29+
module type Result = sig
30+
type t
31+
val equal : t -> t -> bool
32+
val to_string : t -> string
33+
end
34+
35+
let mk_result' equal to_string = fun ~expected ~actual ->
36+
{ expected; actual; equal; to_string }
37+
38+
let mk_result (type a) (module M : Result with type t = a) =
39+
mk_result' M.equal M.to_string
40+
41+
let float_result = mk_result (module Float)
42+
let bool_result = mk_result (module Bool)
43+
let int_result = mk_result (module Int)
44+
let int16_result = mk_result (module Int16)
45+
let string_result = mk_result' String.equal to_ocaml_string
46+
47+
let option_result (type a) (module M : Result with type t = a) =
48+
mk_result'
49+
(Option.equal M.equal)
50+
(function
51+
| None -> "None"
52+
| Some x -> "Some (" ^ M.to_string x ^ ")")
53+
54+
type 'a generator =
55+
| Rand of (unit -> 'a)
56+
| Const of 'a
57+
58+
let map_generator f = function
59+
| Rand r -> Rand (fun () -> f (r ()))
60+
| Const c -> Const (f c)
61+
62+
type 'a input = {
63+
generators : 'a generator list;
64+
to_string : 'a -> string
65+
}
66+
67+
module type Integer = sig
68+
type t
69+
(* Interesting constants *)
70+
val zero : t
71+
val one : t
72+
val minus_one : t
73+
val max_int : t
74+
val min_int : t
75+
(* String generation *)
76+
val to_string : t -> string
77+
(* Comparison (for zero-testing) *)
78+
val equal : t -> t -> bool
79+
(* Arithmetic (for generating small numbers) *)
80+
val sub : t -> t -> t
81+
val shift_left : t -> int -> t
82+
end
83+
84+
let one_thousand (type a) (module I : Integer with type t = a) =
85+
let open I in
86+
let i1024 = shift_left one 10 in
87+
let i16 = shift_left one 4 in
88+
let i8 = shift_left one 3 in
89+
sub (sub i1024 i16) i8
90+
91+
let two_thousand (type a) (module I : Integer with type t = a) =
92+
I.shift_left (one_thousand (module I)) 1
93+
94+
let unit_input =
95+
{ generators = [Const ()]
96+
; to_string = Unit.to_string
97+
}
98+
99+
let bool_input =
100+
{ generators = [Const false; Const true]
101+
; to_string = Bool.to_string
102+
}
103+
104+
let float_input =
105+
{ generators = [ Const 0.
106+
; Const 1.
107+
; Const (-1.)
108+
; Const Float.max_float
109+
; Const Float.min_float
110+
; Const Float.epsilon
111+
; Const Float.nan
112+
; Const Float.infinity
113+
; Const Float.neg_infinity
114+
; Rand (fun () -> Random.float 2000. -. 1000.)
115+
; Rand (fun () -> Int64.float_of_bits (Random.bits64 ()))
116+
]
117+
; to_string = Float.to_string
118+
}
119+
120+
let integer_input
121+
(type a) (module I : Integer with type t = a)
122+
rand_range rand_full =
123+
let rand_small () =
124+
let i0_to_2000 = rand_range (two_thousand (module I)) in
125+
I.sub i0_to_2000 (one_thousand (module I))
126+
in
127+
{ generators = [ Const I.zero
128+
; Const I.one
129+
; Const I.minus_one
130+
; Const I.max_int
131+
; Const I.min_int
132+
; Rand rand_small
133+
; Rand rand_full
134+
]
135+
; to_string = I.to_string
136+
}
137+
138+
let nonzero_integer_input
139+
(type a) (module I : Integer with type t = a)
140+
rand_range rand_full =
141+
let { generators; to_string } =
142+
integer_input (module I) rand_range rand_full
143+
in
144+
let generators =
145+
generators |>
146+
List.filter_map
147+
(function
148+
| Const c ->
149+
if I.equal c I.zero
150+
then None
151+
else Some (Const c)
152+
| Rand r ->
153+
Some (Rand (fun () ->
154+
let n = ref I.zero in
155+
while I.equal !n I.zero do
156+
n := r ()
157+
done;
158+
!n)))
159+
in
160+
{ generators; to_string }
161+
162+
let random_int16 x = Int16.of_int (Random.int (Int16.to_int x))
163+
let random_bits16 x = Int16.of_int (Random.bits ())
164+
165+
let int_input = integer_input (module Int) Random.int Random.bits
166+
let int16_input = integer_input (module Int16) random_int16 random_bits16
167+
let nonzero_int16_input =
168+
nonzero_integer_input (module Int16) random_int16 random_bits16
169+
170+
let int16_shift_amount_input =
171+
{ generators = List.init 16 (fun c -> Const c)
172+
; to_string = Int.to_string
173+
}
174+
175+
let int16_string_input =
176+
{ generators = List.map
177+
(map_generator Int16.to_string)
178+
int16_input.generators
179+
; to_string = to_ocaml_string
180+
}
181+
182+
let product2 ~f xs ys =
183+
List.concat_map (fun x ->
184+
List.map (fun y ->
185+
f x y)
186+
ys)
187+
xs
188+
189+
let two_inputs in1 in2 =
190+
{ generators = product2 in1.generators in2.generators ~f:(fun gen1 gen2 ->
191+
match gen1, gen2 with
192+
| Const c1, Const c2 -> Const (c1, c2)
193+
| Const c1, Rand r2 -> Rand (fun () -> c1, r2 ())
194+
| Rand r1, Const c2 -> Rand (fun () -> r1 (), c2)
195+
| Rand r1, Rand r2 -> Rand (fun () -> r1 (), r2 ())
196+
)
197+
; to_string = fun (x1, x2) ->
198+
Printf.sprintf "(%s, %s)" (in1.to_string x1) (in2.to_string x2)
199+
}
200+
201+
let passed { actual; expected; equal; _ } = equal actual expected
202+
203+
let test ?(n=100) name prop { generators; to_string = input_to_string } =
204+
let test input =
205+
let {expected; actual; to_string} as result = prop input in
206+
let print_test outcome =
207+
Printf.printf "Test %s: %s. Input = %s; expected = %s; actual = %s\n"
208+
outcome name
209+
(input_to_string input) (to_string expected) (to_string actual)
210+
in
211+
if passed result then begin
212+
if debug_tests then print_test "succeeded"
213+
end
214+
else
215+
print_test "failed"
216+
in
217+
List.iter
218+
(function
219+
| Const c -> test c
220+
| Rand r -> for _ = 1 to n do test (r ()) done)
221+
generators
222+
223+
let test_same
224+
~input ~result ~apply_expected ~apply_actual
225+
?n name expected actual =
226+
test ?n name
227+
(fun x ->
228+
result
229+
~expected:(apply_expected expected x)
230+
~actual:(apply_actual actual x))
231+
input
232+
233+
let test_constant ?n name expected actual result =
234+
test ?n name (fun () -> result ~expected ~actual) unit_input
235+
236+
let test_same_unary ?n name input result expected actual =
237+
test_same
238+
~input
239+
~result
240+
~apply_expected:Fun.id
241+
~apply_actual:Fun.id
242+
?n name expected actual
243+
244+
let test_same_binary ?n name input1 input2 result expected actual =
245+
test_same
246+
~input:(two_inputs input1 input2)
247+
~result
248+
~apply_expected:(fun f (x,y) -> f x y)
249+
~apply_actual:(fun f (x,y) -> f x y)
250+
?n name expected actual
251+
252+
let test_unary ?n name f fu =
253+
test_same_unary ?n name int16_input int16_result f
254+
(fun x -> Int16_u.to_int16 (fu (Int16_u.of_int16 x)))
255+
256+
let test_unary_of ?n name f fu result =
257+
test_same_unary ?n name int16_input result f
258+
(fun x -> fu (Int16_u.of_int16 x))
259+
260+
let test_unary_to ?n name f fu input =
261+
test_same_unary ?n name input int16_result f
262+
(fun x -> Int16_u.to_int16 (fu x))
263+
264+
let test_binary' ~second_input ?n name f fu =
265+
test_same_binary ?n name int16_input second_input int16_result f
266+
(fun x y -> Int16_u.to_int16
267+
(fu
268+
(Int16_u.of_int16 x)
269+
(Int16_u.of_int16 y)))
270+
271+
let test_binary = test_binary' ~second_input:int16_input
272+
273+
let test_division = test_binary' ~second_input:nonzero_int16_input
274+
275+
let test_binary_of ?n name f fu result =
276+
test_same_binary ?n name int16_input int16_input result f
277+
(fun x y -> fu
278+
(Int16_u.of_int16 x)
279+
(Int16_u.of_int16 y))
280+
281+
let test_shift ?n name shift shiftu =
282+
test_same_binary
283+
?n name int16_input int16_shift_amount_input int16_result shift
284+
(fun x y -> Int16_u.to_int16
285+
(shiftu
286+
(Int16_u.of_int16 x)
287+
y))
288+
289+
let () =
290+
test_unary "neg" Int16.neg Int16_u.neg;
291+
test_binary "add" Int16.add Int16_u.add;
292+
test_binary "sub" Int16.sub Int16_u.sub;
293+
test_binary "mul" Int16.mul Int16_u.mul;
294+
test_division "div" Int16.div Int16_u.div;
295+
test_division "unsigned_div" Int16.unsigned_div Int16_u.unsigned_div;
296+
test_division "rem" Int16.rem Int16_u.rem;
297+
test_division "unsigned_rem" Int16.unsigned_rem Int16_u.unsigned_rem;
298+
test_unary "succ" Int16.succ Int16_u.succ;
299+
test_unary "pred" Int16.pred Int16_u.pred;
300+
test_unary "abs" Int16.abs Int16_u.abs;
301+
test_binary "logand" Int16.logand Int16_u.logand;
302+
test_binary "logor" Int16.logor Int16_u.logor;
303+
test_binary "logxor" Int16.logxor Int16_u.logxor;
304+
test_unary "lognot" Int16.lognot Int16_u.lognot;
305+
test_shift "shift_left" Int16.shift_left Int16_u.shift_left;
306+
test_shift "shift_right" Int16.shift_right Int16_u.shift_right;
307+
test_shift "shift_right_logical" Int16.shift_right_logical Int16_u.shift_right_logical;
308+
test_unary_to "of_int" Int16.of_int Int16_u.of_int int_input;
309+
test_unary_of "to_int" Int16.to_int Int16_u.to_int int_result;
310+
test_unary_of "unsigned_to_int" Int16.unsigned_to_int Int16_u.unsigned_to_int int_result;
311+
test_unary_to "of_float" Int16.of_float Int16_u.of_float float_input;
312+
test_unary_of "to_float" Int16.to_float Int16_u.to_float float_result;
313+
test_unary_to "of_string" Int16.of_string Int16_u.of_string int16_string_input;
314+
test_unary_of "to_string" Int16.to_string Int16_u.to_string string_result;
315+
test_binary_of "compare" Int16.compare Int16_u.compare int_result;
316+
test_binary_of "unsigned_compare" Int16.unsigned_compare Int16_u.unsigned_compare int_result;
317+
test_binary_of "equal" Int16.equal Int16_u.equal bool_result;
318+
test_binary "min" Int16.min Int16_u.min;
319+
test_binary "max" Int16.max Int16_u.max;
320+
321+
(* Explicit unsigned comparison tests with hardcoded expected values *)
322+
let module I = Int16_u in
323+
324+
(* Test that -1 (0xFFFF) > 0 when compared as unsigned *)
325+
assert (I.unsigned_compare (I.minus_one ()) (I.zero ()) = 1);
326+
assert (I.unsigned_compare (I.zero ()) (I.minus_one ()) = -1);
327+
328+
(* Test that -32768 (0x8000) > 32767 (0x7FFF) when compared as unsigned *)
329+
assert (I.unsigned_compare (I.min_int ()) (I.max_int ()) = 1);
330+
assert (I.unsigned_compare (I.max_int ()) (I.min_int ()) = -1);
331+
332+
(* Test ordering: when viewed as unsigned:
333+
0 < 1 < 32767 < 32768 (min_int) < 65535 (minus_one) *)
334+
assert (I.unsigned_compare (I.zero ()) (I.one ()) = -1);
335+
assert (I.unsigned_compare (I.one ()) (I.max_int ()) = -1);
336+
assert (I.unsigned_compare (I.max_int ()) (I.min_int ()) = -1);
337+
assert (I.unsigned_compare (I.min_int ()) (I.minus_one ()) = -1);
338+
339+
(* Test equality *)
340+
assert (I.unsigned_compare (I.zero ()) (I.zero ()) = 0);
341+
assert (I.unsigned_compare (I.minus_one ()) (I.minus_one ()) = 0);
342+
343+
(* Test the unsigned_lt primitive directly *)
344+
assert (unsigned_lt (I.zero ()) (I.minus_one ()) = true); (* 0 < 65535 *)
345+
assert (unsigned_lt (I.minus_one ()) (I.zero ()) = false); (* 65535 not < 0 *)
346+
assert (unsigned_lt (I.max_int ()) (I.min_int ()) = true); (* 32767 < 32768 *)
347+
assert (unsigned_lt (I.min_int ()) (I.max_int ())
348+
= false); (* 32768 not < 32767 *)
349+
350+
(* Test unsigned greater than using primitive comparisons *)
351+
assert (unsigned_gt (I.minus_one ()) (I.zero ()) = true); (* 65535 > 0 *)
352+
assert (unsigned_gt (I.zero ()) (I.minus_one ()) = false); (* 0 not > 65535 *)
353+
assert (unsigned_gt (I.min_int ()) (I.max_int ()) = true); (* 32768 > 32767 *)
354+
assert (unsigned_gt (I.max_int ()) (I.min_int ())
355+
= false); (* 32767 not > 32768 *)
356+
357+
()

0 commit comments

Comments
 (0)