Add TEST_COMPARE_STRING_WIDE to support/check.h

Message ID alpine.DEB.2.22.394.2110062143330.3641536@digraph.polyomino.org.uk
State Committed
Commit de82cb0da4b8fa5b3d56c457438d2568c67ab1b1
Headers
Series Add TEST_COMPARE_STRING_WIDE to support/check.h |

Checks

Context Check Description
dj/TryBot-32bit success Build for i686
dj/TryBot-apply_patch success Patch applied to master at the time it was sent

Commit Message

Joseph Myers Oct. 6, 2021, 9:44 p.m. UTC
  I'd like to be able to test narrow and wide string interfaces, with
the narrow string tests using TEST_COMPARE_STRING and the wide string
tests using something analogous (possibly generated using macros from
a common test template for both the narrow and wide string tests where
appropriate).

Add such a TEST_COMPARE_STRING_WIDE, along with functions
support_quote_blob_wide and support_test_compare_string_wide that it
builds on.  Those functions are built using macros from common
templates shared by the narrow and wide string implementations, though
I didn't do that for the tests of test functions.  In
support_quote_blob_wide, I chose to use the \x{} delimited escape
sequence syntax proposed for C2X in N2785, rather than e.g. trying to
generate the end of a string and the start of a new string when
ambiguity would result from undelimited \x (when the next character
after such an escape sequence is valid hex) or forcing an escape
sequence to be used for the next character in the case of such
ambiguity.

Tested for x86_64.
  

Comments

