The unit tests of module nstrftime, so far, test only a small subset of the
format directives, and only in the "C" locale. Time to extend these tests.


2024-02-07  Bruno Haible  <[email protected]>

        nstrftime: Add tests of all directives, also in non-trivial locales.
        * tests/test-nstrftime.h (language_t): New type.
        (locales_test): New function.
        * tests/test-nstrftime.c: Include <locale.h>, <stdlib.h>.
        (main): Invoke setlocale and locales_test.
        * tests/test-nstrftime-1.sh: New file.
        * tests/test-nstrftime-2.sh: New file, based on tests/test-strtod1.sh.
        * modules/nstrftime-tests (Files): Add them. Add m4/locale-fr.m4,
        m4/codeset.m4, m4/musl.m4.
        (configure.ac): Invoke gt_LOCALE_FR, gt_LOCALE_FR_UTF8, gl_MUSL_LIBC.
        (Makefile.am): Link test-nstrftime with $(SETLOCALE_LIB). Test
        test-nstrftime-*.sh instead of test-nstrftime.

diff --git a/modules/nstrftime-tests b/modules/nstrftime-tests
index b30ebbbbae..24212a4f84 100644
--- a/modules/nstrftime-tests
+++ b/modules/nstrftime-tests
@@ -1,7 +1,12 @@
 Files:
+tests/test-nstrftime-1.sh
+tests/test-nstrftime-2.sh
 tests/test-nstrftime.c
 tests/test-nstrftime.h
 tests/macros.h
+m4/locale-fr.m4
+m4/codeset.m4
+m4/musl.m4
 
 Depends-on:
 atoll
@@ -10,7 +15,14 @@ intprops
 strerror
 
 configure.ac:
+gt_LOCALE_FR
+gt_LOCALE_FR_UTF8
+gl_MUSL_LIBC
 
 Makefile.am:
-TESTS += test-nstrftime
+TESTS += test-nstrftime-1.sh test-nstrftime-2.sh
+TESTS_ENVIRONMENT += \
+  LOCALE_FR='@LOCALE_FR@' \
+  LOCALE_FR_UTF8='@LOCALE_FR_UTF8@'
 check_PROGRAMS += test-nstrftime
+test_nstrftime_LDADD = $(LDADD) $(SETLOCALE_LIB)
diff --git a/tests/test-nstrftime-1.sh b/tests/test-nstrftime-1.sh
new file mode 100755
index 0000000000..c0c4665b7e
--- /dev/null
+++ b/tests/test-nstrftime-1.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+LC_ALL=C ${CHECKER} ./test-nstrftime${EXEEXT} || exit 1
+
+exit 0
diff --git a/tests/test-nstrftime-2.sh b/tests/test-nstrftime-2.sh
new file mode 100755
index 0000000000..c0ce75c339
--- /dev/null
+++ b/tests/test-nstrftime-2.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+: "${LOCALE_FR=fr_FR}"
+: "${LOCALE_FR_UTF8=fr_FR.UTF-8}"
+
+if test $LOCALE_FR = none && test $LOCALE_FR_UTF8 = none; then
+  if test -f /usr/bin/localedef; then
+    echo "Skipping test: no locale for testing is installed"
+  else
+    echo "Skipping test: no locale for testing is supported"
+  fi
+  exit 77
+fi
+
+final_rc=0
+
+if test $LOCALE_FR != none; then
+  LC_ALL=$LOCALE_FR      ${CHECKER} ./test-nstrftime${EXEEXT}
+  rc=$?
+  if test $rc = 77; then
+    final_rc=77
+  else
+    if test $rc != 0; then
+      exit 1
+    fi
+  fi
+fi
+
+if test $LOCALE_FR_UTF8 != none; then
+  LC_ALL=$LOCALE_FR_UTF8 ${CHECKER} ./test-nstrftime${EXEEXT}
+  rc=$?
+  if test $rc = 77; then
+    final_rc=77
+  else
+    if test $rc != 0; then
+      exit 1
+    fi
+  fi
+fi
+
+exit $final_rc
diff --git a/tests/test-nstrftime.c b/tests/test-nstrftime.c
index 3a76298f5e..4074874366 100644
--- a/tests/test-nstrftime.c
+++ b/tests/test-nstrftime.c
@@ -18,8 +18,12 @@
 
 #include <config.h>
 
