]> Savannah Git Hosting - gnulib.git/commitdiff
fenv-exceptions-trapping: Add tests.
authorBruno Haible <bruno@clisp.org>
Tue, 31 Oct 2023 20:54:49 +0000 (21:54 +0100)
committerBruno Haible <bruno@clisp.org>
Tue, 31 Oct 2023 20:56:08 +0000 (21:56 +0100)
* tests/test-fenv-except-trapping-1.c: New file.
* tests/test-fenv-except-trapping-2.sh: New file.
* tests/test-fenv-except-trapping-2.c: New file.
* modules/fenv-exceptions-trapping-tests: New file.

ChangeLog
modules/fenv-exceptions-trapping-tests [new file with mode: 0644]
tests/test-fenv-except-trapping-1.c [new file with mode: 0644]
tests/test-fenv-except-trapping-2.c [new file with mode: 0644]
tests/test-fenv-except-trapping-2.sh [new file with mode: 0755]

index f7d1a2237e600d7b9f8bf491a7a2ce0e3f7d6299..3b327c4c3da990ced61aa3c12c60c37b994347f1 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
 2023-10-31  Bruno Haible  <bruno@clisp.org>
 
+       fenv-exceptions-trapping: Add tests.
+       * tests/test-fenv-except-trapping-1.c: New file.
+       * tests/test-fenv-except-trapping-2.sh: New file.
+       * tests/test-fenv-except-trapping-2.c: New file.
+       * modules/fenv-exceptions-trapping-tests: New file.
+
        fenv-exceptions-trapping: New module.
        * lib/fenv.in.h (feenableexcept, fedisableexcept, fegetexcept): New
        declarations.
