mbs* tests: Enhance tests

Bruno Haible via Gnulib discussion list <[email protected]>
Newsgroups gmane.comp.lib.gnulib.bugs
Organization GNU
Message-ID <3074441.8YlivOVZr6@nimes>
Remember the discussion about error handling while parsing/scanning
multibyte strings, that we had in July 2023? Paul coined the terms
"MEE" and "SEE".
<https://lists.gnu.org/archive/html/bug-gnulib/2023-07/msg00145.html>

Now I got interested in
  - whether the mb*iter* modules actually implement MEE,
  - what's the behavioural difference between MEE and SEE, function by
    function.

As a first step to understanding this, I'm enhancing the unit tests
to cover incomplete characters, both at the end of the string and
inside a string.


2026-05-25  Bruno Haible  <[email protected]>

	trim tests: Enhance tests.
	* tests/test-trim.c (main): Add test cases with incomplete characters.

2026-05-25  Bruno Haible  <[email protected]>

	mbmemcasecmp tests: Enhance tests.
	* tests/test-mbmemcasecmp.h (test_utf_8): Add test cases with incomplete
	characters.

2026-05-25  Bruno Haible  <[email protected]>

	mbspcasecmp tests: Enhance tests.
	* tests/test-mbspcasecmp.c (test_ascii): New function, extracted from
	main.
	(test_utf_8): Likewise. Add test cases with incomplete characters.
	(main): Invoke them. Accept a numeric argument.
	* tests/test-mbspcasecmp-4.sh: Renamed from tests/test-mbspcasecmp.sh.
	* tests/test-mbspcasecmp-3.sh: New file, based on
	tests/test-mbmemcasecmp-3.sh.
	* modules/mbspcasecmp-tests (Files): Update after rename. Add
	locale-en.m4, locale-fr.m4.
	(configure.ac): Invoke gt_LOCALE_EN_UTF8, gt_LOCALE_FR_UTF8.
	(Makefile.am): Arrange to run test-mbspcasecmp-3.sh,
	test-mbspcasecmp-4.sh, instead of test-mbspcasecmp.sh.

2026-05-25  Bruno Haible  <[email protected]>

	mbsncasecmp tests: Enhance tests.
	* tests/test-mbsncasecmp.c (test_ascii): New function, extracted from
	main.
	(test_utf_8): Likewise. Add test cases with incomplete characters.
	(main): Invoke them. Accept a numeric argument.
	* tests/test-mbsncasecmp-4.sh: Renamed from tests/test-mbsncasecmp.sh.
	* tests/test-mbsncasecmp-3.sh: New file, based on
	tests/test-mbmemcasecmp-3.sh.
	* modules/mbsncasecmp-tests (Files): Update after rename. Add
	locale-en.m4, locale-fr.m4.
	(configure.ac): Invoke gt_LOCALE_EN_UTF8, gt_LOCALE_FR_UTF8.
	(Makefile.am): Arrange to run test-mbsncasecmp-3.sh,
	test-mbsncasecmp-4.sh, instead of test-mbsncasecmp.sh.

2026-05-25  Bruno Haible  <[email protected]>

	mbscasecmp tests: Enhance tests.
	* tests/test-mbscasecmp.c (test_ascii): New function, extracted from
	main.
	(test_utf_8): Likewise. Add test cases with incomplete characters.
	(main): Invoke them. Accept a numeric argument.
	* tests/test-mbscasecmp-4.sh: Renamed from tests/test-mbscasecmp.sh.
	* tests/test-mbscasecmp-3.sh: New file, based on
	tests/test-mbmemcasecmp-3.sh.
	* modules/mbscasecmp-tests (Files): Update after rename. Add
	locale-en.m4, locale-fr.m4.
	(configure.ac): Invoke gt_LOCALE_EN_UTF8, gt_LOCALE_FR_UTF8.
	(Makefile.am): Arrange to run test-mbscasecmp-3.sh,
	test-mbscasecmp-4.sh, instead of test-mbscasecmp.sh.

2026-05-25  Bruno Haible  <[email protected]>

	mbs_endswith tests: Enhance tests.
	* tests/test-mbs_endswith2.c (main): Add more test cases. Add more
	comments.
	* tests/test-mbs_endswith1.c: Update comments.
	* tests/test-mbs_endswith3.c: Likewise.

2026-05-25  Bruno Haible  <[email protected]>

	mbs_startswith tests: Enhance tests.
	* tests/test-mbs_startswith2.c (OR): New macro, copied from
	tests/test-mbsnlen.c.
	(main): Add more test cases. Add more comments.
	* tests/test-mbs_startswith1.c: Update comments.
	* tests/test-mbs_startswith3.c: Likewise.
0001-mbs_startswith-tests-Enhance-tests.patch (text/x-patch, 7.6 KB)
From 900e90c433d17a467a26522b051e3f527102b289 Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Mon, 25 May 2026 17:55:56 +0200
Subject: [PATCH 1/7] mbs_startswith tests: Enhance tests.

* tests/test-mbs_startswith2.c (OR): New macro, copied from
tests/test-mbsnlen.c.
(main): Add more test cases. Add more comments.
* tests/test-mbs_startswith1.c: Update comments.
* tests/test-mbs_startswith3.c: Likewise.
---
 ChangeLog                    |  9 ++++
 tests/test-mbs_startswith1.c |  4 +-
 tests/test-mbs_startswith2.c | 79 +++++++++++++++++++++++++++++++++++-
 tests/test-mbs_startswith3.c |  2 +-
 4 files changed, 90 insertions(+), 4 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 556c9bf5b5..3c72ed6dc8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2026-05-25  Bruno Haible  <[email protected]>
+
+	mbs_startswith tests: Enhance tests.
+	* tests/test-mbs_startswith2.c (OR): New macro, copied from
+	tests/test-mbsnlen.c.
+	(main): Add more test cases. Add more comments.
+	* tests/test-mbs_startswith1.c: Update comments.
+	* tests/test-mbs_startswith3.c: Likewise.
+
 2026-05-24  Paul Eggert  <[email protected]>
 
 	regex: pacify 16.1.1 -Wanalyzer-out-of-bounds
diff --git a/tests/test-mbs_startswith1.c b/tests/test-mbs_startswith1.c
index a1b89fa4a5..4c2e8b409d 100644
--- a/tests/test-mbs_startswith1.c
+++ b/tests/test-mbs_startswith1.c
@@ -1,4 +1,4 @@
-/* Test of mbs_startswith() function.
+/* Test of mbs_startswith() function in the "C" locale.
    Copyright (C) 2025-2026 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
@@ -27,7 +27,7 @@
 int
 main ()
 {
-  /* This test is executed in the C locale.  */
+  /* This test is executed in the "C" locale.  */
 
   ASSERT (mbs_startswith ("", ""));
   ASSERT (mbs_startswith ("abc", ""));