+/* Specification.  */
 #include "strftime.h"
 
+#include <locale.h>
+#include <stdlib.h>
+
 #include "intprops.h"
 
 #include <errno.h>
@@ -37,10 +41,33 @@
 int
 main (void)
 {
+  /* Try to set the locale by implicitly looking at the LC_ALL environment
+     variable.
+     configure should already have checked that the locale is supported.  */
+  if (setlocale (LC_ALL, "") == NULL)
+    return 1;
+
+  bool is_C_locale = STREQ (getenv ("LC_ALL"), "C");
+
   int fail = 0;
   fail |= posixtm_test ();
   fail |= tzalloc_test ();
   fail |= quarter_test ();
   fail |= errno_test ();
+  if (is_C_locale)
+    fail |= locales_test (english);
+  else
+    {
+#if MUSL_LIBC
+      if (fail == 0)
+        {
+          fputs ("Skipping test: musl libc does not come with localizations\n",
+                 stderr);
+          return 77;
+        }
+#else
+      fail |= locales_test (french);
+#endif
+    }
   return fail;
 }
diff --git a/tests/test-nstrftime.h b/tests/test-nstrftime.h
index 97190a6654..5c54266043 100644
--- a/tests/test-nstrftime.h
+++ b/tests/test-nstrftime.h
@@ -1,4 +1,4 @@
-/* Test of nstrftime-like functions in the "C" locale.
+/* Test of nstrftime-like functions.
    Copyright (C) 2011-2024 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
@@ -327,6 +327,341 @@ errno_test (void)
 
 /* -------------------------------------------------------------------------- 
*/
 
