-
Notifications
You must be signed in to change notification settings - Fork 129
/
test_exported_int_conversions.ml
295 lines (268 loc) · 8.09 KB
/
test_exported_int_conversions.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
open! Import
module type S = sig
type t [@@deriving compare, sexp_of]
val num_bits : int
val min_value : t
val minus_one : t
val zero : t
val one : t
val max_value : t
val to_int64 : t -> int64
val shift_right : t -> int -> t
val random : Random.State.t -> t -> t -> t
end
module I : S with type t = int = struct
include Int
let random = Random.State.int_incl
end
module Native : S with type t = nativeint = struct
include Nativeint
let random = Random.State.nativeint_incl
end
module I32 : S with type t = int32 = struct
include Int32
let random = Random.State.int32_incl
end
module I64 : S with type t = int64 = struct
include Int64
let random = Random.State.int64_incl
end
module I63 : S with type t = Int63.t = struct
include Int63
let random state lo hi = Int63.random_incl ~state lo hi
end
let iter (type a) (module M : S with type t = a) ~f =
let state = Random.State.make [| 0; 1; 2; 3; 4; 5 |] in
List.iter ~f [ M.min_value; M.minus_one; M.zero; M.one; M.max_value ];
for _ = 1 to 10_000 do
(* skew toward low numbers of bits so that, e.g., choosing a random int64 does
frequently find a value that can be converted to int32. *)
let strip_bits = Random.State.int_incl state 0 (M.num_bits - 1) in
let lo = M.shift_right M.min_value strip_bits in
let hi = M.shift_right M.max_value strip_bits in
f (M.random state lo hi)
done
;;
let try_with f x = Option.try_with (fun () -> f x)
(* Checks that a conversion from [A.t] to [B.t] is total using [of] and [to]. *)
let test_total
(type a b)
(module A : S with type t = a)
(module B : S with type t = b)
~of_:b_of_a
~to_:a_to_b
=
iter
(module A)
~f:(fun a ->
require_compare_equal (module B) (b_of_a a) (a_to_b a);
require_compare_equal (module Int64) (A.to_int64 a) (B.to_int64 (b_of_a a)))
;;
let truncate int64 ~num_bits =
Int64.shift_right (Int64.shift_left int64 (64 - num_bits)) (64 - num_bits)
;;
(* Checks that a conversion from [A.t] to [B.t] is partial using [of] and [to], and the
[_exn] equivalents. In the case where the conversion fails, ensure that the value,
converted to an [Int64.t] is outside the representable range of [B.t] converted to an
[Int64.t] as well. *)
let test_partial
(type a b)
(module A : S with type t = a)
(module B : S with type t = b)
~of_:b_of_a
~of_exn:b_of_a_exn
~of_trunc:b_of_a_trunc
~to_:a_to_b
~to_exn:a_to_b_exn
~to_trunc:a_to_b_trunc
=
let module B_option = struct
type t = B.t option [@@deriving compare, sexp_of]
end
in
let convertible_count = ref 0 in
iter
(module A)
~f:(fun a ->
require_compare_equal (module B_option) (b_of_a a) (a_to_b a);
require_compare_equal (module B_option) (b_of_a a) (try_with b_of_a_exn a);
require_compare_equal (module B_option) (a_to_b a) (try_with a_to_b_exn a);
match b_of_a a with
| Some b ->
Int.incr convertible_count;
require_compare_equal (module B) b (b_of_a_trunc a);
require_compare_equal (module B) b (a_to_b_trunc a);
require_compare_equal (module Int64) (A.to_int64 a) (B.to_int64 b)
| None ->
let trunc = truncate (A.to_int64 a) ~num_bits:B.num_bits in
require_compare_equal (module Int64) trunc (B.to_int64 (b_of_a_trunc a));
require_compare_equal (module Int64) trunc (B.to_int64 (a_to_b_trunc a));
require
(Int64.( > ) (A.to_int64 a) (B.to_int64 B.max_value)
|| Int64.( < ) (A.to_int64 a) (B.to_int64 B.min_value))
~if_false_then_print_s:(lazy [%message "failed to convert" ~_:(a : A.t)]));
(* Make sure we stress the conversion a nontrivial number of times. This makes sure the
random generation is useful and we aren't just testing the hard-coded examples. *)
require
(!convertible_count > 100)
~if_false_then_print_s:
(lazy
[%message
"did not test successful conversion often enough" (convertible_count : int ref)])
;;
let%expect_test "int <-> nativeint" =
test_total (module I) (module Native) ~of_:Nativeint.of_int ~to_:Int.to_nativeint;
[%expect {| |}];
test_partial
(module Native)
(module I)
~of_:Int.of_nativeint
~of_exn:Int.of_nativeint_exn
~of_trunc:Int.of_nativeint_trunc
~to_:Nativeint.to_int
~to_exn:Nativeint.to_int_exn
~to_trunc:Nativeint.to_int_trunc;
[%expect {| |}]
;;
let%expect_test "int <-> int32" =
test_partial
(module I)
(module I32)
~of_:Int32.of_int
~of_exn:Int32.of_int_exn
~of_trunc:Int32.of_int_trunc
~to_:Int.to_int32
~to_exn:Int.to_int32_exn
~to_trunc:Int.to_int32_trunc;
[%expect {| |}];
test_partial
(module I32)
(module I)
~of_:Int.of_int32
~of_exn:Int.of_int32_exn
~of_trunc:Int.of_int32_trunc
~to_:Int32.to_int
~to_exn:Int32.to_int_exn
~to_trunc:Int32.to_int_trunc;
[%expect {| |}]
;;
let%expect_test "nativeint <-> int32" =
test_partial
(module Native)
(module I32)
~of_:Int32.of_nativeint
~of_exn:Int32.of_nativeint_exn
~of_trunc:Int32.of_nativeint_trunc
~to_:Nativeint.to_int32
~to_exn:Nativeint.to_int32_exn
~to_trunc:Nativeint.to_int32_trunc;
[%expect {| |}];
test_total (module I32) (module Native) ~of_:Nativeint.of_int32 ~to_:Int32.to_nativeint;
[%expect {| |}]
;;
let%expect_test "int <-> int64" =
test_total (module I) (module I64) ~of_:Int64.of_int ~to_:Int.to_int64;
[%expect {| |}];
test_partial
(module I64)
(module I)
~of_:Int.of_int64
~of_exn:Int.of_int64_exn
~of_trunc:Int.of_int64_trunc
~to_:Int64.to_int
~to_exn:Int64.to_int_exn
~to_trunc:Int64.to_int_trunc;
[%expect {| |}]
;;
let%expect_test "nativeint <-> int64" =
test_total (module Native) (module I64) ~of_:Int64.of_nativeint ~to_:Nativeint.to_int64;
[%expect {| |}];
test_partial
(module I64)
(module Native)
~of_:Nativeint.of_int64
~of_exn:Nativeint.of_int64_exn
~of_trunc:Nativeint.of_int64_trunc
~to_:Int64.to_nativeint
~to_exn:Int64.to_nativeint_exn
~to_trunc:Int64.to_nativeint_trunc;
[%expect {| |}]
;;
let%expect_test "int32 <-> int64" =
test_total (module I32) (module I64) ~of_:Int64.of_int32 ~to_:Int32.to_int64;
[%expect {| |}];
test_partial
(module I64)
(module I32)
~of_:Int32.of_int64
~of_exn:Int32.of_int64_exn
~of_trunc:Int32.of_int64_trunc
~to_:Int64.to_int32
~to_exn:Int64.to_int32_exn
~to_trunc:Int64.to_int32_trunc;
[%expect {| |}]
;;
let%expect_test "int <-> int63" =
test_total (module I) (module I63) ~of_:Int63.of_int ~to_:Int63.of_int;
[%expect {| |}];
test_partial
(module I63)
(module I)
~of_:Int63.to_int
~of_exn:Int63.to_int_exn
~of_trunc:Int63.to_int_trunc
~to_:Int63.to_int
~to_exn:Int63.to_int_exn
~to_trunc:Int63.to_int_trunc;
[%expect {| |}]
;;
let%expect_test "nativeint <-> int63" =
test_partial
(module Native)
(module I63)
~of_:Int63.of_nativeint
~of_exn:Int63.of_nativeint_exn
~of_trunc:Int63.of_nativeint_trunc
~to_:Int63.of_nativeint
~to_exn:Int63.of_nativeint_exn
~to_trunc:Int63.of_nativeint_trunc;
[%expect {| |}];
test_partial
(module I63)
(module Native)
~of_:Int63.to_nativeint
~of_exn:Int63.to_nativeint_exn
~of_trunc:Int63.to_nativeint_trunc
~to_:Int63.to_nativeint
~to_exn:Int63.to_nativeint_exn
~to_trunc:Int63.to_nativeint_trunc;
[%expect {| |}]
;;
let%expect_test "int32 <-> int63" =
test_total (module I32) (module I63) ~of_:Int63.of_int32 ~to_:Int63.of_int32;
[%expect {| |}];
test_partial
(module I63)
(module I32)
~of_:Int63.to_int32
~of_exn:Int63.to_int32_exn
~of_trunc:Int63.to_int32_trunc
~to_:Int63.to_int32
~to_exn:Int63.to_int32_exn
~to_trunc:Int63.to_int32_trunc;
[%expect {| |}]
;;
let%expect_test "int64 <-> int63" =
test_partial
(module I64)
(module I63)
~of_:Int63.of_int64
~of_exn:Int63.of_int64_exn
~of_trunc:Int63.of_int64_trunc
~to_:Int63.of_int64
~to_exn:Int63.of_int64_exn
~to_trunc:Int63.of_int64_trunc;
[%expect {| |}];
test_total (module I63) (module I64) ~of_:Int63.to_int64 ~to_:Int63.to_int64;
[%expect {| |}]
;;