diff --git a/tests/test-mbs_startswith2.c b/tests/test-mbs_startswith2.c
index 38c53dd12b..0ab6a9eeaf 100644
--- a/tests/test-mbs_startswith2.c
+++ b/tests/test-mbs_startswith2.c
@@ -1,4 +1,4 @@
-/* Test of mbs_startswith() function.
+/* Test of mbs_startswith() function in a UTF-8 locale.
    Copyright (C) 2025-2026 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
@@ -25,6 +25,20 @@
 
 #include "macros.h"
 
+/* The mcel-based implementation of mbsnlen behaves differently than the
+   original one.  Namely, for invalid/incomplete byte sequences:
+   Where we ideally should have multi-byte-per-encoding-error (MEE) behaviour
+   everywhere, mcel implements single-byte-per-encoding-error (SEE) behaviour.
+   See <https://lists.gnu.org/archive/html/bug-gnulib/2023-07/msg00131.html>,
+       <https://lists.gnu.org/archive/html/bug-gnulib/2023-07/msg00145.html>.
+   Therefore, here we have different expected results, depending on the
+   implementation.  */
+#if GNULIB_MCEL_PREFER
+# define OR(a,b) b
+#else
+# define OR(a,b) a
+#endif
+
 int
 main ()
 {
@@ -70,26 +84,89 @@ main ()
   /* Test cases with invalid or incomplete characters.  */
 
   /* A valid character should not match an invalid character.  */
+  /* "\301\247" = 0xC1 0xA7 is invalid.
+     In fact, "\301" = 0xC1 is already invalid, see
+     https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf page 125 table 3-7.
+   */
   ASSERT (!mbs_startswith ("\303\247", "\301\247"));
   ASSERT (!mbs_startswith ("\301\247", "\303\247"));
 
   /* A valid character should not match an incomplete character.  */
+  /* "\343\247" = 0xE3 0xA7 is incomplete, "\343\247\214" = U+39CC is valid.  */
   ASSERT (!mbs_startswith ("\303\247", "\343\247"));
   ASSERT (!mbs_startswith ("\343\247", "\303\247"));
+  ASSERT (!mbs_startswith ("\343\247\214", "\343\247"));
+  ASSERT (!mbs_startswith ("\343\247\214", "\343"));
 
   /* An invalid character should not match an incomplete character.  */
+  /* "\301\247" = 0xC1 0xA7 is invalid.
+     In fact, "\301" = 0xC1 is already invalid, see
+     https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf page 125 table 3-7.
+   */
+  /* "\343\247" = 0xE3 0xA7 is incomplete, "\343\247\214" = U+39CC is valid.  */
   ASSERT (!mbs_startswith ("\301\247", "\343\247"));
   ASSERT (!mbs_startswith ("\343\247", "\301\247"));
 
+  /* Incomplete characters.  See
+     https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf
+     page 128 table 3-11.  */
+  /* "\341\200\240" = 0xE1 0x80 0xA0 = U+1020.  */
+  ASSERT (!mbs_startswith ("\341\200\240", "\341\200"));
+  ASSERT (!mbs_startswith ("\341\200\240", "\341"));
+  ASSERT (mbs_startswith ("\341\200", "\341") == OR(false,true));
+  /* "\360\221\222\240" = 0xF0 0x91 0x92 0xA0 = U+114A0.  */
+  ASSERT (!mbs_startswith ("\360\221\222\240", "\360\221\222"));
+  ASSERT (!mbs_startswith ("\360\221\222\240", "\360\221"));
+  ASSERT (!mbs_startswith ("\360\221\222\240", "\360"));
+  ASSERT (mbs_startswith ("\360\221\222", "\360\221") == OR(false,true));
+  ASSERT (mbs_startswith ("\360\221\222", "\360") == OR(false,true));
+  ASSERT (mbs_startswith ("\360\221", "\360") == OR(false,true));
+
+  /* "\355\240\200" = 0xED 0xA0 0x80 = U+D800 is invalid.
+     In fact, "\355\240" = 0xED 0xA0 is already invalid, see
+     https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf page 125 table 3-7
+     and page 128 table 3-9.  */
+#if 0
+  /* mbs_startswith ("\355\240\200", "\355\240") returns
+     - true on musl libc, macOS, Solaris 11.4, Cygwin, mingw, MSVC
+       and with GNULIB_MCEL_PREFER on newer glibc, FreeBSD, NetBSD, OpenBSD,
+     - false on older glibc (CentOS 5), Solaris 11 OpenIndiana/OmniOS,
+       and with !GNULIB_MCEL_PREFER on newer glibc, FreeBSD, NetBSD, OpenBSD. */
+  ASSERT (!mbs_startswith ("\355\240\200", "\355\240"));
+#endif
+#if 0
+  /* mbs_startswith ("\355\240\200", "\355") returns
+     - true on newer glibc, musl libc, macOS, FreeBSD, NetBSD, OpenBSD,
+       Solaris 11.4, Cygwin, mingw, MSVC,
+     - false on older glibc (CentOS 5), Solaris 11 OpenIndiana/OmniOS.  */
+  ASSERT (!mbs_startswith ("\355\240\200", "\355"));
+#endif
+#if GNULIB_MCEL_PREFER
+  /* Single-byte encoding error (SEE) */
+  ASSERT (mbs_startswith ("\355\240", "\355"));
+#elif 0
+  /* Multi-byte encoding error (MEE) */
+  /* mbs_startswith ("\355\240", "\355") returns
+     - true on musl libc, macOS, Solaris 11.4, Cygwin, mingw, MSVC,
+     - false on glibc, FreeBSD, NetBSD, OpenBSD, Solaris 11 OpenIndiana/OmniOS.
+   */
+  ASSERT (!mbs_startswith ("\355\240", "\355"));
+#endif
+
   /* Two invalid characters should match only if they are identical.  */
+  /* "\301\246" = 0xC1 0xA6 is invalid.  */
+  /* "\301\247" = 0xC1 0xA7 is invalid.  */
   ASSERT (!mbs_startswith ("\301\246", "\301\247"));
   ASSERT (!mbs_startswith ("\301\247", "\301\246"));
   ASSERT (mbs_startswith ("\301\247", "\301\247"));
 
   /* Two incomplete characters should match only if they are identical.  */
+  /* "\343\246" = 0xE3 0xA6 is incomplete, "\343\246\214" = U+398C is valid.  */
+  /* "\343\247" = 0xE3 0xA7 is incomplete, "\343\247\214" = U+39CC is valid.  */
   ASSERT (!mbs_startswith ("\343\246", "\343\247"));
   ASSERT (!mbs_startswith ("\343\247", "\343\246"));
   ASSERT (mbs_startswith ("\343\247", "\343\247"));
+  ASSERT (mbs_startswith ("\343\247", "\343") == OR(false,true));
 
   return test_exit_status;
 }