+/* Test various format directives.  */
+
+typedef enum { english, french } language_t;
+
+static int
+locales_test (language_t language)
+{
+  int fail = 0;
+
+  time_t t = 1509000003;
+  struct tm *tm = gmtime (&t);
+  int ns = 123456789;
+  char buf[100];
+  size_t n;
+
+  n = FUNC (buf, sizeof buf, "%+4Y-%m-%d %H:%M:%S.%N", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("Format as ISO 8601: %s\n", buf);
+  ASSERT (STREQ (buf, "2017-10-26 06:40:03.123456789"));
+
+  /* Exercise various POSIX format directives.  */
+
+  n = FUNC (buf, sizeof buf, "%a", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%a directive: %s\n", buf);
+  switch (language)
+    {
+    case english:
+      ASSERT (STREQ (buf, "Thu"));
+      break;
+    case french:
+      ASSERT (STREQ (buf, "jeu.") /* glibc, FreeBSD, NetBSD, Solaris, Cygwin */
+              || STREQ (buf, "Jeu") /* macOS, older FreeBSD */);
+      break;
+    }
+
+  n = FUNC (buf, sizeof buf, "%A", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%A directive: %s\n", buf);
+  switch (language)
+    {
+    case english:
+      ASSERT (STREQ (buf, "Thursday"));
+      break;
+    case french:
+      ASSERT (STREQ (buf, "jeudi") /* glibc, FreeBSD, NetBSD, Solaris, Cygwin 
*/
+              || STREQ (buf, "Jeudi") /* macOS, older FreeBSD */);
+      break;
+    }
+
+  n = FUNC (buf, sizeof buf, "%b", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%b directive: %s\n", buf);
+  switch (language)
+    {
+    case english:
+      ASSERT (STREQ (buf, "Oct"));
+      break;
+    case french:
+      ASSERT (STREQ (buf, "oct.") /* glibc, FreeBSD, NetBSD, Solaris, Cygwin */
+              || STREQ (buf, "oct") /* macOS, older FreeBSD */);
+      break;
+    }
+
+  n = FUNC (buf, sizeof buf, "%B", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%B directive: %s\n", buf);
+  switch (language)
+    {
+    case english:
+      ASSERT (STREQ (buf, "October"));
+      break;
+    case french:
+      ASSERT (STREQ (buf, "octobre"));
+      break;
+    }
+
+  n = FUNC (buf, sizeof buf, "%c", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%c directive: %s\n", buf);
+  switch (language)
+    {
+    case english:
+      ASSERT (STREQ (buf, "Thu Oct 26 06:40:03 2017"));
+      break;
+    case french:
+      ASSERT (STREQ (buf, "jeu. 26 oct. 2017 06:40:03") /* glibc, Cygwin */
+              || STREQ (buf, "jeu. 26 oct. 06:40:03 2017") /* FreeBSD */
+              || STREQ (buf, "Jeu 26 oct 06:40:03 2017") /* macOS, older 
FreeBSD */
+              || STREQ (buf, "26 octobre 2017 06:40:03") /* NetBSD */
+              || STREQ (buf, "26 octobre 2017 à 06:40:03") /* Solaris (UTF-8) 
*/
+              || STREQ (buf, "26 octobre 2017 \340 06:40:03") /* Solaris 
(ISO-8859-1) */);
+      break;
+    }
+
+  n = FUNC (buf, sizeof buf, "%C", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%C directive: %s\n", buf);
+  ASSERT (STREQ (buf, "20"));
+
+  n = FUNC (buf, sizeof buf, "%d", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%d directive: %s\n", buf);
+  ASSERT (STREQ (buf, "26"));
+
+  n = FUNC (buf, sizeof buf, "%D", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%D directive: %s\n", buf);
+  ASSERT (STREQ (buf, "10/26/17"));
+
+  n = FUNC (buf, sizeof buf, "%e", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%e directive: %s\n", buf);
+  ASSERT (STREQ (buf, "26"));
+
+  n = FUNC (buf, sizeof buf, "%F", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%F directive: %s\n", buf);
+  ASSERT (STREQ (buf, "2017-10-26"));
+
+  n = FUNC (buf, sizeof buf, "%g", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%g directive: %s\n", buf);
+  ASSERT (STREQ (buf, "17"));
+
+  n = FUNC (buf, sizeof buf, "%G", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%G directive: %s\n", buf);
+  ASSERT (STREQ (buf, "2017"));
+
+  n = FUNC (buf, sizeof buf, "%h", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%h directive: %s\n", buf);
+  switch (language)
+    {
+    case english:
+      ASSERT (STREQ (buf, "Oct"));
+      break;
+    case french:
+      ASSERT (STREQ (buf, "oct.") /* glibc, FreeBSD, NetBSD, Solaris, Cygwin */
+              || STREQ (buf, "oct") /* macOS, older FreeBSD */);
+      break;
+    }
+
+  n = FUNC (buf, sizeof buf, "%H", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%H directive: %s\n", buf);
+  ASSERT (STREQ (buf, "06"));
+
+  n = FUNC (buf, sizeof buf, "%I", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%I directive: %s\n", buf);
+  ASSERT (STREQ (buf, "06"));
+
+  n = FUNC (buf, sizeof buf, "%j", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%j directive: %s\n", buf);
+  ASSERT (STREQ (buf, "299"));
+
+  n = FUNC (buf, sizeof buf, "%m", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%m directive: %s\n", buf);
+  ASSERT (STREQ (buf, "10"));
+
+  n = FUNC (buf, sizeof buf, "%M", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%M directive: %s\n", buf);
+  ASSERT (STREQ (buf, "40"));
+
+  n = FUNC (buf, sizeof buf, "%n", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%n directive: %s\n", buf);
+  ASSERT (STREQ (buf, "\n"));
+
+  n = FUNC (buf, sizeof buf, "%p", tm, 0, ns);
+  switch (language)
+    {
+    case english:
+      ASSERT (n > 0);
+      printf ("%%p directive: %s\n", buf);
+      ASSERT (STREQ (buf, "AM"));
+      break;
+    case french:
+      ASSERT (n == 0);
+      break;
+    }
+
+  n = FUNC (buf, sizeof buf, "%r", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%r directive: %s\n", buf);
+  switch (language)
+    {
+    case english:
+      ASSERT (STREQ (buf, "06:40:03 AM"));
+      break;
+    case french:
+      ASSERT (STREQ (buf, "06:40:03 ") /* glibc */
+              || STREQ (buf, "06:40:03") /* Cygwin */
+              || STREQ (buf, "06:40:03 AM") /* NetBSD */
+              || STREQ (buf, " 6:40:03 AM") /* Solaris */);
+      break;
+    }
+
+  n = FUNC (buf, sizeof buf, "%R", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%R directive: %s\n", buf);
+  ASSERT (STREQ (buf, "06:40"));
+
+  n = FUNC (buf, sizeof buf, "%S", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%S directive: %s\n", buf);
+  ASSERT (STREQ (buf, "03"));
+
+  n = FUNC (buf, sizeof buf, "%t", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%t directive: %s\n", buf);
+  ASSERT (STREQ (buf, "\t"));
+
+  n = FUNC (buf, sizeof buf, "%T", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%T directive: %s\n", buf);
+  ASSERT (STREQ (buf, "06:40:03"));
+
+  n = FUNC (buf, sizeof buf, "%u", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%u directive: %s\n", buf);
+  ASSERT (STREQ (buf, "4"));
+
+  n = FUNC (buf, sizeof buf, "%U", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%U directive: %s\n", buf);
+  ASSERT (STREQ (buf, "43"));
+
+  n = FUNC (buf, sizeof buf, "%V", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%V directive: %s\n", buf);
+  ASSERT (STREQ (buf, "43"));
+
+  n = FUNC (buf, sizeof buf, "%w", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%w directive: %s\n", buf);
+  ASSERT (STREQ (buf, "4"));
+
+  n = FUNC (buf, sizeof buf, "%W", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%W directive: %s\n", buf);
+  ASSERT (STREQ (buf, "43"));
+
+  n = FUNC (buf, sizeof buf, "%x", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%x directive: %s\n", buf);
+  switch (language)
+    {
+    case english:
+      ASSERT (STREQ (buf, "10/26/17"));
+      break;
+    case french:
+      ASSERT (STREQ (buf, "26/10/2017") /* glibc, NetBSD, Solaris, Cygwin */
+              || STREQ (buf, "26.10.2017") /* FreeBSD, macOS */);
+      break;
+    }
+
+  n = FUNC (buf, sizeof buf, "%X", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%X directive: %s\n", buf);
+  ASSERT (STREQ (buf, "06:40:03"));
+
+  n = FUNC (buf, sizeof buf, "%y", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%y directive: %s\n", buf);
+  ASSERT (STREQ (buf, "17"));
+
+  n = FUNC (buf, sizeof buf, "%Y", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%Y directive: %s\n", buf);
+  ASSERT (STREQ (buf, "2017"));
+
+  n = FUNC (buf, sizeof buf, "%z", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%z directive: %s\n", buf);
+  ASSERT (STREQ (buf, "+0000"));
+
+  n = FUNC (buf, sizeof buf, "%Z", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%Z directive: %s\n", buf);
+  ASSERT (STREQ (buf, "GMT") /* glibc, NetBSD, OpenBSD, AIX, Solaris, Cygwin, 
Android */
+          || STREQ (buf, "UTC") /* musl, macOS, FreeBSD */);
+
+  n = FUNC (buf, sizeof buf, "%%", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%%% directive: %s\n", buf);
+  ASSERT (STREQ (buf, "%"));
+
+  /* Exercise various GNU extensions from glibc.  */
+
+  n = FUNC (buf, sizeof buf, "%k", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%k directive: %s\n", buf);
+  ASSERT (STREQ (buf, " 6"));
+
+  n = FUNC (buf, sizeof buf, "%l", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%l directive: %s\n", buf);
+  ASSERT (STREQ (buf, " 6"));
+
+  n = FUNC (buf, sizeof buf, "%P", tm, 0, ns);
+  switch (language)
+    {
+    case english:
+      ASSERT (n > 0);
+      printf ("%%P directive: %s\n", buf);
+      ASSERT (STREQ (buf, "am"));
+      break;
+    case french:
+      ASSERT (n == 0);
+      break;
+    }
+
+  n = FUNC (buf, sizeof buf, "%s", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%s directive: %s\n", buf);
+  ASSERT (STREQ (buf, "1509000003"));
+
+  /* Exercise various GNU extensions from gnulib.  */
+
+  n = FUNC (buf, sizeof buf, "%N", tm, 0, ns);
+  ASSERT (n > 0);
+  printf ("%%N directive: %s\n", buf);
+  ASSERT (STREQ (buf, "123456789"));
+
+  return fail;
+}
+
+/* -------------------------------------------------------------------------- 
*/
+
 /*
 Local Variables:
 indent-tabs-mode: nil




Reply via email to