xref: /netbsd-src/external/gpl3/gdb/dist/gdb/unittests/gmp-utils-selftests.c (revision d16b7486a53dcb8072b60ec6fcb4373a2d0c27b7)
1 /* Self tests of the gmp-utils API.
2 
3    Copyright (C) 2019-2023 Free Software Foundation, Inc.
4 
5    This file is part of GDB.
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19 
20 #include "gmp-utils.h"
21 #include "gdbsupport/selftest.h"
22 
23 #include <math.h>
24 
25 namespace selftests {
26 
27 /* Perform a series of general tests of gdb_mpz's as_integer method.
28 
29    This function limits itself to values which are in range (out-of-range
30    values will be tested separately).  In doing so, it tries to be reasonably
31    exhaustive, by testing the edges, as well as a resonable set of values
32    including negative ones, zero, and positive values.  */
33 
34 static void
35 gdb_mpz_as_integer ()
36 {
37   /* Test a range of values, both as LONGEST and ULONGEST.  */
38   gdb_mpz v;
39   LONGEST l_expected;
40   ULONGEST ul_expected;
41 
42   /* Start with the smallest LONGEST  */
43   l_expected = (LONGEST) 1 << (sizeof (LONGEST) * 8 - 1);
44 
45   mpz_ui_pow_ui (v.val, 2, sizeof (LONGEST) * 8 - 1);
46   mpz_neg (v.val, v.val);
47 
48   SELF_CHECK (v.as_integer<LONGEST> () == l_expected);
49 
50   /* Try with a small range of integers including negative, zero,
51      and positive values.  */
52   for (int i = -256; i <= 256; i++)
53     {
54       l_expected = (LONGEST) i;
55       mpz_set_si (v.val, i);
56       SELF_CHECK (v.as_integer<LONGEST> () == l_expected);
57 
58       if (i >= 0)
59 	{
60 	  ul_expected = (ULONGEST) i;
61 	  mpz_set_ui (v.val, i);
62 	  SELF_CHECK (v.as_integer<ULONGEST> () == ul_expected);
63 	}
64     }
65 
66   /* Try with LONGEST_MAX.  */
67   l_expected = LONGEST_MAX;
68   ul_expected = (ULONGEST) l_expected;
69 
70   mpz_ui_pow_ui (v.val, 2, sizeof (LONGEST) * 8 - 1);
71   mpz_sub_ui (v.val, v.val, 1);
72 
73   SELF_CHECK (v.as_integer<LONGEST> () == l_expected);
74   SELF_CHECK (v.as_integer<ULONGEST> () == ul_expected);
75 
76   /* Try with ULONGEST_MAX.  */
77   ul_expected = ULONGEST_MAX;
78   mpz_ui_pow_ui (v.val, 2, sizeof (LONGEST) * 8);
79   mpz_sub_ui (v.val, v.val, 1);
80 
81   SELF_CHECK (v.as_integer<ULONGEST> () == ul_expected);
82 }
83 
84 /* A helper function which calls the given gdb_mpz object's as_integer
85    method with the given type T, and verifies that this triggers
86    an error due to VAL's value being out of range for type T.  */
87 
88 template<typename T, typename = gdb::Requires<std::is_integral<T>>>
89 static void
90 check_as_integer_raises_out_of_range_error (const gdb_mpz &val)
91 {
92   try
93     {
94       val.as_integer<T> ();
95     }
96   catch (const gdb_exception_error &ex)
97     {
98       SELF_CHECK (ex.reason == RETURN_ERROR);
99       SELF_CHECK (ex.error == GENERIC_ERROR);
100       SELF_CHECK (strstr (ex.what (), "Cannot export value") != nullptr);
101       return;
102     }
103   /* The expected exception did not get raised.  */
104   SELF_CHECK (false);
105 }
106 
107 /* Perform out-of-range tests of gdb_mpz's as_integer method.
108 
109    The goal of this function is to verify that gdb_mpz::as_integer
110    handles out-of-range values correctly.  */
111 
112 static void
113 gdb_mpz_as_integer_out_of_range ()
114 {
115   gdb_mpz v;
116 
117   /* Try LONGEST_MIN minus 1.  */
118   mpz_ui_pow_ui (v.val, 2, sizeof (LONGEST) * 8 - 1);
119   mpz_neg (v.val, v.val);
120   mpz_sub_ui (v.val, v.val, 1);
121 
122   check_as_integer_raises_out_of_range_error<ULONGEST> (v);
123   check_as_integer_raises_out_of_range_error<LONGEST> (v);
124 
125   /* Try negative one (-1). */
126   v = -1;
127 
128   check_as_integer_raises_out_of_range_error<ULONGEST> (v);
129   SELF_CHECK (v.as_integer<LONGEST> () == (LONGEST) -1);
130 
131   /* Try LONGEST_MAX plus 1.  */
132   v = LONGEST_MAX;
133   mpz_add_ui (v.val, v.val, 1);
134 
135   SELF_CHECK (v.as_integer<ULONGEST> () == (ULONGEST) LONGEST_MAX + 1);
136   check_as_integer_raises_out_of_range_error<LONGEST> (v);
137 
138   /* Try ULONGEST_MAX plus 1.  */
139   v = ULONGEST_MAX;
140   mpz_add_ui (v.val, v.val, 1);
141 
142   check_as_integer_raises_out_of_range_error<ULONGEST> (v);
143   check_as_integer_raises_out_of_range_error<LONGEST> (v);
144 }
145 
146 /* A helper function to store the given integer value into a buffer,
147    before reading it back into a gdb_mpz.  Sets ACTUAL to the value
148    read back, while at the same time setting EXPECTED as the value
149    we would expect to be read back.
150 
151    Note that this function does not perform the comparison between
152    EXPECTED and ACTUAL.  The caller will do it inside a SELF_CHECK
153    call, allowing the line information shown when the test fails
154    to provide a bit more information about the kind of values
155    that were used when the check failed.  This makes the writing
156    of the tests a little more verbose, but the debugging in case
157    of problems should hopefuly be easier.  */
158 
159 template<typename T>
160 void
161 store_and_read_back (T val, size_t buf_len, enum bfd_endian byte_order,
162 		     gdb_mpz &expected, gdb_mpz &actual)
163 {
164   gdb_byte *buf;
165 
166   expected = val;
167 
168   buf = (gdb_byte *) alloca (buf_len);
169   store_integer (buf, buf_len, byte_order, val);
170 
171   /* Pre-initialize ACTUAL to something that's not the expected value.  */
172   mpz_set (actual.val, expected.val);
173   mpz_sub_ui (actual.val, actual.val, 500);
174 
175   actual.read ({buf, buf_len}, byte_order, !std::is_signed<T>::value);
176 }
177 
178 /* Test the gdb_mpz::read method over a reasonable range of values.
179 
180    The testing is done by picking an arbitrary buffer length, after
181    which we test every possible value that this buffer allows, both
182    with signed numbers as well as unsigned ones.  */
183 
184 static void
185 gdb_mpz_read_all_from_small ()
186 {
187   /* Start with a type whose size is small enough that we can afford
188      to check the complete range.  */
189 
190   int buf_len = 1;
191   LONGEST l_min = -pow (2.0, buf_len * 8 - 1);
192   LONGEST l_max = pow (2.0, buf_len * 8 - 1) - 1;
193 
194   for (LONGEST l = l_min; l <= l_max; l++)
195     {
196       gdb_mpz expected, actual;
197 
198       store_and_read_back (l, buf_len, BFD_ENDIAN_BIG, expected, actual);
199       SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
200 
201       store_and_read_back (l, buf_len, BFD_ENDIAN_LITTLE, expected, actual);
202       SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
203     }
204 
205   /* Do the same as above, but with an unsigned type.  */
206   ULONGEST ul_min = 0;
207   ULONGEST ul_max = pow (2.0, buf_len * 8) - 1;
208 
209   for (ULONGEST ul = ul_min; ul <= ul_max; ul++)
210     {
211       gdb_mpz expected, actual;
212 
213       store_and_read_back (ul, buf_len, BFD_ENDIAN_BIG, expected, actual);
214       SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
215 
216       store_and_read_back (ul, buf_len, BFD_ENDIAN_LITTLE, expected, actual);
217       SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
218     }
219 }
220 
221 /* Test the gdb_mpz::read the extremes of LONGEST and ULONGEST.  */
222 
223 static void
224 gdb_mpz_read_min_max ()
225 {
226   gdb_mpz expected, actual;
227 
228   /* Start with the smallest LONGEST.  */
229 
230   LONGEST l_min = (LONGEST) 1 << (sizeof (LONGEST) * 8 - 1);
231 
232   store_and_read_back (l_min, sizeof (LONGEST), BFD_ENDIAN_BIG,
233 		       expected, actual);
234   SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
235 
236   store_and_read_back (l_min, sizeof (LONGEST), BFD_ENDIAN_LITTLE,
237 		       expected, actual);
238   SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
239 
240   /* Same with LONGEST_MAX.  */
241 
242   LONGEST l_max = LONGEST_MAX;
243 
244   store_and_read_back (l_max, sizeof (LONGEST), BFD_ENDIAN_BIG,
245 		       expected, actual);
246   SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
247 
248   store_and_read_back (l_max, sizeof (LONGEST), BFD_ENDIAN_LITTLE,
249 		       expected, actual);
250   SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
251 
252   /* Same with the smallest ULONGEST.  */
253 
254   ULONGEST ul_min = 0;
255 
256   store_and_read_back (ul_min, sizeof (ULONGEST), BFD_ENDIAN_BIG,
257 		       expected, actual);
258   SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
259 
260   store_and_read_back (ul_min, sizeof (ULONGEST), BFD_ENDIAN_LITTLE,
261 		       expected, actual);
262   SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
263 
264   /* Same with ULONGEST_MAX.  */
265 
266   ULONGEST ul_max = ULONGEST_MAX;
267 
268   store_and_read_back (ul_max, sizeof (ULONGEST), BFD_ENDIAN_BIG,
269 		       expected, actual);
270   SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
271 
272   store_and_read_back (ul_max, sizeof (ULONGEST), BFD_ENDIAN_LITTLE,
273 		       expected, actual);
274   SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
275 }
276 
277 /* A helper function which creates a gdb_mpz object from the given
278    integer VAL, and then writes it using its gdb_mpz::write method.
279 
280    The written value is then extracted from the buffer and returned,
281    for comparison with the original.
282 
283    Note that this function does not perform the comparison between
284    VAL and the returned value.  The caller will do it inside a SELF_CHECK
285    call, allowing the line information shown when the test fails
286    to provide a bit more information about the kind of values
287    that were used when the check failed.  This makes the writing
288    of the tests a little more verbose, but the debugging in case
289    of problems should hopefuly be easier.  */
290 
291 template<typename T>
292 T
293 write_and_extract (T val, size_t buf_len, enum bfd_endian byte_order)
294 {
295   gdb_mpz v (val);
296 
297   SELF_CHECK (v.as_integer<T> () == val);
298 
299   gdb_byte *buf = (gdb_byte *) alloca (buf_len);
300   v.write ({buf, buf_len}, byte_order, !std::is_signed<T>::value);
301 
302   return extract_integer<T> ({buf, buf_len}, byte_order);
303 }
304 
305 /* Test the gdb_mpz::write method over a reasonable range of values.
306 
307    The testing is done by picking an arbitrary buffer length, after
308    which we test every possible value that this buffer allows.  */
309 
310 static void
311 gdb_mpz_write_all_from_small ()
312 {
313   int buf_len = 1;
314   LONGEST l_min = -pow (2.0, buf_len * 8 - 1);
315   LONGEST l_max = pow (2.0, buf_len * 8 - 1) - 1;
316 
317   for (LONGEST l = l_min; l <= l_max; l++)
318     {
319       SELF_CHECK (write_and_extract (l, buf_len, BFD_ENDIAN_BIG) == l);
320       SELF_CHECK (write_and_extract (l, buf_len, BFD_ENDIAN_LITTLE) == l);
321     }
322 
323     /* Do the same as above, but with an unsigned type.  */
324   ULONGEST ul_min = 0;
325   ULONGEST ul_max = pow (2.0, buf_len * 8) - 1;
326 
327   for (ULONGEST ul = ul_min; ul <= ul_max; ul++)
328     {
329       SELF_CHECK (write_and_extract (ul, buf_len, BFD_ENDIAN_BIG) == ul);
330       SELF_CHECK (write_and_extract (ul, buf_len, BFD_ENDIAN_LITTLE) == ul);
331     }
332 }
333 
334 /* Test the gdb_mpz::write the extremes of LONGEST and ULONGEST.  */
335 
336 static void
337 gdb_mpz_write_min_max ()
338 {
339   /* Start with the smallest LONGEST.  */
340 
341   LONGEST l_min = (LONGEST) 1 << (sizeof (LONGEST) * 8 - 1);
342   SELF_CHECK (write_and_extract (l_min, sizeof (LONGEST), BFD_ENDIAN_BIG)
343 	      == l_min);
344   SELF_CHECK (write_and_extract (l_min, sizeof (LONGEST), BFD_ENDIAN_LITTLE)
345 	      == l_min);
346 
347   /* Same with LONGEST_MAX.  */
348 
349   LONGEST l_max = LONGEST_MAX;
350   SELF_CHECK (write_and_extract (l_max, sizeof (LONGEST), BFD_ENDIAN_BIG)
351 	      == l_max);
352   SELF_CHECK (write_and_extract (l_max, sizeof (LONGEST), BFD_ENDIAN_LITTLE)
353 	      == l_max);
354 
355   /* Same with the smallest ULONGEST.  */
356 
357   ULONGEST ul_min = (ULONGEST) 1 << (sizeof (ULONGEST) * 8 - 1);
358   SELF_CHECK (write_and_extract (ul_min, sizeof (ULONGEST), BFD_ENDIAN_BIG)
359 	      == ul_min);
360   SELF_CHECK (write_and_extract (ul_min, sizeof (ULONGEST), BFD_ENDIAN_LITTLE)
361 	      == ul_min);
362 
363   /* Same with ULONGEST_MAX.  */
364 
365   ULONGEST ul_max = ULONGEST_MAX;
366   SELF_CHECK (write_and_extract (ul_max, sizeof (ULONGEST), BFD_ENDIAN_BIG)
367 	      == ul_max);
368   SELF_CHECK (write_and_extract (ul_max, sizeof (ULONGEST), BFD_ENDIAN_LITTLE)
369 	      == ul_max);
370 }
371 
372 /* A helper function which stores the signed number, the unscaled value
373    of a fixed point object, into a buffer, and then uses gdb_mpq's
374    read_fixed_point to read it as a fixed_point value, with
375    the given parameters.
376 
377    EXPECTED is set to the value we expected to get after the call
378    to read_fixed_point.  ACTUAL is the value we actually do get.
379 
380    Note that this function does not perform the comparison between
381    EXPECTED and ACTUAL.  The caller will do it inside a SELF_CHECK
382    call, allowing the line information shown when the test fails
383    to provide a bit more information about the kind of values
384    that were used when the check failed.  This makes the writing
385    of the tests a little more verbose, but the debugging in case
386    of problems should hopefuly be easier.  */
387 
388 static void
389 read_fp_test (int unscaled, const gdb_mpq &scaling_factor,
390 	      enum bfd_endian byte_order,
391 	      gdb_mpq &expected, gdb_mpq &actual)
392 {
393   /* For this kind of testing, we'll use a buffer the same size as
394      our unscaled parameter.  */
395   const size_t len = sizeof (unscaled);
396   gdb_byte buf[len];
397   store_signed_integer (buf, len, byte_order, unscaled);
398 
399   actual.read_fixed_point ({buf, len}, byte_order, 0, scaling_factor);
400 
401   mpq_set_si (expected.val, unscaled, 1);
402   mpq_mul (expected.val, expected.val, scaling_factor.val);
403 }
404 
405 /* Perform various tests of the gdb_mpq::read_fixed_point method.  */
406 
407 static void
408 gdb_mpq_read_fixed_point ()
409 {
410   gdb_mpq expected, actual;
411   gdb_mpq scaling_factor;
412 
413   /* Pick an arbitrary scaling_factor; this operation is trivial enough
414      thanks to GMP that the value we use isn't really important.  */
415   mpq_set_ui (scaling_factor.val, 3, 5);
416 
417   /* Try a few values, both negative and positive... */
418 
419   read_fp_test (-256, scaling_factor, BFD_ENDIAN_BIG, expected, actual);
420   SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
421   read_fp_test (-256, scaling_factor, BFD_ENDIAN_LITTLE, expected, actual);
422   SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
423 
424   read_fp_test (-1, scaling_factor, BFD_ENDIAN_BIG, expected, actual);
425   SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
426   read_fp_test (-1, scaling_factor, BFD_ENDIAN_LITTLE, expected, actual);
427   SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
428 
429   read_fp_test (0, scaling_factor, BFD_ENDIAN_BIG, expected, actual);
430   SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
431   read_fp_test (0, scaling_factor, BFD_ENDIAN_LITTLE, expected, actual);
432   SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
433 
434   read_fp_test (1, scaling_factor, BFD_ENDIAN_BIG, expected, actual);
435   SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
436   read_fp_test (1, scaling_factor, BFD_ENDIAN_LITTLE, expected, actual);
437   SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
438 
439   read_fp_test (1025, scaling_factor, BFD_ENDIAN_BIG, expected, actual);
440   SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
441   read_fp_test (1025, scaling_factor, BFD_ENDIAN_LITTLE, expected, actual);
442   SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
443 }
444 
445 /* A helper function which builds a gdb_mpq object from the given
446    NUMERATOR and DENOMINATOR, and then calls gdb_mpq's write_fixed_point
447    method to write it to a buffer.
448 
449    The value written into the buffer is then read back as is,
450    and returned.  */
451 
452 static LONGEST
453 write_fp_test (int numerator, unsigned int denominator,
454 	       const gdb_mpq &scaling_factor,
455 	       enum bfd_endian byte_order)
456 {
457   /* For this testing, we'll use a buffer the size of LONGEST.
458      This is really an arbitrary decision, as long as the buffer
459      is long enough to hold the unscaled values that we'll be
460      writing.  */
461   const size_t len = sizeof (LONGEST);
462   gdb_byte buf[len];
463   memset (buf, 0, len);
464 
465   gdb_mpq v;
466   mpq_set_si (v.val, numerator, denominator);
467   mpq_canonicalize (v.val);
468   v.write_fixed_point ({buf, len}, byte_order, 0, scaling_factor);
469 
470   return extract_unsigned_integer (buf, len, byte_order);
471 }
472 
473 /* Perform various tests of the gdb_mpq::write_fixed_point method.  */
474 
475 static void
476 gdb_mpq_write_fixed_point ()
477 {
478   /* Pick an arbitrary factor; this operations is sufficiently trivial
479      with the use of GMP that the value of this factor is not really
480      all that important.  */
481   gdb_mpq scaling_factor;
482   mpq_set_ui (scaling_factor.val, 1, 3);
483 
484   gdb_mpq vq;
485 
486   /* Try a few multiples of the scaling factor, both negative,
487      and positive... */
488 
489   SELF_CHECK (write_fp_test (-8, 1, scaling_factor, BFD_ENDIAN_BIG) == -24);
490   SELF_CHECK (write_fp_test (-8, 1, scaling_factor, BFD_ENDIAN_LITTLE) == -24);
491 
492   SELF_CHECK (write_fp_test (-2, 3, scaling_factor, BFD_ENDIAN_BIG) == -2);
493   SELF_CHECK (write_fp_test (-2, 3, scaling_factor, BFD_ENDIAN_LITTLE) == -2);
494 
495   SELF_CHECK (write_fp_test (0, 3, scaling_factor, BFD_ENDIAN_BIG) == 0);
496   SELF_CHECK (write_fp_test (0, 3, scaling_factor, BFD_ENDIAN_LITTLE) == 0);
497 
498   SELF_CHECK (write_fp_test (5, 3, scaling_factor, BFD_ENDIAN_BIG) == 5);
499   SELF_CHECK (write_fp_test (5, 3, scaling_factor, BFD_ENDIAN_LITTLE) == 5);
500 }
501 
502 }
503 
504 void _initialize_gmp_utils_selftests ();
505 
506 void
507 _initialize_gmp_utils_selftests ()
508 {
509   selftests::register_test ("gdb_mpz_as_integer",
510 			    selftests::gdb_mpz_as_integer);
511   selftests::register_test ("gdb_mpz_as_integer_out_of_range",
512 			    selftests::gdb_mpz_as_integer_out_of_range);
513   selftests::register_test ("gdb_mpz_read_all_from_small",
514 			    selftests::gdb_mpz_read_all_from_small);
515   selftests::register_test ("gdb_mpz_read_min_max",
516 			    selftests::gdb_mpz_read_min_max);
517   selftests::register_test ("gdb_mpz_write_all_from_small",
518 			    selftests::gdb_mpz_write_all_from_small);
519   selftests::register_test ("gdb_mpz_write_min_max",
520 			    selftests::gdb_mpz_write_min_max);
521   selftests::register_test ("gdb_mpq_read_fixed_point",
522 			    selftests::gdb_mpq_read_fixed_point);
523   selftests::register_test ("gdb_mpq_write_fixed_point",
524 			    selftests::gdb_mpq_write_fixed_point);
525 }
526