diff --git a/tests/test-mbs_startswith3.c b/tests/test-mbs_startswith3.c
index 1965070401..11d87562c7 100644
--- a/tests/test-mbs_startswith3.c
+++ b/tests/test-mbs_startswith3.c
@@ -1,4 +1,4 @@
-/* Test of mbs_startswith() function.
+/* Test of mbs_startswith() function in a GB18030 locale.
    Copyright (C) 2025-2026 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
-- 
2.54.0
0002-mbs_endswith-tests-Enhance-tests.patch (text/x-patch, 4.7 KB)
From 26621d07249663c0cfba331ee5295efd59bef0f7 Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Mon, 25 May 2026 17:56:44 +0200
Subject: [PATCH 2/7] mbs_endswith tests: Enhance tests.

* tests/test-mbs_endswith2.c (main): Add more test cases. Add more
comments.
* tests/test-mbs_endswith1.c: Update comments.
* tests/test-mbs_endswith3.c: Likewise.
---
 ChangeLog                  |  8 ++++++++
 tests/test-mbs_endswith1.c |  2 +-
 tests/test-mbs_endswith2.c | 30 +++++++++++++++++++++++++++++-
 tests/test-mbs_endswith3.c |  2 +-
 4 files changed, 39 insertions(+), 3 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 3c72ed6dc8..02ad380e55 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2026-05-25  Bruno Haible  <[email protected]>
+
+	mbs_endswith tests: Enhance tests.
+	* tests/test-mbs_endswith2.c (main): Add more test cases. Add more
+	comments.
+	* tests/test-mbs_endswith1.c: Update comments.
+	* tests/test-mbs_endswith3.c: Likewise.
+
 2026-05-25  Bruno Haible  <[email protected]>
 
 	mbs_startswith tests: Enhance tests.
diff --git a/tests/test-mbs_endswith1.c b/tests/test-mbs_endswith1.c
index 63722b0137..7742efbc42 100644
--- a/tests/test-mbs_endswith1.c
+++ b/tests/test-mbs_endswith1.c
@@ -1,4 +1,4 @@
-/* Test of mbs_endswith() function.
+/* Test of mbs_endswith() function in the "C" locale.
    Copyright (C) 2025-2026 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
diff --git a/tests/test-mbs_endswith2.c b/tests/test-mbs_endswith2.c
index 01c12f47ab..17ccc1f6e0 100644
--- a/tests/test-mbs_endswith2.c
+++ b/tests/test-mbs_endswith2.c
@@ -1,4 +1,4 @@
-/* Test of mbs_endswith() function.
+/* Test of mbs_endswith() function in a UTF-8 locale.
    Copyright (C) 2025-2026 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
@@ -65,23 +65,51 @@ main ()
   /* Test cases with invalid or incomplete characters.  */
 
   /* A valid character should not match an invalid character.  */
+  /* "\301\247" = 0xC1 0xA7 is invalid.
+     In fact, "\301" = 0xC1 is already invalid, see
+     https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf page 125 table 3-7.
+   */
   ASSERT (!mbs_endswith ("\303\247", "\301\247"));
   ASSERT (!mbs_endswith ("\301\247", "\303\247"));
 
   /* A valid character should not match an incomplete character.  */
+  /* "\343\247" = 0xE3 0xA7 is incomplete, "\343\247\214" = U+39CC is valid.  */
   ASSERT (!mbs_endswith ("\303\247", "\343\247"));
   ASSERT (!mbs_endswith ("\343\247", "\303\247"));
 
   /* An invalid character should not match an incomplete character.  */
+  /* "\301\247" = 0xC1 0xA7 is invalid.
+     In fact, "\301" = 0xC1 is already invalid, see
+     https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf page 125 table 3-7.
+   */
+  /* "\343\247" = 0xE3 0xA7 is incomplete, "\343\247\214" = U+39CC is valid.  */
   ASSERT (!mbs_endswith ("\301\247", "\343\247"));
   ASSERT (!mbs_endswith ("\343\247", "\301\247"));
 
+  /* Incomplete characters.  See
+     https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf
+     page 128 table 3-11.  */
+  /* "\341\200\240" = 0xE1 0x80 0xA0 = U+1020.  */
+  ASSERT (!mbs_endswith ("\341\200\240", "\200\240"));
+  ASSERT (!mbs_endswith ("\341\200\240", "\240"));
+  /* "\360\221\222\240" = 0xF0 0x91 0x92 0xA0 = U+114A0.  */
+  ASSERT (!mbs_endswith ("\360\221\222\240", "\221\222\240"));
+  ASSERT (!mbs_endswith ("\360\221\222\240", "\222\240"));
+  ASSERT (!mbs_endswith ("\360\221\222\240", "\240"));
+
   /* Two invalid characters should match only if they are identical.  */
+  /* "\301\246" = 0xC1 0xA6 is invalid.
+     "\301\247" = 0xC1 0xA7 is invalid.
+     In fact, "\301" = 0xC1 is already invalid, see
+     https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf page 125 table 3-7.
+   */
   ASSERT (!mbs_endswith ("\301\246", "\301\247"));
   ASSERT (!mbs_endswith ("\301\247", "\301\246"));
   ASSERT (mbs_endswith ("\301\247", "\301\247"));
 
   /* Two incomplete characters should match only if they are identical.  */
+  /* "\343\246" = 0xE3 0xA6 is incomplete, "\343\246\214" = U+398C is valid.  */
+  /* "\343\247" = 0xE3 0xA7 is incomplete, "\343\247\214" = U+39CC is valid.  */
   ASSERT (!mbs_endswith ("\343\246", "\343\247"));
   ASSERT (!mbs_endswith ("\343\247", "\343\246"));
   ASSERT (mbs_endswith ("\343\247", "\343\247"));
