libc/tests/string: add a more comprehensive unit test for strrchr()
The unit tests are patterned after those for memrchr(). This catches the issue found in 293915. PR: 293915 Reviewed by: strajabot Reported by: safonov.paul@gmail.com MFC after: 1 week Differential Revision: https://reviews.freebsd.org/D56037
This commit is contained in:
@@ -20,6 +20,7 @@ ATF_TESTS_C+= strcmp2_test
|
||||
ATF_TESTS_C+= strcspn_test
|
||||
ATF_TESTS_C+= strerror2_test
|
||||
ATF_TESTS_C+= strlcpy_test
|
||||
ATF_TESTS_C+= strrchr2_test
|
||||
ATF_TESTS_C+= strspn_test
|
||||
ATF_TESTS_C+= strverscmp_test
|
||||
ATF_TESTS_C+= strxfrm_test
|
||||
@@ -49,6 +50,7 @@ NETBSD_ATF_TESTS_C+= swab_test
|
||||
SRCS.memset2_test= memset_test.c
|
||||
SRCS.strcmp2_test= strcmp_test.c
|
||||
SRCS.strerror2_test= strerror_test.c
|
||||
SRCS.strrchr2_test= strrchr_test.c
|
||||
|
||||
.include "../Makefile.netbsd-tests"
|
||||
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2023, 2026 Robert Clausecker <fuz@FreeBSD.org>
|
||||
*
|
||||
* Adapted from memrchr_test.c.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <atf-c.h>
|
||||
|
||||
static char *(*strrchr_fn)(const char *, int);
|
||||
|
||||
/*
|
||||
* Check that when looking for the character NUL, we find the
|
||||
* string terminator, and not some NUL character after it.
|
||||
*/
|
||||
ATF_TC_WITHOUT_HEAD(nul);
|
||||
ATF_TC_BODY(nul, tc)
|
||||
{
|
||||
size_t i, j, k;
|
||||
char buf[1+15+64]; /* offset [0+15] + 64 buffer bytes + sentinels */
|
||||
|
||||
buf[0] = '\0';
|
||||
memset(buf + 1, '-', sizeof(buf) - 1);
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
for (j = 0; j < 64; j++)
|
||||
for (k = j; k < 64; k++) {
|
||||
buf[i + j + 1] = '\0';
|
||||
buf[i + k + 1] = '\0';
|
||||
ATF_CHECK_EQ(strrchr_fn(buf + i + 1, '\0'), buf + i + j + 1);
|
||||
buf[i + j + 1] = '-';
|
||||
buf[i + k + 1] = '-';
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that if the character 'X' does not occur in the string
|
||||
* (but occurs before and after it), we correctly return NULL.
|
||||
*/
|
||||
ATF_TC_WITHOUT_HEAD(not_found);
|
||||
ATF_TC_BODY(not_found, tc)
|
||||
{
|
||||
size_t i, j;
|
||||
char buf[1+15+64+2]; /* offset [0..15] + 64 buffer bytes + sentinels */
|
||||
|
||||
buf[0] = 'X';
|
||||
memset(buf + 1, '-', sizeof(buf) - 1);
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
for (j = 0; j < 64; j++) {
|
||||
buf[i + j + 1] = '\0';
|
||||
buf[i + j + 2] = 'X';
|
||||
ATF_CHECK_EQ(strrchr_fn(buf + i + 1, 'X'), NULL);
|
||||
buf[i + j + 1] = '-';
|
||||
buf[i + j + 2] = '-';
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
do_found_test(char buf[], size_t first, size_t second)
|
||||
{
|
||||
/* invariant: first <= second */
|
||||
|
||||
buf[first] = 'X';
|
||||
buf[second] = 'X';
|
||||
ATF_CHECK_EQ(strrchr_fn(buf, 'X'), buf + second);
|
||||
buf[first] = '-';
|
||||
buf[second] = '-';
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that if the character 'X' occurs in the string multiple
|
||||
* times (i. e. twice), its last encounter is returned.
|
||||
*/
|
||||
ATF_TC_WITHOUT_HEAD(found);
|
||||
ATF_TC_BODY(found, tc)
|
||||
{
|
||||
size_t i, j, k, l;
|
||||
char buf[1+15+64+2];
|
||||
|
||||
buf[0] = 'X';
|
||||
memset(buf + 1, '-', sizeof(buf) - 1);
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
for (j = 0; j < 64; j++)
|
||||
for (k = 0; k < j; k++)
|
||||
for (l = 0; l <= k; l++) {
|
||||
buf[i + j + 1] = '\0';
|
||||
buf[i + j + 2] = 'X';
|
||||
do_found_test(buf + i + 1, l, k);
|
||||
buf[i + j + 1] = '-';
|
||||
buf[i + j + 2] = '-';
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
do_values_test(char buf[], size_t len, size_t i, int c)
|
||||
{
|
||||
/* sentinels */
|
||||
buf[-1] = c;
|
||||
buf[len] = '\0';
|
||||
buf[len + 1] = 'c';
|
||||
|
||||
/* fill the string with some other character, but not with NUL */
|
||||
memset(buf, c == UCHAR_MAX ? c - 1 : c + 1, len);
|
||||
|
||||
if (i < len) {
|
||||
buf[i] = c;
|
||||
ATF_CHECK_EQ(strrchr_fn(buf, c), buf + i);
|
||||
} else
|
||||
ATF_CHECK_EQ(strrchr_fn(buf, c), c == 0 ? buf + len : NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that the character is found regardless of its value.
|
||||
* This catches arithmetic (overflow) errors in incorrect SWAR
|
||||
* implementations of byte-parallel character matching.
|
||||
*/
|
||||
ATF_TC_WITHOUT_HEAD(values);
|
||||
ATF_TC_BODY(values, tc)
|
||||
{
|
||||
size_t i, j, k;
|
||||
int c;
|
||||
char buf[1+15+64+2];
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
for (j = 0; j < 64; j++)
|
||||
for (k = 0; k <= j; k++)
|
||||
for (c = 0; c <= UCHAR_MAX; c++)
|
||||
do_values_test(buf + i + 1, j, k, c);
|
||||
}
|
||||
|
||||
ATF_TP_ADD_TCS(tp)
|
||||
{
|
||||
void *dl_handle;
|
||||
|
||||
dl_handle = dlopen(NULL, RTLD_LAZY);
|
||||
strrchr_fn = dlsym(dl_handle, "test_strrchr");
|
||||
if (strrchr_fn == NULL)
|
||||
strrchr_fn = strrchr;
|
||||
|
||||
ATF_TP_ADD_TC(tp, nul);
|
||||
ATF_TP_ADD_TC(tp, not_found);
|
||||
ATF_TP_ADD_TC(tp, found);
|
||||
ATF_TP_ADD_TC(tp, values);
|
||||
|
||||
return (atf_no_error());
|
||||
}
|
||||
Reference in New Issue
Block a user