//---------------------------------------------------------------------- // glibc-2.36/string/tester.c original from http://ftp.gnu.org/gnu/glibc/glibc-2.36.tar.xz // added extra DEBUG printing and removed libc-diag.h dependency and all tests except: // strspn, strcspn, strcat, strcpy, strncpy, strchr, strrchr, strstr, memcpy, memset, // which are renamed to my_str... and my_mem... via rename.h // // generate tester.o using -w to disable warnings: gcc -c -w tester.c // to get continuous messages as tests are passed: gcc -c -w tester.c -DDEBUG // // link with your code: gcc -std=c11 -pedantic -Wall p1.c tester.o //---------------------------------------------------------------------- /* Tester for string functions. Copyright (C) 1995-2022 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 . */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif /* Make sure we don't test the optimized inline functions if we want to test the real implementation. */ #if !defined DO_STRING_INLINES #undef __USE_STRING_INLINES #endif #include #include #include #include "rename.h" /* This file tests a range of corner cases of string functions, including cases where truncation occurs or where sizes specified are larger than the actual buffers, which result in various warnings. */ #define STREQ(a, b) (strcmp((a), (b)) == 0) const char *it = ""; /* Routine name for message routines. */ size_t errors = 0; /* Complain if condition is not true. */ static void check (int thing, int number) { if (!thing) { printf ("%s flunked test %d\n", it, number); ++errors; } #ifdef DEBUG else printf("%s passed test %d\n", it, number); #endif } /* Complain if first two args don't strcmp as equal. */ static void equal (const char *a, const char *b, int number) { check (a != NULL && b != NULL && STREQ (a, b), number); } char one[50]; char two[50]; char *cp; #define SIMPLE_COPY(fn, n, str, ntest) \ do { \ int __n; \ char *cp; \ for (__n = 0; __n < (int) sizeof (one); ++__n) \ one[__n] = 'Z'; \ fn (one, str); \ for (cp = one, __n = 0; __n < n; ++__n, ++cp) \ check (*cp == '0' + (n % 10), ntest); \ check (*cp == '\0', ntest); \ } while (0) static void test_strcpy (void) { int i; it = "strcpy"; check (strcpy (one, "abcd") == one, 1); /* Returned value. */ equal (one, "abcd", 2); /* Basic test. */ (void) strcpy (one, "x"); equal (one, "x", 3); /* Writeover. */ equal (one+2, "cd", 4); /* Wrote too much? */ (void) strcpy (two, "hi there"); (void) strcpy (one, two); equal (one, "hi there", 5); /* Basic test encore. */ equal (two, "hi there", 6); /* Stomped on source? */ (void) strcpy (one, ""); equal (one, "", 7); /* Boundary condition. */ for (i = 0; i < 16; i++) { (void) strcpy (one + i, "hi there"); /* Unaligned destination. */ equal (one + i, "hi there", 8 + (i * 2)); (void) strcpy (two, one + i); /* Unaligned source. */ equal (two, "hi there", 9 + (i * 2)); } SIMPLE_COPY(strcpy, 0, "", 41); SIMPLE_COPY(strcpy, 1, "1", 42); SIMPLE_COPY(strcpy, 2, "22", 43); SIMPLE_COPY(strcpy, 3, "333", 44); SIMPLE_COPY(strcpy, 4, "4444", 45); SIMPLE_COPY(strcpy, 5, "55555", 46); SIMPLE_COPY(strcpy, 6, "666666", 47); SIMPLE_COPY(strcpy, 7, "7777777", 48); SIMPLE_COPY(strcpy, 8, "88888888", 49); SIMPLE_COPY(strcpy, 9, "999999999", 50); SIMPLE_COPY(strcpy, 10, "0000000000", 51); SIMPLE_COPY(strcpy, 11, "11111111111", 52); SIMPLE_COPY(strcpy, 12, "222222222222", 53); SIMPLE_COPY(strcpy, 13, "3333333333333", 54); SIMPLE_COPY(strcpy, 14, "44444444444444", 55); SIMPLE_COPY(strcpy, 15, "555555555555555", 56); SIMPLE_COPY(strcpy, 16, "6666666666666666", 57); /* Simple test using implicitly coerced `void *' arguments. */ const void *src = "frobozz"; void *dst = one; check (strcpy (dst, src) == dst, 1); equal (dst, "frobozz", 2); } static void test_strcat (void) { it = "strcat"; (void) strcpy (one, "ijk"); check (strcat (one, "lmn") == one, 1); /* Returned value. */ equal (one, "ijklmn", 2); /* Basic test. */ (void) strcpy (one, "x"); (void) strcat (one, "yz"); equal (one, "xyz", 3); /* Writeover. */ equal (one+4, "mn", 4); /* Wrote too much? */ (void) strcpy (one, "gh"); (void) strcpy (two, "ef"); (void) strcat (one, two); equal (one, "ghef", 5); /* Basic test encore. */ equal (two, "ef", 6); /* Stomped on source? */ (void) strcpy (one, ""); (void) strcat (one, ""); equal (one, "", 7); /* Boundary conditions. */ (void) strcpy (one, "ab"); (void) strcat (one, ""); equal (one, "ab", 8); (void) strcpy (one, ""); (void) strcat (one, "cd"); equal (one, "cd", 9); int ntest = 10; char buf1[80] __attribute__ ((aligned (16))); char buf2[32] __attribute__ ((aligned (16))); for (size_t n1 = 0; n1 < 16; ++n1) for (size_t n2 = 0; n2 < 16; ++n2) for (size_t n3 = 0; n3 < 32; ++n3) { size_t olderrors = errors; memset (buf1, 'b', sizeof (buf1)); memset (buf1 + n2, 'a', n3); buf1[n2 + n3] = '\0'; strcpy (buf2 + n1, "123"); check (strcat (buf1 + n2, buf2 + n1) == buf1 + n2, ntest); if (errors == olderrors) for (size_t i = 0; i < sizeof (buf1); ++i) { if (i < n2) check (buf1[i] == 'b', ntest); else if (i < n2 + n3) check (buf1[i] == 'a', ntest); else if (i < n2 + n3 + 3) check (buf1[i] == "123"[i - (n2 + n3)], ntest); else if (i == n2 + n3 + 3) check (buf1[i] == '\0', ntest); else check (buf1[i] == 'b', ntest); if (errors != olderrors) { printf ("n1=%zu, n2=%zu, n3=%zu, buf1=%02hhx", n1, n2, n3, buf1[0]); for (size_t j = 1; j < sizeof (buf1); ++j) printf (",%02hhx", buf1[j]); putchar_unlocked ('\n'); break; } } } } static void test_strncpy (void) { /* Testing is a bit different because of odd semantics. */ it = "strncpy"; check (strncpy (one, "abc", 4) == one, 1); /* Returned value. */ equal (one, "abc", 2); /* Did the copy go right? */ (void) strcpy (one, "abcdefgh"); (void) strncpy (one, "xyz", 2); equal (one, "xycdefgh", 3); /* Copy cut by count. */ (void) strcpy (one, "abcdefgh"); (void) strncpy (one, "xyz", 3); /* Copy cut just before NUL. */ equal (one, "xyzdefgh", 4); (void) strcpy (one, "abcdefgh"); (void) strncpy (one, "xyz", 4); /* Copy just includes NUL. */ equal (one, "xyz", 5); equal (one+4, "efgh", 6); /* Wrote too much? */ (void) strcpy (one, "abcdefgh"); (void) strncpy (one, "xyz", 5); /* Copy includes padding. */ equal (one, "xyz", 7); equal (one+4, "", 8); equal (one+5, "fgh", 9); (void) strcpy (one, "abc"); (void) strncpy (one, "xyz", 0); /* Zero-length copy. */ equal (one, "abc", 10); (void) strncpy (one, "", 2); /* Zero-length source. */ equal (one, "", 11); equal (one+1, "", 12); equal (one+2, "c", 13); (void) strcpy (one, "hi there"); (void) strncpy (two, one, 9); equal (two, "hi there", 14); /* Just paranoia. */ equal (one, "hi there", 15); /* Stomped on source? */ } static void test_strchr (void) { it = "strchr"; check (strchr ("abcd", 'z') == NULL, 1); /* Not found. */ (void) strcpy (one, "abcd"); check (strchr (one, 'c') == one+2, 2); /* Basic test. */ check (strchr (one, 'd') == one+3, 3); /* End of string. */ check (strchr (one, 'a') == one, 4); /* Beginning. */ check (strchr (one, '\0') == one+4, 5); /* Finding NUL. */ (void) strcpy (one, "ababa"); check (strchr (one, 'b') == one+1, 6); /* Finding first. */ (void) strcpy (one, ""); check (strchr (one, 'b') == NULL, 7); /* Empty string. */ check (strchr (one, '\0') == one, 8); /* NUL in empty string. */ { char buf[4096]; int i; char *p; for (i=0; i < 0x100; i++) { p = (char *) ((unsigned long int) (buf + 0xff) & ~0xff) + i; strcpy (p, "OK"); strcpy (p+3, "BAD/WRONG"); check (strchr (p, '/') == NULL, 9+i); } } } static void test_strrchr (void) { it = "strrchr"; check (strrchr ("abcd", 'z') == NULL, 1); /* Not found. */ (void) strcpy (one, "abcd"); check (strrchr (one, 'c') == one+2, 2); /* Basic test. */ check (strrchr (one, 'd') == one+3, 3); /* End of string. */ check (strrchr (one, 'a') == one, 4); /* Beginning. */ check (strrchr (one, '\0') == one+4, 5); /* Finding NUL. */ (void) strcpy (one, "ababa"); check (strrchr (one, 'b') == one+3, 6); /* Finding last. */ (void) strcpy (one, ""); check (strrchr (one, 'b') == NULL, 7); /* Empty string. */ check (strrchr (one, '\0') == one, 8); /* NUL in empty string. */ { char buf[4096]; int i; char *p; for (i=0; i < 0x100; i++) { p = (char *) ((unsigned long int) (buf + 0xff) & ~0xff) + i; strcpy (p, "OK"); strcpy (p+3, "BAD/WRONG"); check (strrchr (p, '/') == NULL, 9+i); } } } static void test_strstr (void) { it = "strstr"; check(strstr("abcd", "z") == NULL, 1); /* Not found. */ check(strstr("abcd", "abx") == NULL, 2); /* Dead end. */ (void) strcpy(one, "abcd"); check(strstr(one, "c") == one+2, 3); /* Basic test. */ check(strstr(one, "bc") == one+1, 4); /* Multichar. */ check(strstr(one, "d") == one+3, 5); /* End of string. */ check(strstr(one, "cd") == one+2, 6); /* Tail of string. */ check(strstr(one, "abc") == one, 7); /* Beginning. */ check(strstr(one, "abcd") == one, 8); /* Exact match. */ check(strstr(one, "abcde") == NULL, 9); /* Too long. */ check(strstr(one, "de") == NULL, 10); /* Past end. */ check(strstr(one, "") == one, 11); /* Finding empty. */ (void) strcpy(one, "ababa"); check(strstr(one, "ba") == one+1, 12); /* Finding first. */ (void) strcpy(one, ""); check(strstr(one, "b") == NULL, 13); /* Empty string. */ check(strstr(one, "") == one, 14); /* Empty in empty string. */ (void) strcpy(one, "bcbca"); check(strstr(one, "bca") == one+2, 15); /* False start. */ (void) strcpy(one, "bbbcabbca"); check(strstr(one, "bbca") == one+1, 16); /* With overlap. */ } static void test_strspn (void) { it = "strspn"; check(strspn("abcba", "abc") == 5, 1); /* Whole string. */ check(strspn("abcba", "ab") == 2, 2); /* Partial. */ check(strspn("abc", "qx") == 0, 3); /* None. */ check(strspn("", "ab") == 0, 4); /* Null string. */ check(strspn("abc", "") == 0, 5); /* Null search list. */ } static void test_strcspn (void) { it = "strcspn"; check(strcspn("abcba", "qx") == 5, 1); /* Whole string. */ check(strcspn("abcba", "cx") == 2, 2); /* Partial. */ check(strcspn("abc", "abc") == 0, 3); /* None. */ check(strcspn("", "ab") == 0, 4); /* Null string. */ check(strcspn("abc", "") == 3, 5); /* Null search list. */ } static void test_memcpy (void) { int i; it = "memcpy"; check(memcpy(one, "abc", 4) == one, 1); /* Returned value. */ equal(one, "abc", 2); /* Did the copy go right? */ (void) strcpy(one, "abcdefgh"); (void) memcpy(one+1, "xyz", 2); equal(one, "axydefgh", 3); /* Basic test. */ (void) strcpy(one, "abc"); (void) memcpy(one, "xyz", 0); equal(one, "abc", 4); /* Zero-length copy. */ (void) strcpy(one, "hi there"); (void) strcpy(two, "foo"); (void) memcpy(two, one, 9); equal(two, "hi there", 5); /* Just paranoia. */ equal(one, "hi there", 6); /* Stomped on source? */ for (i = 0; i < 16; i++) { const char *x = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; strcpy (one, x); check (memcpy (one + i, "hi there", 9) == one + i, 7 + (i * 6)); /* Unaligned destination. */ check (memcmp (one, x, i) == 0, 8 + (i * 6)); /* Wrote under? */ equal (one + i, "hi there", 9 + (i * 6)); check (one[i + 9] == 'x', 10 + (i * 6)); /* Wrote over? */ check (memcpy (two, one + i, 9) == two, 11 + (i * 6)); /* Unaligned source. */ equal (two, "hi there", 12 + (i * 6)); } } static void test_memset (void) { int i; it = "memset"; (void) strcpy(one, "abcdefgh"); check(memset(one+1, 'x', 3) == one+1, 1); /* Return value. */ equal(one, "axxxefgh", 2); /* Basic test. */ (void) memset(one+2, 'y', 0); equal(one, "axxxefgh", 3); /* Zero-length set. */ (void) memset(one+5, 0, 1); equal(one, "axxxe", 4); /* Zero fill. */ equal(one+6, "gh", 5); /* And the leftover. */ (void) memset(one+2, 010045, 1); equal(one, "ax\045xe", 6); /* Unsigned char convert. */ /* Non-8bit fill character. */ memset (one, 0x101, sizeof (one)); for (i = 0; i < (int) sizeof (one); ++i) check (one[i] == '\01', 7); /* Test for more complex versions of memset, for all alignments and lengths up to 256. This test takes a little while, perhaps it should be made weaker? */ { char data[512]; int j; int k; int c; for (i = 0; i < 512; i++) data[i] = 'x'; for (c = 0; c <= 'y'; c += 'y') /* check for memset(,0,) and memset(,'y',) */ for (j = 0; j < 256; j++) for (i = 0; i < 256; i++) { memset (data + i, c, j); for (k = 0; k < i; k++) if (data[k] != 'x') goto fail; for (k = i; k < i+j; k++) { if (data[k] != c) goto fail; data[k] = 'x'; } for (k = i+j; k < 512; k++) if (data[k] != 'x') goto fail; continue; fail: check (0, 8 + i + j * 256 + (c != 0) * 256 * 256); } } } int main (void) { int status; /* Test strcpy next because we need it to set up other tests. */ test_strcpy (); /* strcat. */ test_strcat (); /* strncpy. */ test_strncpy (); /* strchr. */ test_strchr (); /* strrchr. */ test_strrchr (); /* strstr - somewhat like strchr. */ test_strstr (); /* strspn. */ test_strspn (); /* strcspn. */ test_strcspn (); /* memcpy - need not work for overlap. */ test_memcpy (); /* memset. */ test_memset (); if (errors == 0) { status = EXIT_SUCCESS; puts("No errors."); } else { status = EXIT_FAILURE; printf("%Zd errors.\n", errors); } return status; }