diff --git a/tests/test-mbs_endswith3.c b/tests/test-mbs_endswith3.c
index ad1e24f5e8..e1abd1195e 100644
--- a/tests/test-mbs_endswith3.c
+++ b/tests/test-mbs_endswith3.c
@@ -1,4 +1,4 @@
-/* Test of mbs_endswith() function.
+/* Test of mbs_endswith() function in a GB18030 locale.
    Copyright (C) 2025-2026 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
-- 
2.54.0
0005-mbspcasecmp-tests-Enhance-tests.patch (text/x-patch, 8.4 KB)
From 2e73a29a97c7fc0e7b3d5737cd84172cb82b4069 Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Mon, 25 May 2026 18:32:18 +0200
Subject: [PATCH 5/7] mbspcasecmp tests: Enhance tests.

* tests/test-mbspcasecmp.c (test_ascii): New function, extracted from
main.
(test_utf_8): Likewise. Add test cases with incomplete characters.
(main): Invoke them. Accept a numeric argument.
* tests/test-mbspcasecmp-4.sh: Renamed from tests/test-mbspcasecmp.sh.
* tests/test-mbspcasecmp-3.sh: New file, based on
tests/test-mbmemcasecmp-3.sh.
* modules/mbspcasecmp-tests (Files): Update after rename. Add
locale-en.m4, locale-fr.m4.
(configure.ac): Invoke gt_LOCALE_EN_UTF8, gt_LOCALE_FR_UTF8.
(Makefile.am): Arrange to run test-mbspcasecmp-3.sh,
test-mbspcasecmp-4.sh, instead of test-mbspcasecmp.sh.
---
 ChangeLog                                     |  16 +++
 modules/mbspcasecmp-tests                     |  14 ++-
 tests/test-mbspcasecmp-3.sh                   |  23 ++++
 ...t-mbspcasecmp.sh => test-mbspcasecmp-4.sh} |   2 +-
 tests/test-mbspcasecmp.c                      | 114 +++++++++++++++---
 5 files changed, 145 insertions(+), 24 deletions(-)
 create mode 100755 tests/test-mbspcasecmp-3.sh
 rename tests/{test-mbspcasecmp.sh => test-mbspcasecmp-4.sh} (89%)

diff --git a/ChangeLog b/ChangeLog
index 14ce0d68fd..c5b5e39291 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2026-05-25  Bruno Haible  <[email protected]>
+
+	mbspcasecmp tests: Enhance tests.
+	* tests/test-mbspcasecmp.c (test_ascii): New function, extracted from
+	main.
+	(test_utf_8): Likewise. Add test cases with incomplete characters.
+	(main): Invoke them. Accept a numeric argument.
+	* tests/test-mbspcasecmp-4.sh: Renamed from tests/test-mbspcasecmp.sh.
+	* tests/test-mbspcasecmp-3.sh: New file, based on
+	tests/test-mbmemcasecmp-3.sh.
+	* modules/mbspcasecmp-tests (Files): Update after rename. Add
+	locale-en.m4, locale-fr.m4.
+	(configure.ac): Invoke gt_LOCALE_EN_UTF8, gt_LOCALE_FR_UTF8.
+	(Makefile.am): Arrange to run test-mbspcasecmp-3.sh,
+	test-mbspcasecmp-4.sh, instead of test-mbspcasecmp.sh.
+
 2026-05-25  Bruno Haible  <[email protected]>
 
 	mbsncasecmp tests: Enhance tests.
diff --git a/modules/mbspcasecmp-tests b/modules/mbspcasecmp-tests
index e82a37eb23..4ca8c4d95e 100644
--- a/modules/mbspcasecmp-tests
+++ b/modules/mbspcasecmp-tests
@@ -1,7 +1,10 @@
 Files:
-tests/test-mbspcasecmp.sh
+tests/test-mbspcasecmp-3.sh
+tests/test-mbspcasecmp-4.sh
 tests/test-mbspcasecmp.c
 tests/macros.h
+m4/locale-en.m4
+m4/locale-fr.m4
 m4/locale-tr.m4
 m4/codeset.m4
 
@@ -9,10 +12,15 @@ Depends-on:
 setlocale
 
 configure.ac:
+gt_LOCALE_EN_UTF8
+gt_LOCALE_FR_UTF8
 gt_LOCALE_TR_UTF8
 
 Makefile.am:
-TESTS += test-mbspcasecmp.sh
-TESTS_ENVIRONMENT += LOCALE_TR_UTF8='@LOCALE_TR_UTF8@'
+TESTS += test-mbspcasecmp-3.sh test-mbspcasecmp-4.sh
+TESTS_ENVIRONMENT += \
+  LOCALE_EN_UTF8='@LOCALE_EN_UTF8@' \
+  LOCALE_FR_UTF8='@LOCALE_FR_UTF8@' \
+  LOCALE_TR_UTF8='@LOCALE_TR_UTF8@'
 check_PROGRAMS += test-mbspcasecmp
 test_mbspcasecmp_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) $(MBRTOWC_LIB) $(LIBC32CONV)
diff --git a/tests/test-mbspcasecmp-3.sh b/tests/test-mbspcasecmp-3.sh
new file mode 100755
index 0000000000..dc4619a0c3
--- /dev/null
+++ b/tests/test-mbspcasecmp-3.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+# Test whether a specific UTF-8 locale is installed.
+: "${LOCALE_EN_UTF8=en_US.UTF-8}"
+: "${LOCALE_FR_UTF8=fr_FR.UTF-8}"
+if test "$LOCALE_EN_UTF8" = none && test $LOCALE_FR_UTF8 = none; then
+  if test -f /usr/bin/localedef; then
+    echo "Skipping test: no english or french Unicode locale is installed"
+  else
+    echo "Skipping test: no english or french Unicode locale is supported"
+  fi
+  exit 77
+fi
+
+# It's sufficient to test in one of the two locales.
+if test $LOCALE_FR_UTF8 != none; then
+  testlocale=$LOCALE_FR_UTF8
+else
+  testlocale="$LOCALE_EN_UTF8"
+fi
+
+LC_ALL="$testlocale" \
+${CHECKER} ./test-mbspcasecmp${EXEEXT} 3
diff --git a/tests/test-mbspcasecmp.sh b/tests/test-mbspcasecmp-4.sh
similarity index 89%
rename from tests/test-mbspcasecmp.sh
rename to tests/test-mbspcasecmp-4.sh
index 1e390755f1..daef45b62c 100755
--- a/tests/test-mbspcasecmp.sh
+++ b/tests/test-mbspcasecmp-4.sh
@@ -12,4 +12,4 @@ if test $LOCALE_TR_UTF8 = none; then
 fi
 
 LC_ALL=$LOCALE_TR_UTF8 \
-${CHECKER} ./test-mbspcasecmp${EXEEXT}
+${CHECKER} ./test-mbspcasecmp${EXEEXT} 4
diff --git a/tests/test-mbspcasecmp.c b/tests/test-mbspcasecmp.c
index 8839a7444d..a1407164cd 100644
--- a/tests/test-mbspcasecmp.c
+++ b/tests/test-mbspcasecmp.c
@@ -24,13 +24,9 @@
 
 #include "macros.h"
 
-int
-main ()
+static void
+test_ascii (void)
 {
-  /* configure should already have checked that the locale is supported.  */
-  if (setlocale (LC_ALL, "") == NULL)
-    return 1;
-
   {
     const char string[] = "paragraph";
     ASSERT (mbspcasecmp (string, "Paragraph") == string + 9);
@@ -60,31 +56,109 @@ main ()
     const char string[] = "paragraph";
     ASSERT (mbspcasecmp (string, "para") == string + 4);
   }
+}
 
