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