/* Unit tests of winlibc.c Copyright © 2021-2024 Samuel Lidén Borell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "../winlibc/winlibc.c" #include "unittest.h" #include "alltests.h" /** Performs an equality test and calls free() afterwards */ #define EQ_F(actual, expected) \ (tmp = (actual), \ eq = tmp != NULL && \ tmp != (expected) && \ !strcmp(tmp, (expected)), \ free(tmp), \ eq) static void test_memcpy(void) { static const char src[2] = "AB"; char dst[4] = {0}; tsoftassert(memcpy(dst, src, 0) == dst); tsoftassert(dst[0] == 0); tsoftassert(dst[3] == 0); tsoftassert(memcpy(dst, src, 2) == dst); tsoftassert(dst[0] == 'A'); tsoftassert(dst[1] == 'B'); tsoftassert(dst[2] == 0); tsoftassert(dst[3] == 0); } static void test_memset(void) { unsigned char buff[3]; tsoftassert(memset(buff, 0xAB, 3) == buff); tsoftassert(buff[0] == 0xAB); tsoftassert(buff[1] == 0xAB); tsoftassert(buff[2] == 0xAB); tsoftassert(memset(buff+1, 0xCD, 1) == buff+1); tsoftassert(memset(buff+1, 0xEF, 0) == buff+1); tsoftassert(memset(buff+3, 0xEF, 0) == buff+3); tsoftassert(buff[0] == 0xAB); tsoftassert(buff[1] == 0xCD); tsoftassert(buff[2] == 0xAB); tsoftassert(memset(buff+1, 0x00, 1) == buff+1); tsoftassert(buff[0] == 0xAB); tsoftassert(buff[1] == 0x00); tsoftassert(buff[2] == 0xAB); } static void test_memcmp(void) { static const unsigned char buff1[] = { 0x12, 0x23, 0x00, 0xCD, 0xEE }; static const unsigned char buff2[] = { 0x12, 0x23, 0x00, 0xCD, 0xEF }; tsoftassert(memcmp(buff1, buff1, 0) == 0); tsoftassert(memcmp(buff1, buff2, 0) == 0); tsoftassert(memcmp(buff1, buff1, 5) == 0); tsoftassert(memcmp(buff1, buff2, 4) == 0); tsoftassert(memcmp(buff1, buff2, 5) < 0); tsoftassert(memcmp(buff2, buff1, 5) > 0); tsoftassert(memcmp(&buff1[4], buff2, 1) > 0); tsoftassert(memcmp(&buff2[4], buff1, 1) > 0); tsoftassert(memcmp(&buff1[5], &buff2[5], 0) == 0); } static void test_memchr(void) { static const unsigned char buff[] = { 0x12, 0x23, 0x00, 0x12, 0xEF }; tsoftassert(memchr(buff, 0x12, 0) == NULL); tsoftassert(memchr(buff, 0x12, 1) == &buff[0]); tsoftassert(memchr(buff, 0x12, 5) == &buff[0]); tsoftassert(memchr(buff, 0x23, 1) == NULL); tsoftassert(memchr(buff, 0x00, 3) == &buff[2]); tsoftassert(memchr(buff, 0x00, 5) == &buff[2]); tsoftassert(memchr(buff, 0xEF, 5) == &buff[4]); } static void test_memrchr(void) { static const unsigned char buff[] = { 0x12, 0x23, 0x00, 0x12, 0xEF }; tsoftassert(memrchr(buff, 0x12, 0) == NULL); tsoftassert(memrchr(buff, 0x12, 1) == &buff[0]); tsoftassert(memrchr(buff, 0x12, 5) == &buff[3]); tsoftassert(memrchr(buff, 0x23, 1) == NULL); tsoftassert(memrchr(buff, 0x00, 3) == &buff[2]); tsoftassert(memrchr(buff, 0x00, 5) == &buff[2]); tsoftassert(memrchr(buff, 0xEF, 5) == &buff[4]); } static void test_strchr(void) { static const char s[] = "Test string!"; tsoftassert(strchr("", 'T') == NULL); tsoftassert(strchr(s, 'T') == s); tsoftassert(strchr(s, 's') == s+2); tsoftassert(strchr(s, '!') == s+11); } static void test_strrchr(void) { static const char s[] = "Test string!"; tsoftassert(strrchr("", 'T') == NULL); tsoftassert(strrchr(s, 'T') == s); tsoftassert(strrchr(s, 's') == s+5); tsoftassert(strrchr(s, '!') == s+11); } static void test_strcmp(void) { static const char buff2[] = "bc"; tsoftassert(strcmp("a", buff2) < 0); tsoftassert(strcmp("b", buff2) < 0); tsoftassert(strcmp("c", buff2) > 0); tsoftassert(strcmp("ba", buff2) < 0); tsoftassert(strcmp("bc", buff2) == 0); tsoftassert(strcmp("bd", buff2) > 0); tsoftassert(strcmp(buff2, buff2) == 0); tsoftassert(strcmp("bcd", buff2) > 0); tsoftassert(strcmp("b\xFF", buff2) > 0); tsoftassert(strcmp(buff2, "b\xFF") < 0); } static void test_strncmp(void) { static const char buff2[] = "bc"; tsoftassert(strncmp(NULL, NULL, 0) == 0); tsoftassert(strncmp("a", buff2, 1) < 0); tsoftassert(strncmp("b", buff2, 1) == 0); tsoftassert(strncmp("c", buff2, 1) > 0); tsoftassert(strncmp("ba", buff2, 2) < 0); tsoftassert(strncmp("bc", buff2, 2) == 0); tsoftassert(strncmp("bd", buff2, 2) > 0); tsoftassert(strncmp(buff2, buff2, 2) == 0); tsoftassert(strncmp("bcd", buff2, 3) > 0); tsoftassert(strncmp("b\xFF", buff2, 2) > 0); tsoftassert(strncmp(buff2, "b\xFF", 2) < 0); } static void test_strlen(void) { tsoftassert(strlen("") == 0); tsoftassert(strlen("x") == 1); tsoftassert(strlen("0\xFF") == 2); tsoftassert(strlen("0123456789") == 10); } static void test_strnlen(void) { tsoftassert(strnlen("", 0) == 0); tsoftassert(strnlen("", 1) == 0); tsoftassert(strnlen("x", 0) == 0); tsoftassert(strnlen("x", 1) == 1); tsoftassert(strnlen("x", 2) == 1); tsoftassert(strnlen("0\xFF", 3) == 2); tsoftassert(strnlen("0123456789", 9) == 9); tsoftassert(strnlen("0123456789", 11) == 10); } static void test_strcspn(void) { tsoftassert(strcspn("", "") == 0); tsoftassert(strcspn("", "a") == 0); tsoftassert(strcspn("a", "") == 1); tsoftassert(strcspn("a", "a") == 0); tsoftassert(strcspn("b", "a") == 1); tsoftassert(strcspn("ab", "a") == 0); tsoftassert(strcspn("ba", "ab") == 0); tsoftassert(strcspn("cba", "ab") == 1); tsoftassert(strcspn("cba", "ba") == 1); tsoftassert(strcspn("cba", "a") == 2); tsoftassert(strcspn("cba", "x") == 3); } static void test_strspn(void) { tsoftassert(strspn("", "") == 0); tsoftassert(strspn("", "a") == 0); tsoftassert(strspn("a", "") == 0); tsoftassert(strspn("a", "a") == 1); tsoftassert(strspn("b", "a") == 0); tsoftassert(strspn("ab", "a") == 1); tsoftassert(strspn("ba", "ab") == 2); tsoftassert(strspn("cba", "ab") == 0); tsoftassert(strspn("cba", "abc") == 3); tsoftassert(strspn("cba", "bac") == 3); tsoftassert(strspn("cba", "bc") == 2); tsoftassert(strspn("cba", "x") == 0); } static void test_strdup(void) { char *tmp; int eq; tsoftassert(EQ_F(strdup(""), "")); tsoftassert(EQ_F(strdup("Abc"), "Abc")); } static void test_strndup(void) { char *tmp; int eq; tsoftassert(EQ_F(strndup("", 0), "")); tsoftassert(EQ_F(strndup("", 3), "")); tsoftassert(EQ_F(strndup("Abc", 3), "Abc")); tsoftassert(EQ_F(strndup("Abc", 5), "Abc")); tsoftassert(EQ_F(strndup("Abc", 2), "Ab")); tsoftassert(EQ_F(strndup("Abc", 0), "")); } static void test_malloc(void) { char *p = malloc(1); tassert(p != NULL); p[0] = 123; free(p); p = malloc(0); free(p); } static void test_realloc(void) { char *p = malloc(1); tassert(p != NULL); p[0] = 123; p = realloc(p, 2); tassert(p != NULL); tsoftassert(p[0] == 123); p[1] = 34; p = realloc(p, 1); tassert(p != NULL); tsoftassert(p[0] == 123); p = realloc(p, 0); tassert(p == NULL); p = realloc(NULL, 1); tassert(p != NULL); p[0] = 123; free(p); p = realloc(NULL, 0); free(p); } static void test_free(void) { free(NULL); } static void test_fprintf(void) { uint64_t ui64; int64_t i64; FILE f; f.hfile = INVALID_HANDLE_VALUE; f.buff = NULL; f.error = 0; f.is_unbuffered = 0; f.is_console = 0; f.special_init = 0; tsoftassert(fprintf(&f, "xy") == 2); tassert(f.buff != NULL); tassert(f.buffend == f.buff+BUFSIZ); tassert(f.buffp == f.buff+2); tsoftassert(f.buff[0] == 'x'); tsoftassert(f.buff[1] == 'y'); tsoftassert(fprintf(&f, "[%s,%s]", "abc", "ABC") == 9); tassert(f.buffp == f.buff+11); tsoftassert(!memcmp(f.buff, "xy[abc,ABC]", 11)); /* Test of %s and width (%0...s is actually undefined) */ f.buffp = f.buff; /* reset */ tsoftassert(fprintf(&f, "[%12s,%013s,%*s,%0*s,%0*s]", "abc", "ABC", 4, "xy", 3, "A", 3, "") == 41); tassert(f.buffp == f.buff+41); tsoftassert(!memcmp(f.buff, "[ abc,0000000000ABC, xy,00A,000]", 41)); /* Test of %s and precision and width */ f.buffp = f.buff; /* reset */ tsoftassert(fprintf(&f, "[%12.2s,%2.1s,%.*s,%*.*s,%0*.2s]", "abc", "ABC", 2, "pqrs", 3, 2, "ABC", 3, "DEF") == 28); tassert(f.buffp == f.buff+28); tsoftassert(!memcmp(f.buff, "[ ab, A,pq, AB, DE]", 28)); /* Test of %d and width */ f.buffp = f.buff; /* reset */ tsoftassert(fprintf(&f, "[%d,%12d,%013d,%*d,%0*d,%0*d]", 0, 34567, 9080706, 4, 50, 3, 1, 3, 0) == 43); tassert(f.buffp == f.buff+43); tsoftassert(!memcmp(f.buff, "[0, 34567,0000009080706, 50,001,000]", 43)); /* Test of %d and width - negative values */ f.buffp = f.buff; /* reset */ tsoftassert(fprintf(&f, "[%d,%12d,%013d,%*d,%0*d]", -9, -34567, -9080706, 4, -50, 3, -100) == 41); tassert(f.buffp == f.buff+41); tsoftassert(!memcmp(f.buff, "[-9, -34567,-000009080706, -50,-100]", 41)); /* Test of %d and %u - limits */ f.buffp = f.buff; /* reset */ tsoftassert(fprintf(&f, "[%d,%d,%u,%u]", -2147483647-1, 2147483647, 0U, 4294967295U) == 37); tassert(f.buffp == f.buff+37); tsoftassert(!memcmp(f.buff, "[-2147483648,2147483647,0,4294967295]", 37)); /* Test of %d and precision and width */ f.buffp = f.buff; /* reset */ tsoftassert(fprintf(&f, "[%.2d,%.2d,%2.3d,%2.3d,%*.3d,%.0d,%4.2d]", 0, 9, 0, 91, 4, 92, 0, -9) == 26); tassert(f.buffp == f.buff+26); tsoftassert(!memcmp(f.buff, "[00,09,000,091, 092,, -09]", 26)); /* Test of %ld, %lu, %lx, %lld, %llu and %llx - limits */ f.buffp = f.buff; /* reset */ ui64 = 0; ui64--; i64 = -2147483647-1; i64 *= 65536; i64 *= 65536; tsoftassert(fprintf(&f, "[%ld,%lu,%lx,%lld,%llu,%llx]", -2147483647L-1, 4294967295UL, 0xffffffffUL, i64, ui64, ui64) == 92); tassert(f.buffp == f.buff+92); tsoftassert(!memcmp(f.buff, "[-2147483648,4294967295,ffffffff," "-9223372036854775808,18446744073709551615,ffffffffffffffff]", 92)); /* Test of %i (alias of %d) */ f.buffp = f.buff; /* reset */ tsoftassert(fprintf(&f, "[%i,%i]", -2147483647-1, 2147483647) == 24); tassert(f.buffp == f.buff+24); tsoftassert(!memcmp(f.buff, "[-2147483648,2147483647]", 24)); /* Test of %d with positive sign */ f.buffp = f.buff; /* reset */ tsoftassert(fprintf(&f, "[% d,% d,% d,%+d,%+d,%+d,%+5.2d,%+5.2d,%+5.2d]", -1, 0, 1, -1, 0, 1, -1, 0, 1) == 37); tassert(f.buffp == f.buff+37); tsoftassert(!memcmp(f.buff, "[-1, 0, 1,-1,+0,+1, -01, +00, +01]", 37)); /* Test of %x */ f.buffp = f.buff; /* reset */ tsoftassert(fprintf(&f, "[%x,%3x,%3x,%.3x,%03x,%03x,%0*x,%2.3x,%#4x,%#2x]", 0xA9, 0xF, 0x0, 0x1, 0x2, 0x0, 3, 0xf3, 0x9, 0x3, 0x0) == 40); tassert(f.buffp == f.buff+40); tsoftassert(!memcmp(f.buff, "[a9, f, 0,001,002,000,0f3,009, 0x3, 0]", 40)); /* Test of %p (this test is not standards-conforming C code) */ f.buffp = f.buff; /* reset */ tsoftassert(fprintf(&f, "[%p,%.10p,%06p,%2p,%3p,%*p]", (void *)0x123, (void *)0xabcdef, (void *)0x999, (void *)0x111, (void *)0, 6, (void *)0x567) == 42); tassert(f.buffp == f.buff+42); tsoftassert(!memcmp(f.buff, "[0x123,0x00abcdef,0x0999,0x111, 0, 0x567]", 42)); /* Test of %c and %% */ f.buffp = f.buff; /* reset */ tsoftassert(fprintf(&f, "[%c,%c,%c,%3c,%%]", 65, 0, 0xFF, 66) == 13); tassert(f.buffp == f.buff+13); tsoftassert(!memcmp(f.buff, "[A,\0,\xFF, B,%]", 13)); /* Test of left justification */ f.buffp = f.buff; /* reset */ tsoftassert(fprintf(&f, "[%-2c,%-2d,%-2d,%-3.2d,%-4.2d,%-3x,%-3s]", 65, 0, 1, 2, -2, 0xF, "ab") == 27); tassert(f.buffp == f.buff+27); tsoftassert(!memcmp(f.buff, "[A ,0 ,1 ,02 ,-02 ,f ,ab ]", 27)); } static void test_sprintf(void) { char buff[100]; buff[7] = 'X'; tassert(sprintf(buff, "A%c\n%s\n", 66, "CD") == 6); tsoftassert(!memcmp(buff, "AB\nCD\n", 7)); tsoftassert(buff[7] == 'X'); /* should not be overwritten */ } static void test_extract_appdir(void) { char *tmp; int eq; tsoftassert(extract_appdir("","Bin",NULL) == NULL); tsoftassert(extract_appdir("a.exe","Bin",NULL) == NULL); tsoftassert(extract_appdir("C:\\a.exe","Bin",NULL) == NULL); tsoftassert(extract_appdir("C:\\Xyz\\a.exe","Bin",NULL) == NULL); tsoftassert(extract_appdir("C:\\Xyz\\a.exe","Bin","SubDir") == NULL); tsoftassert(EQ_F(extract_appdir("C:\\Xyz\\biN\\a.exe","Bin",NULL), "C:\\Xyz\\")); tsoftassert(EQ_F(extract_appdir("C:\\Xyz\\biN\\a.exe","Bin","SubDir"), "C:\\Xyz\\SubDir")); tsoftassert(EQ_F(extract_appdir("C:/Xyz/biN/a.exe","Bin",NULL), "C:/Xyz/")); tsoftassert(EQ_F(extract_appdir("C:/Xyz/biN/a.exe","Bin","SubDir"), "C:/Xyz/SubDir")); } static void test_cmdline_parsing(void) { static const WCHAR cmdl_none[] = { '\0' }; static const WCHAR cmdl_exeonly[] = { 'a','.','e','x','e','\0' }; static const WCHAR cmdl_args[] = { 'a','.','e','x','e','\t','a','b',' ',' ','1','\0' }; static const WCHAR cmdl_quoted[] = { '"','a','.','e','x','e','"',' ','"','t','"',' ','"','"',' ','"','\\','\\','\\','"','"','\0' }; static const WCHAR cmdl_unicode[] = { 0x00C0, 0x1C80, 0xE000, 0xD83D,0xDE00,'.','e','x','e','\0' }; static const WCHAR cmdl_trailing1[] = { 'a','.','e','x','e',' ','\0' }; static const WCHAR cmdl_trailing2[] = { 'a','.','e','x','e',' ','"','"','\0' }; int argc; char **argv; static char buff[100]; /* Empty cmdline (should not happen) */ tassert(parse_cmdline(cmdl_none, &argc, NULL, NULL) == 2*PTRSIZE+1); tassert(parse_cmdline(cmdl_none, &argc, &argv, buff) == 2*PTRSIZE+1); tassert(argc == 1); tassert(argv[0][0] == '\0'); tassert(argv[1] == NULL); /* Basic cmdline, with just an EXE */ tassert(parse_cmdline(cmdl_exeonly, &argc, NULL, NULL) == 2*PTRSIZE+6); tassert(parse_cmdline(cmdl_exeonly, &argc, &argv, buff) == 2*PTRSIZE+6); tassert(argc == 1); tassert(!strcmp(argv[0], "a.exe")); tassert(argv[1] == NULL); /* cmdline with 2 unquoted args */ tassert(parse_cmdline(cmdl_args, &argc, NULL, NULL) == 4*PTRSIZE+11); tassert(parse_cmdline(cmdl_args, &argc, &argv, buff) == 4*PTRSIZE+11); tassert(argc == 3); tassert(!strcmp(argv[0], "a.exe")); tassert(!strcmp(argv[1], "ab")); tassert(!strcmp(argv[2], "1")); tassert(argv[3] == NULL); /* cmdline with quoted args */ tassert(parse_cmdline(cmdl_quoted, &argc, NULL, NULL) == 5*PTRSIZE+12); tassert(parse_cmdline(cmdl_quoted, &argc, &argv, buff) == 5*PTRSIZE+12); tassert(argc == 4); tassert(!strcmp(argv[0], "a.exe")); tassert(!strcmp(argv[1], "t")); tassert(!strcmp(argv[2], "")); tassert(!strcmp(argv[3], "\\\"")); /* = \" */ tassert(argv[4] == NULL); /* Unicode characters (BMP: 2 byte and 2x 3 byte UTF-8, Non-BMP: 4 byte UTF-8) */ tsoftassert(parse_cmdline(cmdl_unicode, &argc, NULL, NULL) == 2*PTRSIZE+17); tsoftassert(parse_cmdline(cmdl_unicode, &argc, &argv, buff) == 2*PTRSIZE+17); tassert(argc == 1); tassert(!strcmp(argv[0], "\xC3\x80" "\xE1\xB2\x80" "\xEE\x80\x80" "\xF0\x9F\x98\x80" ".exe")); tassert(argv[1] == NULL); /* Trailing spaces (inserted by old versions of MingW32) */ tassert(parse_cmdline(cmdl_trailing1, &argc, NULL, NULL) == 2*PTRSIZE+6); tassert(parse_cmdline(cmdl_trailing1, &argc, &argv, buff) == 2*PTRSIZE+6); tassert(argc == 1); tassert(!strcmp(argv[0], "a.exe")); tassert(argv[1] == NULL); /* Trailing spaces (inserted by old versions of MingW32) */ tassert(parse_cmdline(cmdl_trailing2, &argc, NULL, NULL) == 3*PTRSIZE+7); tassert(parse_cmdline(cmdl_trailing2, &argc, &argv, buff) == 3*PTRSIZE+7); tassert(argc == 2); tassert(!strcmp(argv[0], "a.exe")); tassert(!strcmp(argv[1], "")); /* Empty argument */ tassert(argv[2] == NULL); } const TestFunctionInfo tests_winlibc[] = { TEST_INFO(test_memcpy) TEST_INFO(test_memset) TEST_INFO(test_memcmp) TEST_INFO(test_memchr) TEST_INFO(test_memrchr) TEST_INFO(test_strchr) TEST_INFO(test_strrchr) TEST_INFO(test_strcmp) TEST_INFO(test_strncmp) TEST_INFO(test_strlen) TEST_INFO(test_strnlen) TEST_INFO(test_strcspn) TEST_INFO(test_strspn) TEST_INFO(test_strdup) TEST_INFO(test_strndup) TEST_INFO(test_malloc) TEST_INFO(test_realloc) TEST_INFO(test_free) TEST_INFO(test_fprintf) TEST_INFO(test_sprintf) TEST_INFO(test_extract_appdir) TEST_INFO(test_cmdline_parsing) TEST_END };