+static void
+test_utf_8 (bool turkish)
+{
   /* The following tests shows how mbspcasecmp() is different from
      strncasecmp().  */
 
+  if (turkish)
+    {
+      {
+        const char string[] = "\303\266zg\303\274rt\303\274k"; /* özgürtük */
+        ASSERT (mbspcasecmp (string, "\303\226ZG\303\234R") == string + 7); /* özgür */
+      }
+
+      {
+        const char string[] = "\303\226ZG\303\234Rt\303\274k"; /* özgürtük */
+        ASSERT (mbspcasecmp (string, "\303\266zg\303\274r") == string + 7); /* özgür */
+      }
+
+      /* This test shows how strings of different size can compare equal.  */
+
+      {
+        const char string[] = "turkishtime";
+        ASSERT (mbspcasecmp (string, "TURK\304\260SH") == string + 7);
+      }
+
+      {
+        const char string[] = "TURK\304\260SHK\303\234LT\303\234R";
+        ASSERT (mbspcasecmp (string, "turkish") == string + 8);
+      }
+    }
+
+  /* Incomplete characters.  See
+     https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf
+     page 128 table 3-11.  */
+
+  /* "\341\200\240" = 0xE1 0x80 0xA0 = U+1020.  */
   {
-    const char string[] = "\303\266zg\303\274rt\303\274k"; /* özgürtük */
-    ASSERT (mbspcasecmp (string, "\303\226ZG\303\234R") == string + 7); /* özgür */
+    const char string[] = "\341\200";
+    ASSERT (mbspcasecmp (string, "\341\200") == string + 2);
   }
-
   {
-    const char string[] = "\303\226ZG\303\234Rt\303\274k"; /* özgürtük */
-    ASSERT (mbspcasecmp (string, "\303\266zg\303\274r") == string + 7); /* özgür */
+    const char string[] = "\341\200X";
+    ASSERT (mbspcasecmp (string, "\341\200x") == string + 3);
   }
-
-  /* This test shows how strings of different size can compare equal.  */
-
   {
-    const char string[] = "turkishtime";
-    ASSERT (mbspcasecmp (string, "TURK\304\260SH") == string + 7);
+    const char string[] = "\341";
+    ASSERT (mbspcasecmp (string, "\341") == string + 1);
   }
-
   {
-    const char string[] = "TURK\304\260SHK\303\234LT\303\234R";
-    ASSERT (mbspcasecmp (string, "turkish") == string + 8);
+    const char string[] = "\341X";
+    ASSERT (mbspcasecmp (string, "\341x") == string + 2);
   }
+  /* "\360\221\222\240" = 0xF0 0x91 0x92 0xA0 = U+114A0.  */
+  {
+    const char string[] = "\360\221\222";
+    ASSERT (mbspcasecmp (string, "\360\221\222") == string + 3);
+  }
+  {
+    const char string[] = "\360\221\222X";
+    ASSERT (mbspcasecmp (string, "\360\221\222x") == string + 4);
+  }
+  {
+    const char string[] = "\360\221";
+    ASSERT (mbspcasecmp (string, "\360\221") == string + 2);
+  }
+  {
+    const char string[] = "\360\221X";
+    ASSERT (mbspcasecmp (string, "\360\221x") == string + 3);
+  }
+  {
+    const char string[] = "\360";
+    ASSERT (mbspcasecmp (string, "\360") == string + 1);
+  }
+  {
+    const char string[] = "\360X";
+    ASSERT (mbspcasecmp (string, "\360x") == string + 2);
+  }
+}
+
+int
+main (int argc, char *argv[])
+{
+  /* configure should already have checked that the locale is supported.  */
+  if (setlocale (LC_ALL, "") == NULL)
+    return 1;
+
+  test_ascii ();
+
+  if (argc > 1)
+    switch (argv[1][0])
+      {
+      case '3':
+        /* Locale encoding is UTF-8, locale is not Turkish.  */
+        test_utf_8 (false);
+        return test_exit_status;
+
+      case '4':
+        /* Locale encoding is UTF-8, locale is Turkish.  */
+        test_utf_8 (true);
+        return test_exit_status;
+      }
 
-  return test_exit_status;
+  return 1;
 }
-- 
2.54.0
0004-mbsncasecmp-tests-Enhance-tests.patch (text/x-patch, 7.3 KB)
From 1d411d24777b1defd3a065300da16b31586ef85f Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Mon, 25 May 2026 18:27:18 +0200
Subject: [PATCH 4/7] mbsncasecmp tests: Enhance tests.

* tests/test-mbsncasecmp.c (test_ascii): New function, extracted from
main.
(test_utf_8): Likewise. Add test cases with incomplete characters.
(main): Invoke them. Accept a numeric argument.
* tests/test-mbsncasecmp-4.sh: Renamed from tests/test-mbsncasecmp.sh.
* tests/test-mbsncasecmp-3.sh: New file, based on
tests/test-mbmemcasecmp-3.sh.
* modules/mbsncasecmp-tests (Files): Update after rename. Add
locale-en.m4, locale-fr.m4.
(configure.ac): Invoke gt_LOCALE_EN_UTF8, gt_LOCALE_FR_UTF8.
(Makefile.am): Arrange to run test-mbsncasecmp-3.sh,
test-mbsncasecmp-4.sh, instead of test-mbsncasecmp.sh.
---
 ChangeLog                                     | 16 +++++
 modules/mbsncasecmp-tests                     | 14 +++-
 tests/test-mbsncasecmp-3.sh                   | 23 +++++++
 ...t-mbsncasecmp.sh => test-mbsncasecmp-4.sh} |  2 +-
 tests/test-mbsncasecmp.c                      | 68 +++++++++++++++----
 5 files changed, 107 insertions(+), 16 deletions(-)
 create mode 100755 tests/test-mbsncasecmp-3.sh
 rename tests/{test-mbsncasecmp.sh => test-mbsncasecmp-4.sh} (89%)

diff --git a/ChangeLog b/ChangeLog
index 5940e0d95f..14ce0d68fd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2026-05-25  Bruno Haible  <[email protected]>
+
+	mbsncasecmp tests: Enhance tests.
+	* tests/test-mbsncasecmp.c (test_ascii): New function, extracted from
+	main.
+	(test_utf_8): Likewise. Add test cases with incomplete characters.
+	(main): Invoke them. Accept a numeric argument.
+	* tests/test-mbsncasecmp-4.sh: Renamed from tests/test-mbsncasecmp.sh.
+	* tests/test-mbsncasecmp-3.sh: New file, based on
+	tests/test-mbmemcasecmp-3.sh.
+	* modules/mbsncasecmp-tests (Files): Update after rename. Add
+	locale-en.m4, locale-fr.m4.
+	(configure.ac): Invoke gt_LOCALE_EN_UTF8, gt_LOCALE_FR_UTF8.
+	(Makefile.am): Arrange to run test-mbsncasecmp-3.sh,
+	test-mbsncasecmp-4.sh, instead of test-mbsncasecmp.sh.
+
 2026-05-25  Bruno Haible  <[email protected]>
 
 	mbscasecmp tests: Enhance tests.
diff --git a/modules/mbsncasecmp-tests b/modules/mbsncasecmp-tests
index 5ed84188ea..c41804a2d5 100644
--- a/modules/mbsncasecmp-tests
+++ b/modules/mbsncasecmp-tests
@@ -1,7 +1,10 @@
 Files:
-tests/test-mbsncasecmp.sh
+tests/test-mbsncasecmp-3.sh
+tests/test-mbsncasecmp-4.sh
 tests/test-mbsncasecmp.c
 tests/macros.h
+m4/locale-en.m4
+m4/locale-fr.m4
 m4/locale-tr.m4
 m4/codeset.m4
 
@@ -9,10 +12,15 @@ Depends-on:
 setlocale
 
 configure.ac:
+gt_LOCALE_EN_UTF8
+gt_LOCALE_FR_UTF8
 gt_LOCALE_TR_UTF8
 
 Makefile.am:
-TESTS += test-mbsncasecmp.sh
-TESTS_ENVIRONMENT += LOCALE_TR_UTF8='@LOCALE_TR_UTF8@'
+TESTS += test-mbsncasecmp-3.sh test-mbsncasecmp-4.sh
+TESTS_ENVIRONMENT += \
+  LOCALE_EN_UTF8='@LOCALE_EN_UTF8@' \
+  LOCALE_FR_UTF8='@LOCALE_FR_UTF8@' \
+  LOCALE_TR_UTF8='@LOCALE_TR_UTF8@'
 check_PROGRAMS += test-mbsncasecmp
 test_mbsncasecmp_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) $(MBRTOWC_LIB) $(LIBC32CONV)