Joseph Myers Oct. 11, 2021, 9:42 p.m. UTC | #1
Ping.  This patch 
<https://sourceware.org/pipermail/libc-alpha/2021-October/131722.html> 
(used to implement tests in 
<https://sourceware.org/pipermail/libc-alpha/2021-October/131764.html>) is 
pending review.
  
Florian Weimer Oct. 12, 2021, 6:15 a.m. UTC | #2
* Joseph Myers:

> I'd like to be able to test narrow and wide string interfaces, with
> the narrow string tests using TEST_COMPARE_STRING and the wide string
> tests using something analogous (possibly generated using macros from
> a common test template for both the narrow and wide string tests where
> appropriate).
>
> Add such a TEST_COMPARE_STRING_WIDE, along with functions
> support_quote_blob_wide and support_test_compare_string_wide that it
> builds on.  Those functions are built using macros from common
> templates shared by the narrow and wide string implementations, though
> I didn't do that for the tests of test functions.  In
> support_quote_blob_wide, I chose to use the \x{} delimited escape
> sequence syntax proposed for C2X in N2785, rather than e.g. trying to
> generate the end of a string and the start of a new string when
> ambiguity would result from undelimited \x (when the next character
> after such an escape sequence is valid hex) or forcing an escape
> sequence to be used for the next character in the case of such
> ambiguity.

Patch looks okay to me.  I don't like these preprocessor hacks, but I
don't see any other way to avoid the code duplication.
  

Patch

diff --git a/support/Makefile b/support/Makefile
index fd27c8451e..7f03950914 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -71,6 +71,7 @@  libsupport-routines = \
   support_openpty \
   support_paths \
   support_quote_blob \
+  support_quote_blob_wide \
   support_quote_string \
   support_record_failure \
   support_run_diff \
@@ -84,6 +85,7 @@  libsupport-routines = \
   support_test_compare_blob \
   support_test_compare_failure \
   support_test_compare_string \
+  support_test_compare_string_wide \
   support_test_main \
   support_test_verify_impl \
   support_wait_for_thread_exit \
@@ -270,11 +272,13 @@  tests = \
   tst-support-open-dev-null-range \
   tst-support-process_state \
   tst-support_quote_blob \
+  tst-support_quote_blob_wide \
   tst-support_quote_string \
   tst-support_record_failure \
   tst-test_compare \
   tst-test_compare_blob \
   tst-test_compare_string \
+  tst-test_compare_string_wide \
   tst-timespec \
   tst-xreadlink \
   tst-xsigstack \
diff --git a/support/check.h b/support/check.h
index 83662b2d10..9b1844352f 100644
--- a/support/check.h
+++ b/support/check.h
@@ -20,6 +20,7 @@ 
 #define SUPPORT_CHECK_H
 
 #include <sys/cdefs.h>
+#include <stddef.h>
 
 __BEGIN_DECLS
 
@@ -171,11 +172,25 @@  void support_test_compare_blob (const void *left,
   (support_test_compare_string (left, right, __FILE__, __LINE__, \
                                 #left, #right))
 
+/* Compare the wide strings LEFT and RIGHT and report a test failure
+   if they are different.  Also report failure if one of the arguments
+   is a null pointer and the other is not.  The strings should be
+   reasonably short because on mismatch, both are printed.  */
+#define TEST_COMPARE_STRING_WIDE(left, right)                         \
+  (support_test_compare_string_wide (left, right, __FILE__, __LINE__, \
+				     #left, #right))
+
 void support_test_compare_string (const char *left, const char *right,
                                   const char *file, int line,
                                   const char *left_expr,
                                   const char *right_expr);
 
+void support_test_compare_string_wide (const wchar_t *left,
+				       const wchar_t *right,
+				       const char *file, int line,
+				       const char *left_expr,
+				       const char *right_expr);
+
 /* Internal function called by the test driver.  */
 int support_report_failure (int status)
   __attribute__ ((weak, warn_unused_result));
diff --git a/support/support.h b/support/support.h
index 837a806531..0ee454da6d 100644
--- a/support/support.h
+++ b/support/support.h
@@ -73,6 +73,12 @@  void support_write_file_string (const char *path, const char *contents);
    the result).  */
 char *support_quote_blob (const void *blob, size_t length);
 
+/* Quote the contents of the wide character array starting at BLOB, of
+   LENGTH wide characters, in such a way that the result string can be
+   included in a C wide string literal (in single/double quotes,
+   without putting the quotes into the result).  */
+char *support_quote_blob_wide (const void *blob, size_t length);
+
 /* Quote the contents of the string, in such a way that the result
    string can be included in a C literal (in single/double quotes,
    without putting the quotes into the result).  */
diff --git a/support/support_quote_blob.c b/support/support_quote_blob.c
index b5e70125f1..611980c9a2 100644
--- a/support/support_quote_blob.c
+++ b/support/support_quote_blob.c
@@ -1,4 +1,4 @@ 
-/* Quote a blob so that it can be used in C literals.
+/* Quote a narrow string blob so that it can be used in C literals.
    Copyright (C) 2018-2021 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
@@ -16,68 +16,9 @@ 
    License along with the GNU C Library; if not, see
    <https://www.gnu.org/licenses/>.  */
 
-#include <support/support.h>
-#include <support/xmemstream.h>
+#define CHAR unsigned char
+#define L_(C) C
+#define SUPPORT_QUOTE_BLOB support_quote_blob
+#define WIDE 0
 
-char *
-support_quote_blob (const void *blob, size_t length)
-{
-  struct xmemstream out;
-  xopen_memstream (&out);
-
-  const unsigned char *p = blob;
-  for (size_t i = 0; i < length; ++i)
-    {
-      unsigned char ch = p[i];
-
-      /* Use C backslash escapes for those control characters for
-         which they are defined.  */
-      switch (ch)
-        {
-          case '\a':
-            putc_unlocked ('\\', out.out);
-            putc_unlocked ('a', out.out);
-            break;
-          case '\b':
-            putc_unlocked ('\\', out.out);
-            putc_unlocked ('b', out.out);
-            break;
-          case '\f':
-            putc_unlocked ('\\', out.out);
-            putc_unlocked ('f', out.out);
-            break;
-          case '\n':
-            putc_unlocked ('\\', out.out);
-            putc_unlocked ('n', out.out);
-            break;
-          case '\r':
-            putc_unlocked ('\\', out.out);
-            putc_unlocked ('r', out.out);
-            break;
-          case '\t':
-            putc_unlocked ('\\', out.out);
-            putc_unlocked ('t', out.out);
-            break;
-          case '\v':
-            putc_unlocked ('\\', out.out);
-            putc_unlocked ('v', out.out);
-            break;
-          case '\\':
-          case '\'':
-          case '\"':
-            putc_unlocked ('\\', out.out);
-            putc_unlocked (ch, out.out);
-            break;
-        default:
-          if (ch < ' ' || ch > '~')
-            /* Use octal sequences because they are fixed width,
-               unlike hexadecimal sequences.  */
-            fprintf (out.out, "\\%03o", ch);
-          else
-            putc_unlocked (ch, out.out);
-        }
-    }
-
-  xfclose_memstream (&out);
-  return out.buffer;
-}
+#include "support_quote_blob_main.c"
diff --git a/support/support_quote_blob_main.c b/support/support_quote_blob_main.c
new file mode 100644
index 0000000000..19ccfad593
--- /dev/null
+++ b/support/support_quote_blob_main.c
@@ -0,0 +1,88 @@ 
+/* Quote a blob so that it can be used in C literals.
+   Copyright (C) 2018-2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <support/support.h>
+#include <support/xmemstream.h>
+
+char *
+SUPPORT_QUOTE_BLOB (const void *blob, size_t length)
+{
+  struct xmemstream out;
+  xopen_memstream (&out);
+
+  const CHAR *p = blob;
+  for (size_t i = 0; i < length; ++i)
+    {
+      CHAR ch = p[i];
+
+      /* Use C backslash escapes for those control characters for
+	 which they are defined.  */
+      switch (ch)
+	{
+	case L_('\a'):
+	  putc_unlocked ('\\', out.out);
+	  putc_unlocked ('a', out.out);
+	  break;
+	case L_('\b'):
+	  putc_unlocked ('\\', out.out);
+	  putc_unlocked ('b', out.out);
+	  break;
+	case L_('\f'):
+	  putc_unlocked ('\\', out.out);
+	  putc_unlocked ('f', out.out);
+	  break;
+	case L_('\n'):
+	  putc_unlocked ('\\', out.out);
+	  putc_unlocked ('n', out.out);
+	  break;
+	case L_('\r'):
+	  putc_unlocked ('\\', out.out);
+	  putc_unlocked ('r', out.out);
+	  break;
+	case L_('\t'):
+	  putc_unlocked ('\\', out.out);
+	  putc_unlocked ('t', out.out);
+	  break;
+	case L_('\v'):
+	  putc_unlocked ('\\', out.out);
+	  putc_unlocked ('v', out.out);
+	  break;
+	case L_('\\'):
+	case L_('\''):
+	case L_('\"'):
+	  putc_unlocked ('\\', out.out);
+	  putc_unlocked (ch, out.out);
+	  break;
+	default:
+	  if (ch < L_(' ') || ch > L_('~'))
+	    /* For narrow characters, use octal sequences because they
+	       are fixed width, unlike hexadecimal sequences.  For
+	       wide characters, use N2785 delimited escape
+	       sequences.  */
+	    if (WIDE)
+	      fprintf (out.out, "\\x{%x}", (unsigned int) ch);
+	    else
+	      fprintf (out.out, "\\%03o", (unsigned int) ch);
+	  else
+	    putc_unlocked (ch, out.out);
+	}
+    }
+
+  xfclose_memstream (&out);
+  return out.buffer;
+}
diff --git a/support/support_quote_blob_wide.c b/support/support_quote_blob_wide.c
new file mode 100644
index 0000000000..c451ed889c
--- /dev/null
+++ b/support/support_quote_blob_wide.c
@@ -0,0 +1,24 @@ 
+/* Quote a wide string blob so that it can be used in C literals.
+   Copyright (C) 2018-2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#define CHAR wchar_t
+#define L_(C) L ## C
+#define SUPPORT_QUOTE_BLOB support_quote_blob_wide
+#define WIDE 1
+
+#include "support_quote_blob_main.c"
diff --git a/support/support_test_compare_string.c b/support/support_test_compare_string.c
index cbeaf7b1ee..12bafe43d4 100644
--- a/support/support_test_compare_string.c
+++ b/support/support_test_compare_string.c
@@ -16,76 +16,13 @@ 
    License along with the GNU C Library; if not, see
    <https://www.gnu.org/licenses/>.  */
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <support/check.h>
-#include <support/support.h>
-#include <support/xmemstream.h>
-
-static void
-report_length (const char *what, const char *str, size_t length)
-{
-  if (str == NULL)
-    printf ("  %s string: NULL\n", what);
-  else
-    printf ("  %s string: %zu bytes\n", what, length);
-}
-
-static void
-report_string (const char *what, const unsigned char *blob,
-               size_t length, const char *expr)
-{
-  if (length > 0)
-    {
-      printf ("  %s (evaluated from %s):\n", what, expr);
-      char *quoted = support_quote_blob (blob, length);
-      printf ("      \"%s\"\n", quoted);
-      free (quoted);
-
-      fputs ("     ", stdout);
-      for (size_t i = 0; i < length; ++i)
-        printf (" %02X", blob[i]);
-      putc ('\n', stdout);
-    }
-}
-
-static size_t
-string_length_or_zero (const char *str)
-{
-  if (str == NULL)
-    return 0;
-  else
-    return strlen (str);
-}
-
-void
-support_test_compare_string (const char *left, const char *right,
-                             const char *file, int line,
-                             const char *left_expr, const char *right_expr)
-{
-  /* Two null pointers are accepted.  */
-  if (left == NULL && right == NULL)
-    return;
-
-  size_t left_length = string_length_or_zero (left);
-  size_t right_length = string_length_or_zero (right);
-
-  if (left_length != right_length || left == NULL || right == NULL
-      || memcmp (left, right, left_length) != 0)
-    {
-      support_record_failure ();
-      printf ("%s:%d: error: string comparison failed\n", file, line);
-      if (left_length == right_length && right != NULL && left != NULL)
-        printf ("  string length: %zu bytes\n", left_length);
-      else
-        {
-          report_length ("left", left, left_length);
-          report_length ("right", right, right_length);
-        }
-      report_string ("left", (const unsigned char *) left,
-                     left_length, left_expr);
-      report_string ("right", (const unsigned char *) right,
-                     right_length, right_expr);
-    }
-}
+#define CHAR char
+#define UCHAR unsigned char
+#define LPREFIX ""
+#define STRLEN strlen
+#define MEMCMP memcmp
+#define SUPPORT_QUOTE_BLOB support_quote_blob
+#define SUPPORT_TEST_COMPARE_STRING support_test_compare_string
+#define WIDE 0
+
+#include "support_test_compare_string_main.c"
diff --git a/support/support_test_compare_string_main.c b/support/support_test_compare_string_main.c
new file mode 100644
index 0000000000..0edc0ca97d
--- /dev/null
+++ b/support/support_test_compare_string_main.c
@@ -0,0 +1,94 @@ 
+/* Check two strings for equality.
+   Copyright (C) 2018-2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/xmemstream.h>
+
+static void
+report_length (const char *what, const CHAR *str, size_t length)
+{
+  if (str == NULL)
+    printf ("  %s string: NULL\n", what);
+  else
+    printf ("  %s string: %zu %s\n", what, length,
+	    WIDE ? "wide characters" : "bytes");
+}
+
+static void
+report_string (const char *what, const UCHAR *blob,
+               size_t length, const char *expr)
+{
+  if (length > 0)
+    {
+      printf ("  %s (evaluated from %s):\n", what, expr);
+      char *quoted = SUPPORT_QUOTE_BLOB (blob, length);
+      printf ("      %s\"%s\"\n", LPREFIX, quoted);
+      free (quoted);
+
+      fputs ("     ", stdout);
+      for (size_t i = 0; i < length; ++i)
+        printf (" %02X", (unsigned int) blob[i]);
+      putc ('\n', stdout);
+    }
+}
+
+static size_t
+string_length_or_zero (const CHAR *str)
+{
+  if (str == NULL)
+    return 0;
+  else
+    return STRLEN (str);
+}
+
+void
+SUPPORT_TEST_COMPARE_STRING (const CHAR *left, const CHAR *right,
+                             const char *file, int line,
+                             const char *left_expr, const char *right_expr)
+{
+  /* Two null pointers are accepted.  */
+  if (left == NULL && right == NULL)
+    return;
+
+  size_t left_length = string_length_or_zero (left);
+  size_t right_length = string_length_or_zero (right);
+
+  if (left_length != right_length || left == NULL || right == NULL
+      || MEMCMP (left, right, left_length) != 0)
+    {
+      support_record_failure ();
+      printf ("%s:%d: error: string comparison failed\n", file, line);
+      if (left_length == right_length && right != NULL && left != NULL)
+        printf ("  string length: %zu %s\n", left_length,
+		WIDE ? "wide characters" : "bytes");
+      else
+        {
+          report_length ("left", left, left_length);
+          report_length ("right", right, right_length);
+        }
+      report_string ("left", (const UCHAR *) left,
+                     left_length, left_expr);
+      report_string ("right", (const UCHAR *) right,
+                     right_length, right_expr);
+    }
+}
diff --git a/support/support_test_compare_string_wide.c b/support/support_test_compare_string_wide.c
new file mode 100644
index 0000000000..88b560b142
--- /dev/null
+++ b/support/support_test_compare_string_wide.c
@@ -0,0 +1,28 @@ 
+/* Check two wide strings for equality.
+   Copyright (C) 2018-2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#define CHAR wchar_t
+#define UCHAR wchar_t
+#define LPREFIX "L"
+#define STRLEN wcslen
+#define MEMCMP wmemcmp
+#define SUPPORT_QUOTE_BLOB support_quote_blob_wide
+#define SUPPORT_TEST_COMPARE_STRING support_test_compare_string_wide
+#define WIDE 1
+
+#include "support_test_compare_string_main.c"
diff --git a/support/tst-support_quote_blob_wide.c b/support/tst-support_quote_blob_wide.c
new file mode 100644
index 0000000000..ea71a1f7f8
--- /dev/null
+++ b/support/tst-support_quote_blob_wide.c
@@ -0,0 +1,66 @@ 
+/* Test the support_quote_blob_wide function.
+   Copyright (C) 2018-2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <support/check.h>
+#include <support/support.h>
+#include <string.h>
+#include <stdlib.h>
+
+static int
+do_test (void)
+{
+  /* Check handling of the empty blob, both with and without trailing
+     NUL byte.  */
+  char *p = support_quote_blob_wide (L"", 0);
+  TEST_COMPARE (strlen (p), 0);
+  free (p);
+  p = support_quote_blob_wide (L"X", 0);
+  TEST_COMPARE (strlen (p), 0);
+  free (p);
+
+  /* Check escaping of backslash-escaped characters, and lack of
+     escaping for other shell meta-characters.  */
+  p = support_quote_blob_wide (L"$()*?`@[]{}~\'\"X", 14);
+  TEST_COMPARE (strcmp (p, "$()*?`@[]{}~\\'\\\""), 0);
+  free (p);
+
+  /* Check lack of escaping for letters and digits.  */
+#define LETTERS_AND_DIGTS                       \
+  "abcdefghijklmnopqrstuvwxyz"                  \
+  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"                  \
+  "0123456789"
+#define CONCATX(X, Y) X ## Y
+#define CONCAT(X, Y) CONCATX (X, Y)
+#define WLETTERS_AND_DIGTS CONCAT (L, LETTERS_AND_DIGTS)
+  p = support_quote_blob_wide (WLETTERS_AND_DIGTS "@", 2 * 26 + 10);
+  TEST_COMPARE (strcmp (p, LETTERS_AND_DIGTS), 0);
+  free (p);
+
+  /* Check escaping of control characters and other non-printable
+     characters.  */
+  p = support_quote_blob_wide (L"\r\n\t\a\b\f\v\1\177\200\377"
+			       "\x123\x76543210\xfedcba98\0@", 17);
+  TEST_COMPARE (strcmp (p, "\\r\\n\\t\\a\\b\\f\\v\\x{1}"
+                        "\\x{7f}\\x{80}\\x{ff}\\x{123}\\x{76543210}"
+			"\\x{fedcba98}\\x{0}@\\x{0}"), 0);
+  free (p);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/support/tst-test_compare_string_wide.c b/support/tst-test_compare_string_wide.c
new file mode 100644
index 0000000000..548f7dcdc6
--- /dev/null
+++ b/support/tst-test_compare_string_wide.c
@@ -0,0 +1,107 @@ 
+/* Basic test for the TEST_COMPARE_STRING_WIDE macro.
+   Copyright (C) 2018-2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <string.h>
+#include <support/check.h>
+#include <support/capture_subprocess.h>
+
+static void
+subprocess (void *closure)
+{
+  /* These tests should fail.  They were chosen to cover differences
+     in length (with the same contents), single-bit mismatches, and
+     mismatching null pointers.  */
+  TEST_COMPARE_STRING_WIDE (L"", NULL);             /* Line 29.  */
+  TEST_COMPARE_STRING_WIDE (L"X", L"");              /* Line 30.  */
+  TEST_COMPARE_STRING_WIDE (NULL, L"X");            /* Line 31.  */
+  TEST_COMPARE_STRING_WIDE (L"abcd", L"abcD");       /* Line 32.  */
+  TEST_COMPARE_STRING_WIDE (L"abcd", NULL);         /* Line 33.  */
+  TEST_COMPARE_STRING_WIDE (NULL, L"abcd");         /* Line 34.  */
+}
+
+/* Same contents, different addresses.  */
+wchar_t buffer_abc_1[] = L"abc";
+wchar_t buffer_abc_2[] = L"abc";
+
+static int
+do_test (void)
+{
+  /* This should succeed.  Even if the pointers and array contents are
+     different, zero-length inputs are not different.  */
+  TEST_COMPARE_STRING_WIDE (NULL, NULL);
+  TEST_COMPARE_STRING_WIDE (L"", L"");
+  TEST_COMPARE_STRING_WIDE (buffer_abc_1, buffer_abc_2);
+  TEST_COMPARE_STRING_WIDE (buffer_abc_1, L"abc");
+
+  struct support_capture_subprocess proc = support_capture_subprocess
+    (&subprocess, NULL);
+
+  /* Discard the reported error.  */
+  support_record_failure_reset ();
+
+  puts ("info: *** subprocess output starts ***");
+  fputs (proc.out.buffer, stdout);
+  puts ("info: *** subprocess output ends ***");
+
+  TEST_VERIFY
+    (strcmp (proc.out.buffer,
+"tst-test_compare_string_wide.c:29: error: string comparison failed\n"
+"  left string: 0 wide characters\n"
+"  right string: NULL\n"
+"tst-test_compare_string_wide.c:30: error: string comparison failed\n"
+"  left string: 1 wide characters\n"
+"  right string: 0 wide characters\n"
+"  left (evaluated from L\"X\"):\n"
+"      L\"X\"\n"
+"      58\n"
+"tst-test_compare_string_wide.c:31: error: string comparison failed\n"
+"  left string: NULL\n"
+"  right string: 1 wide characters\n"
+"  right (evaluated from L\"X\"):\n"
+"      L\"X\"\n"
+"      58\n"
+"tst-test_compare_string_wide.c:32: error: string comparison failed\n"
+"  string length: 4 wide characters\n"
+"  left (evaluated from L\"abcd\"):\n"
+"      L\"abcd\"\n"
+"      61 62 63 64\n"
+"  right (evaluated from L\"abcD\"):\n"
+"      L\"abcD\"\n"
+"      61 62 63 44\n"
+"tst-test_compare_string_wide.c:33: error: string comparison failed\n"
+"  left string: 4 wide characters\n"
+"  right string: NULL\n"
+"  left (evaluated from L\"abcd\"):\n"
+"      L\"abcd\"\n"
+"      61 62 63 64\n"
+"tst-test_compare_string_wide.c:34: error: string comparison failed\n"
+"  left string: NULL\n"
+"  right string: 4 wide characters\n"
+"  right (evaluated from L\"abcd\"):\n"
+"      L\"abcd\"\n"
+"      61 62 63 64\n"
+             ) == 0);
+
+  /* Check that there is no output on standard error.  */
+  support_capture_subprocess_check (&proc, "TEST_COMPARE_STRING_WIDE",
+                                    0, sc_allow_stdout);
+
+  return 0;
+}
+
+#include <support/test-driver.c>