diff --git a/modules/fenv-exceptions-trapping-tests b/modules/fenv-exceptions-trapping-tests
new file mode 100644 (file)
index 0000000..21f1784
--- /dev/null
@@ -0,0 +1,25 @@
+Files:
+tests/test-fenv-except-trapping-1.c
+tests/test-fenv-except-trapping-2.sh
+tests/test-fenv-except-trapping-2.c
+tests/infinity.h
+tests/macros.h
+m4/musl.m4
+
+Depends-on:
+fenv-exceptions-tracking-c99
+nan
+snan
+
+configure.ac:
+gl_MUSL_LIBC
+
+Makefile.am:
+TESTS += \
+  test-fenv-except-trapping-1 \
+  test-fenv-except-trapping-2.sh
+check_PROGRAMS += \
+  test-fenv-except-trapping-1 \
+  test-fenv-except-trapping-2
+test_fenv_except_trapping_1_LDADD = $(LDADD) @FENV_EXCEPTIONS_TRAPPING_LIBM@
+test_fenv_except_trapping_2_LDADD = $(LDADD) @FENV_EXCEPTIONS_TRAPPING_LIBM@ @FENV_EXCEPTIONS_TRACKING_LIBM@
diff --git a/tests/test-fenv-except-trapping-1.c b/tests/test-fenv-except-trapping-1.c
new file mode 100644 (file)
index 0000000..7c67a06
--- /dev/null
@@ -0,0 +1,69 @@
+/* Test of turning floating-point exceptions into traps (signals).
+   Copyright (C) 2023 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2023.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <fenv.h>
+
+#include <stdio.h>
+
+#include "macros.h"
+
+/* Check of the Boole-an algebra.  */
+
+/* Converts 5 bits to an exceptions bit mask.  */
+static int
+uint_to_exceptions (unsigned int a)
+{
+  return (a & 0x01 ? FE_INVALID : 0)
+         | (a & 0x02 ? FE_DIVBYZERO : 0)
+         | (a & 0x04 ? FE_OVERFLOW : 0)
+         | (a & 0x08 ? FE_UNDERFLOW : 0)
+         | (a & 0x10 ? FE_INEXACT : 0);
+}
+
+int
+main ()
+{
+  unsigned int a, b;
+
+  /* Run through all possible valid arguments to feenableexcept and
+     fedisableexcept.  */
+  for (a = 0; a < 0x20; a++)
+    for (b = 0; b < 0x20; b++)
+      {
+        unsigned int c = a & ~b;
+
+        if (fedisableexcept (FE_ALL_EXCEPT) == -1
+            || feenableexcept (uint_to_exceptions (a)) == -1)
+          {
+            fputs ("Skipping test: trapping floating-point exceptions are not supported on this machine.\n", stderr);
+            return 77;
+          }
+        ASSERT (fedisableexcept (uint_to_exceptions (b))
+                == uint_to_exceptions (a));
+        /* Check fegetexcept.  */
+        ASSERT (fegetexcept () == uint_to_exceptions (c));
+        /* Check the return value of feenableexcept.  It should be consistent
+           with fegetexcept.  */
+        ASSERT (feenableexcept (0) == uint_to_exceptions (c));
+      }
+
+  return 0;
+}
diff --git a/tests/test-fenv-except-trapping-2.c b/tests/test-fenv-except-trapping-2.c
new file mode 100644 (file)
index 0000000..18f42b8
--- /dev/null
@@ -0,0 +1,488 @@
+/* Test of turning floating-point exceptions into traps (signals).
+   Copyright (C) 2023 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2023.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <fenv.h>
+
+#include <stdio.h>
+
+#include "infinity.h"
+#include "snan.h"
+#include "macros.h"
+
+/* Operations that should raise various floating-point exceptions.  */
+
+/* For a list, see the glibc documentation
+   <https://www.gnu.org/software/libc/manual/html_node/FP-Exceptions.html>  */
+
+#define infinityf my_infinityf /* Avoid collision with Cygwin's <math.h> */
+static float volatile infinityf, qnanf;
+static double volatile infinityd, qnand;
+static long double volatile infinityl, qnanl;
+
+static void
+raise_invalid_addition (char type)
+{
+  switch (type)
+    {
+    case 'f':
+      {
+        float volatile a, b;
+        _GL_UNUSED float volatile c;
+        a = infinityf; b = - infinityf;
+        c = a + b;
+      }
+      break;
+    case 'd':
+      {
+        double volatile a, b;
+        _GL_UNUSED double volatile c;
+        a = infinityd; b = - infinityd;
+        c = a + b;
+      }
+      break;
+    case 'l':
+      {
+        long double volatile a, b;
+        _GL_UNUSED long double volatile c;
+        a = infinityl; b = - infinityl;
+        c = a + b;
+      }
+      break;
+    }
+}
+
+static void
+raise_invalid_multiplication (char type)
+{
+  switch (type)
+    {
+    case 'f':
+      {
+        float volatile a, b;
+        _GL_UNUSED float volatile c;
+        a = infinityf; b = 0.0f;
+        c = a * b;
+      }
+      break;
+    case 'd':
+      {
+        double volatile a, b;
+        _GL_UNUSED double volatile c;
+        a = infinityd; b = 0.0;
+        c = a * b;
+      }
+      break;
+    case 'l':
+      {
+        long double volatile a, b;
+        _GL_UNUSED long double volatile c;
+        a = infinityl; b = 0.0L;
+        c = a * b;
+      }
+      break;
+    }
+}
+
+static void
+raise_invalid_division (char type)
+{
+  switch (type)
+    {
+    case 'f':
+      {
+        float volatile a, b;
+        _GL_UNUSED float volatile c;
+        a = infinityf; b = - infinityf;
+        c = a / b;
+      }
+      break;
+    case 'd':
+      {
+        double volatile a, b;
+        _GL_UNUSED double volatile c;
+        a = infinityd; b = - infinityd;
+        c = a / b;
+      }
+      break;
+    case 'l':
+      {
+        long double volatile a, b;
+        _GL_UNUSED long double volatile c;
+        a = infinityl; b = - infinityl;
+        c = a / b;
+      }
+      break;
+    }
+}
+
+static void
+raise_invalid_comparison (char type)
+{
+  switch (type)
+    {
+    case 'f':
+      {
+        float volatile a, b;
+        _GL_UNUSED int volatile c;
+        a = qnanf; b = qnanf;
+        c = a > b;
+      }
+      break;
+    case 'd':
+      {
+        double volatile a, b;
+        _GL_UNUSED int volatile c;
+        a = qnand; b = qnand;
+        c = a > b;
+      }
+      break;
+    case 'l':
+      {
+        long double volatile a, b;
+        _GL_UNUSED int volatile c;
+        a = qnanl; b = qnanl;
+        c = a > b;
+      }
+      break;
+    }
+}
+
+static void
+raise_invalid_snan (char type)
+{
+  switch (type)
+    {
+    case 'f':
+      {
+        float volatile a, b;
+        _GL_UNUSED float volatile c;
+        a = SNaNf (); b = 1.0f;
+        c = a + b;
+      }
+      break;
+    case 'd':
+      {
+        double volatile a, b;
+        _GL_UNUSED double volatile c;
+        a = SNaNd (); b = 1.0;
+        c = a + b;
+      }
+      break;
+    case 'l':
+      {
+        long double volatile a, b;
+        _GL_UNUSED long double volatile c;
+        a = SNaNl (); b = 1.0L;
+        c = a + b;
+      }
+      break;
+    }
+}
+
+static void
+raise_divbyzero (char type)
+{
+  switch (type)
+    {
+    case 'f':
+      {
+        float volatile a, b;
+        _GL_UNUSED float volatile c;
+        a = 2.5f; b = - 0.0f;
+        c = a / b;
+      }
+      break;
+    case 'd':
+      {
+        double volatile a, b;
+        _GL_UNUSED double volatile c;
+        a = 2.5; b = - 0.0;
+        c = a / b;
+      }
+      break;
+    case 'l':
+      {
+        long double volatile a, b;
+        _GL_UNUSED long double volatile c;
+        a = 2.5L; b = - 0.0L;
+        c = a / b;
+      }
+      break;
+    }
+}
+
+static void
+raise_overflow (char type)
+{
+  switch (type)
+    {
+    case 'f':
+      {
+        float volatile a, b;
+        _GL_UNUSED float volatile c;
+        a = 1e20f; b = 1e30f;
+        c = a * b;
+      }
+      break;
+    case 'd':
+      {
+        double volatile a, b;
+        _GL_UNUSED double volatile c;
+        a = 1e160; b = 1e260;
+        c = a * b;
+      }
+      break;
+    case 'l':
+      {
+        long double volatile a, b;
+        _GL_UNUSED long double volatile c;
+        a = 1e200L; b = 1e300L;
+        c = a * b;
+        c = c * c;
+        c = c * c;
+        c = c * c;
+        c = c * c;
+      }
+      break;
+    }
+}
+
+static void
+raise_underflow (char type)
+{
+  switch (type)
+    {
+    case 'f':
+      {
+        float volatile a, b;
+        _GL_UNUSED float volatile c;
+        a = 1e-20f; b = 1e-30f;
+        c = a * b;
+      }
+      break;
+    case 'd':
+      {
+        double volatile a, b;
+        _GL_UNUSED double volatile c;
+        a = 1e-160; b = 1e-260;
+        c = a * b;
+      }
+      break;
+    case 'l':
+      {
+        long double volatile a, b;
+        _GL_UNUSED long double volatile c;
+        a = 1e-200L; b = 1e-300L;
+        c = a * b;
+        c = c * c;
+        c = c * c;
+        c = c * c;
+        c = c * c;
+      }
+      break;
+    }
+}
+
+static void
+raise_inexact (char type)
+{
+  switch (type)
+    {
+    case 'f':
+      {
+        float volatile a, b;
+        _GL_UNUSED float volatile c;
+        a = 2.5f; b = 3.0f;
+        c = a / b;
+      }
+      break;
+    case 'd':
+      {
+        double volatile a, b;
+        _GL_UNUSED double volatile c;
+        a = 2.5; b = 3.0;
+        c = a / b;
+      }
+      break;
+    case 'l':
+      {
+        long double volatile a, b;
+        _GL_UNUSED long double volatile c;
+        a = 2.5L; b = 3.0L;
+        c = a / b;
+      }
+      break;
+    }
+}
+
+int
+main (int argc, char *argv[])
+{
+  if (argc > 3)
+    {
+      const char *operation_arg = argv[1];
+      const char *procedure_arg = argv[2];
+      const char *type_arg = argv[3];
+
+      void (*operation) (char) = NULL;
+      int expected_exceptions = 0;
+      int possible_exceptions = 0;
+
+      /* Preparations (to be executed before we call feenableexcept).  */
+      infinityf = Infinityf ();
+      qnanf = NaNf ();
+      infinityd = Infinityd ();
+      qnand = NaNd ();
+      infinityl = Infinityl ();
+      qnanl = NaNl ();
+      feclearexcept (FE_ALL_EXCEPT);
+
+      /* Analyze the operation argument.  */
+      switch (operation_arg[0])
+        {
+        case '1':
+          operation = raise_invalid_addition;
+          expected_exceptions = FE_INVALID;
+          break;
+        case '2':
+          operation = raise_invalid_multiplication;
+          expected_exceptions = FE_INVALID;
+          break;
+        case '3':
+          operation = raise_invalid_division;
+          expected_exceptions = FE_INVALID;
+          break;
+        case '4':
+          operation = raise_invalid_comparison;
+          expected_exceptions = FE_INVALID;
+          break;
+        case '5':
+          operation = raise_invalid_snan;
+          expected_exceptions = FE_INVALID;
+          break;
+        case '6':
+          operation = raise_divbyzero;
+          expected_exceptions = FE_DIVBYZERO;
+          break;
+        case '7':
+          operation = raise_overflow;
+          expected_exceptions = FE_OVERFLOW;
+          possible_exceptions = FE_INEXACT;
+          break;
+        case '8':
+          operation = raise_underflow;
+          expected_exceptions = FE_UNDERFLOW;
+          possible_exceptions = FE_INEXACT;
+          break;
+        case '9':
+          operation = raise_inexact;
+          expected_exceptions = FE_INEXACT;
+          break;
+        }
+
+      /* Analyze the procedure argument.  */
+      switch (procedure_arg[0])
+        {
+        /* These three procedures should lead to a trap.  */
+        case 'p':
+          if (feenableexcept (expected_exceptions) == -1)
+            goto skip;
+          break;
+        case 'q':
+          if (feenableexcept (FE_ALL_EXCEPT) == -1)
+            goto skip;
+          break;
+        case 'r':
+          if (feenableexcept (FE_ALL_EXCEPT) == -1)
+            goto skip;
+          ASSERT (fedisableexcept (FE_ALL_EXCEPT & ~expected_exceptions)
+                  == FE_ALL_EXCEPT);
+          break;
+        /* This procedure should *not* lead to a trap.  */
+        case 's':
+          if (feenableexcept (FE_ALL_EXCEPT & ~(expected_exceptions | possible_exceptions)) == -1)
+            goto skip;
+          break;
+        }
+
+      /* Avoid known test failures.  */
+      int known_failure = 0;
+      /* The '4' tests do not work on Linux/powerpc* and Linux/s390*, as well as
+         on GNU/kFreeBSD/i386, GNU/kFreeBSD/x86_64, musl libc/i386,
+         musl libc/powerpc64le, macOS/i386, macOS/x86_64, macOS/arm64,
+         FreeBSD/i386, FreeBSD/x86_64, NetBSD/i386, NetBSD/x86_64, OpenBSD/i386,
+         OpenBSD/x86_64, AIX/powerpc, Solaris/i386, Solaris/x86_64,
+         Cygwin/x86_64, native Windows/i386,
+         native Windows/x86_64.  */
+      #if (__GLIBC__ >= 2 && (defined __powerpc__ || (defined __s390__ || defined __s390x__))) \
+          || (__GLIBC__ >= 2 && __FreeBSD_kernel__ && ((defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86))) \
+          || (defined MUSL_LIBC && ((defined __i386 || defined _M_IX86) || defined __powerpc__)) \
+          || ((defined __APPLE__ && defined __MACH__) && ((defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86) || defined __aarch64__)) \
+          || ((defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__) && ((defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86))) \
+          || (defined _AIX && defined __powerpc__) \
+          || (defined __sun && ((defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86))) \
+          || (defined __CYGWIN__ && (defined __x86_64__ || defined _M_X64)) \
+          || (defined _WIN32 && ((defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86)))
+      known_failure |= (operation_arg[0] == '4');
+      #endif
+      /* The '7' and '8' tests, with types 'f' and 'd', do not work reliably
+         on Linux/i386.  */
+      #if defined __i386 || defined _M_IX86
+      known_failure |= (operation_arg[0] == '7' || operation_arg[0] == '8');
+      #endif
+      /* The '9' tests do not work on Linux/alpha, musl libc/powerpc64le,
+         AIX/powerpc.  */
+      #if (__GLIBC__ >= 2 && defined __alpha) \
+          || ((defined MUSL_LIBC || defined _AIX) && defined __powerpc__)
+      known_failure |= (operation_arg[0] == '9');
+      #endif
+      /* The 'l' tests do not work on Linux/loongarch64 with glibc 2.37.
+         Likewise on Linux/alpha with glibc 2.7 on Linux 2.6.26.
+         Likewise on FreeBSD 12.2/sparc.
+         Cause unknown.  */
+      #if (__GLIBC__ >= 2 && defined __loongarch__) \
+          || ((__GLIBC__ == 2 && __GLIBC_MINOR__ < 36) && defined __alpha) \
+          || (defined __FreeBSD__ && defined __sparc)
+      known_failure |= (type_arg[0] == 'l');
+      #endif
+      if (known_failure)
+        {
+          fputs ("Skipping test: known failure on this platform\n", stderr);
+          return 77;
+        }
+
+      /* Analyze the type argument.  */
+      switch (type_arg[0])
+        {
+        case 'f':
+        case 'd':
+        case 'l':
+          operation (type_arg[0]);
+          break;
+        }
+    }
+
+  return 0;
+
+ skip:
+  fputs ("Skipping test: trapping floating-point exceptions are not supported on this machine.\n", stderr);
+  return 77;
+}
diff --git a/tests/test-fenv-except-trapping-2.sh b/tests/test-fenv-except-trapping-2.sh
new file mode 100755 (executable)
index 0000000..d51197f
--- /dev/null
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+# Test of turning floating-point exceptions into traps (signals).
+
+final_rc=0
+
+for operation in 1 2 3 4 5 6 7 8 9; do
+  for procedure in p q r s; do
+    for type in f d l; do
+      ${CHECKER} ./test-fenv-except-trapping-2${EXEEXT} $operation $procedure $type
+      rc=$?
+      if test $rc = 77; then
+        if test $final_rc = 0; then
+          final_rc=77
+        fi
+      else
+        if test $procedure = s; then
+          if test $rc != 0; then
+            echo "Failed (got a trap): ./test-fenv-except-trapping-2 $operation $procedure $type" 1>&2
+            final_rc=1
+          fi
+        else
+          if test $rc = 0; then
+            echo "Failed (got no trap): ./test-fenv-except-trapping-2 $operation $procedure $type" 1>&2
+            final_rc=1
+          fi
+        fi
+      fi
+    done
+  done
+done
+
+exit $final_rc