diff --git a/tests/test-mbsncasecmp-3.sh b/tests/test-mbsncasecmp-3.sh
new file mode 100755
index 0000000000..f5bee7f298
--- /dev/null
+++ b/tests/test-mbsncasecmp-3.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+# Test whether a specific UTF-8 locale is installed.
+: "${LOCALE_EN_UTF8=en_US.UTF-8}"
+: "${LOCALE_FR_UTF8=fr_FR.UTF-8}"
+if test "$LOCALE_EN_UTF8" = none && test $LOCALE_FR_UTF8 = none; then
+  if test -f /usr/bin/localedef; then
+    echo "Skipping test: no english or french Unicode locale is installed"
+  else
+    echo "Skipping test: no english or french Unicode locale is supported"
+  fi
+  exit 77
+fi
+
+# It's sufficient to test in one of the two locales.
+if test $LOCALE_FR_UTF8 != none; then
+  testlocale=$LOCALE_FR_UTF8
+else
+  testlocale="$LOCALE_EN_UTF8"
+fi
+
+LC_ALL="$testlocale" \
+${CHECKER} ./test-mbsncasecmp${EXEEXT} 3
diff --git a/tests/test-mbsncasecmp.sh b/tests/test-mbsncasecmp-4.sh
similarity index 89%
rename from tests/test-mbsncasecmp.sh
rename to tests/test-mbsncasecmp-4.sh
index baf1e542bd..c7cf85c969 100755
--- a/tests/test-mbsncasecmp.sh
+++ b/tests/test-mbsncasecmp-4.sh
@@ -12,4 +12,4 @@ if test $LOCALE_TR_UTF8 = none; then
 fi
 
 LC_ALL=$LOCALE_TR_UTF8 \
-${CHECKER} ./test-mbsncasecmp${EXEEXT}
+${CHECKER} ./test-mbsncasecmp${EXEEXT} 4
diff --git a/tests/test-mbsncasecmp.c b/tests/test-mbsncasecmp.c
index 1858483f81..fb98f01354 100644
--- a/tests/test-mbsncasecmp.c
+++ b/tests/test-mbsncasecmp.c
@@ -24,13 +24,9 @@
 
 #include "macros.h"
 
-int
-main ()
+static void
+test_ascii (void)
 {
-  /* configure should already have checked that the locale is supported.  */
-  if (setlocale (LC_ALL, "") == NULL)
-    return 1;
-
   ASSERT (mbsncasecmp ("paragraph", "Paragraph", 1000000) == 0);
   ASSERT (mbsncasecmp ("paragraph", "Paragraph", 9) == 0);
 
@@ -54,16 +50,64 @@ main ()
   ASSERT (mbsncasecmp ("paragraph", "para", 9) > 0);
   ASSERT (mbsncasecmp ("paragraph", "para", 5) > 0);
   ASSERT (mbsncasecmp ("paragraph", "para", 4) == 0);
+}
 
+static void
+test_utf_8 (bool turkish)
+{
   /* The following tests shows how mbsncasecmp() is different from
      strncasecmp().  */
 
-  ASSERT (mbsncasecmp ("\303\266zg\303\274r", "\303\226ZG\303\234R", 99) == 0); /* özgür */
-  ASSERT (mbsncasecmp ("\303\226ZG\303\234R", "\303\266zg\303\274r", 99) == 0); /* özgür */
+  if (turkish)
+    {
+      ASSERT (mbsncasecmp ("\303\266zg\303\274r", "\303\226ZG\303\234R", 99) == 0); /* özgür */
+      ASSERT (mbsncasecmp ("\303\226ZG\303\234R", "\303\266zg\303\274r", 99) == 0); /* özgür */
+
+      /* This test shows how strings of different size can compare equal.  */
+      ASSERT (mbsncasecmp ("turkish", "TURK\304\260SH", 7) == 0);
+      ASSERT (mbsncasecmp ("TURK\304\260SH", "turkish", 7) == 0);
+    }
+
+  /* Incomplete characters.  See
+     https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf
+     page 128 table 3-11.  */
+
+  /* "\341\200\240" = 0xE1 0x80 0xA0 = U+1020.  */
+  ASSERT (mbsncasecmp ("\341\200", "\341\200", 99) == 0);
+  ASSERT (mbsncasecmp ("\341\200X", "\341\200x", 99) == 0);
+  ASSERT (mbsncasecmp ("\341", "\341", 99) == 0);
+  ASSERT (mbsncasecmp ("\341X", "\341x", 99) == 0);
+  /* "\360\221\222\240" = 0xF0 0x91 0x92 0xA0 = U+114A0.  */
+  ASSERT (mbsncasecmp ("\360\221\222", "\360\221\222", 99) == 0);
+  ASSERT (mbsncasecmp ("\360\221\222X", "\360\221\222x", 99) == 0);
+  ASSERT (mbsncasecmp ("\360\221", "\360\221", 99) == 0);
+  ASSERT (mbsncasecmp ("\360\221X", "\360\221x", 99) == 0);
+  ASSERT (mbsncasecmp ("\360", "\360", 99) == 0);
+  ASSERT (mbsncasecmp ("\360X", "\360x", 99) == 0);
+}
+
+int
+main (int argc, char *argv[])
+{
+  /* configure should already have checked that the locale is supported.  */
+  if (setlocale (LC_ALL, "") == NULL)
+    return 1;
+
+  test_ascii ();
+
+  if (argc > 1)
+    switch (argv[1][0])
+      {
+      case '3':
+        /* Locale encoding is UTF-8, locale is not Turkish.  */
+        test_utf_8 (false);
+        return test_exit_status;
 
-  /* This test shows how strings of different size can compare equal.  */
-  ASSERT (mbsncasecmp ("turkish", "TURK\304\260SH", 7) == 0);
-  ASSERT (mbsncasecmp ("TURK\304\260SH", "turkish", 7) == 0);
+      case '4':
+        /* Locale encoding is UTF-8, locale is Turkish.  */
+        test_utf_8 (true);
+        return test_exit_status;
+      }
 
-  return test_exit_status;
+  return 1;
 }
-- 
2.54.0
0003-mbscasecmp-tests-Enhance-tests.patch (text/x-patch, 7.1 KB)
From 25b66bfe9de7c305f641bd815f13be03159bfaec Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Mon, 25 May 2026 18:20:40 +0200
Subject: [PATCH 3/7] mbscasecmp tests: Enhance tests.

* tests/test-mbscasecmp.c (test_ascii): New function, extracted from
main.
(test_utf_8): Likewise. Add test cases with incomplete characters.
(main): Invoke them. Accept a numeric argument.
* tests/test-mbscasecmp-4.sh: Renamed from tests/test-mbscasecmp.sh.
* tests/test-mbscasecmp-3.sh: New file, based on
tests/test-mbmemcasecmp-3.sh.
* modules/mbscasecmp-tests (Files): Update after rename. Add
locale-en.m4, locale-fr.m4.
(configure.ac): Invoke gt_LOCALE_EN_UTF8, gt_LOCALE_FR_UTF8.
(Makefile.am): Arrange to run test-mbscasecmp-3.sh,
test-mbscasecmp-4.sh, instead of test-mbscasecmp.sh.
---
 ChangeLog                                     | 16 +++++
 modules/mbscasecmp-tests                      | 14 +++-
 tests/test-mbscasecmp-3.sh                    | 23 +++++++
 ...est-mbscasecmp.sh => test-mbscasecmp-4.sh} |  2 +-
 tests/test-mbscasecmp.c                       | 68 +++++++++++++++----
 5 files changed, 107 insertions(+), 16 deletions(-)
 create mode 100755 tests/test-mbscasecmp-3.sh
 rename tests/{test-mbscasecmp.sh => test-mbscasecmp-4.sh} (89%)

diff --git a/ChangeLog b/ChangeLog
index 02ad380e55..5940e0d95f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2026-05-25  Bruno Haible  <[email protected]>
+
+	mbscasecmp tests: Enhance tests.
+	* tests/test-mbscasecmp.c (test_ascii): New function, extracted from
+	main.
+	(test_utf_8): Likewise. Add test cases with incomplete characters.
+	(main): Invoke them. Accept a numeric argument.
+	* tests/test-mbscasecmp-4.sh: Renamed from tests/test-mbscasecmp.sh.
+	* tests/test-mbscasecmp-3.sh: New file, based on
+	tests/test-mbmemcasecmp-3.sh.
+	* modules/mbscasecmp-tests (Files): Update after rename. Add
+	locale-en.m4, locale-fr.m4.
+	(configure.ac): Invoke gt_LOCALE_EN_UTF8, gt_LOCALE_FR_UTF8.
+	(Makefile.am): Arrange to run test-mbscasecmp-3.sh,
+	test-mbscasecmp-4.sh, instead of test-mbscasecmp.sh.
+
 2026-05-25  Bruno Haible  <[email protected]>
 
 	mbs_endswith tests: Enhance tests.
diff --git a/modules/mbscasecmp-tests b/modules/mbscasecmp-tests
index 61282af862..bdbb0cf17b 100644
--- a/modules/mbscasecmp-tests
+++ b/modules/mbscasecmp-tests
@@ -1,7 +1,10 @@
 Files:
-tests/test-mbscasecmp.sh
+tests/test-mbscasecmp-3.sh
+tests/test-mbscasecmp-4.sh
 tests/test-mbscasecmp.c
 tests/macros.h
+m4/locale-en.m4
+m4/locale-fr.m4
 m4/locale-tr.m4
 m4/codeset.m4
 
@@ -9,10 +12,15 @@ Depends-on:
 setlocale
 
 configure.ac:
+gt_LOCALE_EN_UTF8
+gt_LOCALE_FR_UTF8
 gt_LOCALE_TR_UTF8
 
 Makefile.am:
-TESTS += test-mbscasecmp.sh
-TESTS_ENVIRONMENT += LOCALE_TR_UTF8='@LOCALE_TR_UTF8@'
+TESTS += test-mbscasecmp-3.sh test-mbscasecmp-4.sh
+TESTS_ENVIRONMENT += \
+  LOCALE_EN_UTF8='@LOCALE_EN_UTF8@' \
+  LOCALE_FR_UTF8='@LOCALE_FR_UTF8@' \
+  LOCALE_TR_UTF8='@LOCALE_TR_UTF8@'
 check_PROGRAMS += test-mbscasecmp
 test_mbscasecmp_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) $(MBRTOWC_LIB) $(LIBC32CONV)
diff --git a/tests/test-mbscasecmp-3.sh b/tests/test-mbscasecmp-3.sh
new file mode 100755
index 0000000000..72ee7d4738
--- /dev/null
+++ b/tests/test-mbscasecmp-3.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+# Test whether a specific UTF-8 locale is installed.
+: "${LOCALE_EN_UTF8=en_US.UTF-8}"
+: "${LOCALE_FR_UTF8=fr_FR.UTF-8}"
+if test "$LOCALE_EN_UTF8" = none && test $LOCALE_FR_UTF8 = none; then
+  if test -f /usr/bin/localedef; then
+    echo "Skipping test: no english or french Unicode locale is installed"
+  else
+    echo "Skipping test: no english or french Unicode locale is supported"
+  fi
+  exit 77
+fi
+
+# It's sufficient to test in one of the two locales.
+if test $LOCALE_FR_UTF8 != none; then
+  testlocale=$LOCALE_FR_UTF8
+else
+  testlocale="$LOCALE_EN_UTF8"
+fi
+
+LC_ALL="$testlocale" \
+${CHECKER} ./test-mbscasecmp${EXEEXT} 3
diff --git a/tests/test-mbscasecmp.sh b/tests/test-mbscasecmp-4.sh
similarity index 89%
rename from tests/test-mbscasecmp.sh
rename to tests/test-mbscasecmp-4.sh
index 73e62b5f50..e5c5a90b17 100755
--- a/tests/test-mbscasecmp.sh
+++ b/tests/test-mbscasecmp-4.sh
@@ -12,4 +12,4 @@ if test $LOCALE_TR_UTF8 = none; then
 fi
 
 LC_ALL=$LOCALE_TR_UTF8 \
-${CHECKER} ./test-mbscasecmp${EXEEXT}
+${CHECKER} ./test-mbscasecmp${EXEEXT} 4
diff --git a/tests/test-mbscasecmp.c b/tests/test-mbscasecmp.c
index 1c12691dea..f309d3e517 100644
--- a/tests/test-mbscasecmp.c
+++ b/tests/test-mbscasecmp.c
@@ -24,13 +24,9 @@
 
 #include "macros.h"
 
-int
-main ()
+static void
+test_ascii (void)
 {
-  /* configure should already have checked that the locale is supported.  */
-  if (setlocale (LC_ALL, "") == NULL)
-    return 1;
-
   ASSERT (mbscasecmp ("paragraph", "Paragraph") == 0);
 
   ASSERT (mbscasecmp ("paragrapH", "parAgRaph") == 0);
@@ -40,16 +36,64 @@ main ()
 
   ASSERT (mbscasecmp ("para", "paragraph") < 0);
   ASSERT (mbscasecmp ("paragraph", "para") > 0);
+}
 
+static void
+test_utf_8 (bool turkish)
+{
   /* The following tests shows how mbscasecmp() is different from
      strcasecmp().  */
 
-  ASSERT (mbscasecmp ("\303\266zg\303\274r", "\303\226ZG\303\234R") == 0); /* özgür */
-  ASSERT (mbscasecmp ("\303\226ZG\303\234R", "\303\266zg\303\274r") == 0); /* özgür */
+  if (turkish)
+    {
+      ASSERT (mbscasecmp ("\303\266zg\303\274r", "\303\226ZG\303\234R") == 0); /* özgür */
+      ASSERT (mbscasecmp ("\303\226ZG\303\234R", "\303\266zg\303\274r") == 0); /* özgür */
+
+      /* This test shows how strings of different size can compare equal.  */
+      ASSERT (mbscasecmp ("turkish", "TURK\304\260SH") == 0);
+      ASSERT (mbscasecmp ("TURK\304\260SH", "turkish") == 0);
+    }
+
+  /* Incomplete characters.  See
+     https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf
+     page 128 table 3-11.  */
+
+  /* "\341\200\240" = 0xE1 0x80 0xA0 = U+1020.  */
+  ASSERT (mbscasecmp ("\341\200", "\341\200") == 0);
+  ASSERT (mbscasecmp ("\341\200X", "\341\200x") == 0);
+  ASSERT (mbscasecmp ("\341", "\341") == 0);
+  ASSERT (mbscasecmp ("\341X", "\341x") == 0);
+  /* "\360\221\222\240" = 0xF0 0x91 0x92 0xA0 = U+114A0.  */
+  ASSERT (mbscasecmp ("\360\221\222", "\360\221\222") == 0);
+  ASSERT (mbscasecmp ("\360\221\222X", "\360\221\222x") == 0);
+  ASSERT (mbscasecmp ("\360\221", "\360\221") == 0);
+  ASSERT (mbscasecmp ("\360\221X", "\360\221x") == 0);
+  ASSERT (mbscasecmp ("\360", "\360") == 0);
+  ASSERT (mbscasecmp ("\360X", "\360x") == 0);
+}
+
+int
+main (int argc, char *argv[])
+{
+  /* configure should already have checked that the locale is supported.  */
+  if (setlocale (LC_ALL, "") == NULL)
+    return 1;
+
+  test_ascii ();
+
+  if (argc > 1)
+    switch (argv[1][0])
+      {
+      case '3':
+        /* Locale encoding is UTF-8, locale is not Turkish.  */
+        test_utf_8 (false);
+        return test_exit_status;
 
-  /* This test shows how strings of different size can compare equal.  */
-  ASSERT (mbscasecmp ("turkish", "TURK\304\260SH") == 0);
-  ASSERT (mbscasecmp ("TURK\304\260SH", "turkish") == 0);
+      case '4':
+        /* Locale encoding is UTF-8, locale is Turkish.  */
+        test_utf_8 (true);
+        return test_exit_status;
+      }
 
-  return test_exit_status;
+  return 1;
 }
-- 
2.54.0
0006-mbmemcasecmp-tests-Enhance-tests.patch (text/x-patch, 2.5 KB)
From 8d19402b5bd78976c08312da1e387d16c8fb8ff9 Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Mon, 25 May 2026 18:36:12 +0200
Subject: [PATCH 6/7] mbmemcasecmp tests: Enhance tests.

* tests/test-mbmemcasecmp.h (test_utf_8): Add test cases with incomplete
characters.
---
 ChangeLog                 |  6 ++++++
 tests/test-mbmemcasecmp.h | 36 ++++++++++++++++++++++++++++++++++++
 2 files changed, 42 insertions(+)

diff --git a/ChangeLog b/ChangeLog
index c5b5e39291..766a5860a5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2026-05-25  Bruno Haible  <[email protected]>
+
+	mbmemcasecmp tests: Enhance tests.
+	* tests/test-mbmemcasecmp.h (test_utf_8): Add test cases with incomplete
+	characters.
+
 2026-05-25  Bruno Haible  <[email protected]>
 
 	mbspcasecmp tests: Enhance tests.
diff --git a/tests/test-mbmemcasecmp.h b/tests/test-mbmemcasecmp.h
index c2175815b2..ff19c70b5c 100644
--- a/tests/test-mbmemcasecmp.h
+++ b/tests/test-mbmemcasecmp.h
@@ -395,4 +395,40 @@ test_utf_8 (int (*my_casecmp) (const char *, size_t, const char *, size_t), bool
     ASSERT (my_casecmp (input, countof (input), casefolded_decomposed, countof (casefolded_decomposed)) == 0);
   }
   #endif
+
+  /* Incomplete characters.  See
+     https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf
+     page 128 table 3-11.  */
+  /* 0xE1 0x80 0xA0 = U+1020.  */
+  {
+    static const char input1[] = { 0xE1, 0x80, 'x', 0xE1, 0x80 };
+    static const char input2[] = { 0xE1, 0x80, 'X', 0xE1, 0x80 };
+
+    ASSERT (my_casecmp (input1, countof (input1), input2, countof (input2)) == 0);
+  }
+  {
+    static const char input1[] = { 0xE1, 'x', 0xE1 };
+    static const char input2[] = { 0xE1, 'X', 0xE1 };
+
+    ASSERT (my_casecmp (input1, countof (input1), input2, countof (input2)) == 0);
+  }
+  /* 0xF0 0x91 0x92 0xA0 = U+114A0.  */
+  {
+    static const char input1[] = { 0xF0, 0x91, 0x92, 'x', 0xF0, 0x91, 0x92 };
+    static const char input2[] = { 0xF0, 0x91, 0x92, 'X', 0xF0, 0x91, 0x92 };
+
+    ASSERT (my_casecmp (input1, countof (input1), input2, countof (input2)) == 0);
+  }
+  {
+    static const char input1[] = { 0xF0, 0x91, 'x', 0xF0, 0x91 };
+    static const char input2[] = { 0xF0, 0x91, 'X', 0xF0, 0x91 };
+
+    ASSERT (my_casecmp (input1, countof (input1), input2, countof (input2)) == 0);
+  }
+  {
+    static const char input1[] = { 0xF0, 'x', 0xF0 };
+    static const char input2[] = { 0xF0, 'X', 0xF0 };
+
+    ASSERT (my_casecmp (input1, countof (input1), input2, countof (input2)) == 0);
+  }
 }
-- 
2.54.0
0007-trim-tests-Enhance-tests.patch (text/x-patch, 2.1 KB)
From 070f9259d67373ef7530e8a2523b04258920a449 Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Mon, 25 May 2026 18:37:25 +0200
Subject: [PATCH 7/7] trim tests: Enhance tests.

* tests/test-trim.c (main): Add test cases with incomplete characters.
---
 ChangeLog         |  5 +++++
 tests/test-trim.c | 30 ++++++++++++++++++++++++++++++
 2 files changed, 35 insertions(+)

diff --git a/ChangeLog b/ChangeLog
index 766a5860a5..0611c6c7a1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2026-05-25  Bruno Haible  <[email protected]>
+
+	trim tests: Enhance tests.
+	* tests/test-trim.c (main): Add test cases with incomplete characters.
+
 2026-05-25  Bruno Haible  <[email protected]>
 
 	mbmemcasecmp tests: Enhance tests.
diff --git a/tests/test-trim.c b/tests/test-trim.c
index 745c7492dd..27a7a193c4 100644
--- a/tests/test-trim.c
+++ b/tests/test-trim.c
@@ -133,6 +133,36 @@ main (int argc, char *argv[])
           ASSERT (streq (result, "\302\267foo"));
           free (result);
         }
+        /* Incomplete characters.  See
+           https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf
+           page 128 table 3-11.  */
+        /* "\341\200\240" = 0xE1 0x80 0xA0 = U+1020.  */
+        {
+          char *result = trim ("\342\200\202\341\200\342\200\202");
+          ASSERT (streq (result, "\341\200"));
+          free (result);
+        }
+        {
+          char *result = trim ("\342\200\202\341\342\200\202");
+          ASSERT (streq (result, "\341"));
+          free (result);
+        }
+        /* "\360\221\222\240" = 0xF0 0x91 0x92 0xA0 = U+114A0.  */
+        {
+          char *result = trim ("\342\200\202\360\221\222\342\200\202");
+          ASSERT (streq (result, "\360\221\222"));
+          free (result);
+        }
+        {
+          char *result = trim ("\342\200\202\360\221\342\200\202");
+          ASSERT (streq (result, "\360\221"));
+          free (result);
+        }
+        {
+          char *result = trim ("\342\200\202\360\342\200\202");
+          ASSERT (streq (result, "\360"));
+          free (result);
+        }
         return test_exit_status;
 
       case '3':
-- 
2.54.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.