/* * vlibc — ctype.h classification + case mapping test (todo 9). * * Coverage: * * 1. classification sweep: every is* function, for all 256 unsigned char * values AND EOF, must agree with the "C" locale ASCII ranges the * standard specifies — the golden model below derives its expectation * straight from those ranges, independent of the implementation's * table; * 2. case-mapping sweep: tolower/toupper over all 256 values + EOF (map * A-Z/a-z, pass every non-letter through unchanged, EOF -> EOF); * 3. negative-char sweep: every negative signed-char value passed as int * must classify false in all is* functions, case-map to itself, and * never fault — proving the unsigned char cast keeps negative values * from indexing out of bounds (isalpha((char)0xe9) == 0 included); * 4. spot checks from the todo acceptance list; * 5. -f: the negative QA — isprint('\n') == 0, isalpha(EOF) == 0, * tolower(EOF)/toupper(EOF) == EOF (a nonzero isprint('\n') is the * defect). * * No host headers: is vlibc's own (-Iinclude shadows the system * one), and every diagnostic goes through the raw SYS_write helpers from * . Compiled with -DVLIBC_LEVEL=2 so both the L1 * functions and the L2 isascii/toascii gate are exercised (mirrors * libvlibc-check.a, which builds the level-2 surface). * * Not part of the library proper; compiled manually for this todo (the * tests/ + make check wiring is owned by a later todo). */ #include #include /* EOF as the standard defines it; ctype.h deliberately does not provide it. */ #define TEST_EOF (-1) static int failures; /* ---- Golden model: the "C" locale classes, as the standard defines them ---- */ enum { CLS_ALNUM, CLS_ALPHA, CLS_BLANK, CLS_CNTRL, CLS_DIGIT, CLS_GRAPH, CLS_LOWER, CLS_PRINT, CLS_PUNCT, CLS_SPACE, CLS_UPPER, CLS_XDIGIT }; typedef int (*ctype_pred)(int); /* The real functions under test, indexed by the CLS_* enum above. */ static const ctype_pred cls_preds[] = {isalnum, isalpha, isblank, iscntrl, isdigit, isgraph, islower, isprint, ispunct, isspace, isupper, isxdigit}; static const char *const cls_names[] = {"isalnum", "isalpha", "isblank", "iscntrl", "isdigit", "isgraph", "islower", "isprint", "ispunct", "isspace", "isupper", "isxdigit"}; static int expect_class(int c, int cls) // NOLINT(bugprone-easily-swappable-parameters) { if (c < 0 || c > 127) { return 0; } // NOLINTBEGIN(bugprone-switch-missing-default-case) switch (cls) { case CLS_UPPER: return c >= 'A' && c <= 'Z'; case CLS_LOWER: return c >= 'a' && c <= 'z'; case CLS_DIGIT: return c >= '0' && c <= '9'; case CLS_ALPHA: return expect_class(c, CLS_UPPER) || expect_class(c, CLS_LOWER); case CLS_ALNUM: return expect_class(c, CLS_ALPHA) || expect_class(c, CLS_DIGIT); case CLS_XDIGIT: return expect_class(c, CLS_DIGIT) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); case CLS_CNTRL: return (c >= 0x00 && c <= 0x1f) || c == 0x7f; case CLS_BLANK: return c == ' ' || c == '\t'; case CLS_SPACE: return c == ' ' || (c >= '\t' && c <= '\r'); case CLS_PUNCT: return (c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~'); case CLS_GRAPH: return expect_class(c, CLS_ALNUM) || expect_class(c, CLS_PUNCT); case CLS_PRINT: return expect_class(c, CLS_GRAPH) || c == ' '; } // NOLINTEND(bugprone-switch-missing-default-case) return 0; } static int expect_tolower(int c) { return c >= 'A' && c <= 'Z' ? c + ('a' - 'A') : c; } static int expect_toupper(int c) { return c >= 'a' && c <= 'z' ? c - ('a' - 'A') : c; } /* ---- Sweeps ---- */ /* 1. All 256 byte values + EOF against the golden model, per class. */ static void classification_sweep(void) { size_t k; for (k = 0; k < sizeof(cls_preds) / sizeof(cls_preds[0]); k++) { int c; for (c = 0; c < 256; c++) { if ((cls_preds[k](c) != 0) == (expect_class(c, (int)k) != 0)) { continue; } vlibc_test_say(2, "mismatch: "); vlibc_test_say(2, cls_names[k]); vlibc_test_say(2, "(0x"); vlibc_test_say_hex(2, (unsigned long long)c); vlibc_test_say(2, ")\n"); failures++; break; } if (cls_preds[k](TEST_EOF) != 0) { vlibc_test_say(2, "mismatch: "); vlibc_test_say(2, cls_names[k]); vlibc_test_say(2, "(EOF)\n"); failures++; } } } /* 2. Case mapping over all 256 byte values + EOF. */ static void case_mapping_sweep(void) { int c; for (c = 0; c < 256; c++) { if (tolower(c) != expect_tolower(c)) { vlibc_test_say(2, "tolower mismatch at 0x"); vlibc_test_say_hex(2, (unsigned long long)c); vlibc_test_say(2, "\n"); failures++; } if (toupper(c) != expect_toupper(c)) { vlibc_test_say(2, "toupper mismatch at 0x"); vlibc_test_say_hex(2, (unsigned long long)c); vlibc_test_say(2, "\n"); failures++; } } if (tolower(TEST_EOF) != TEST_EOF || toupper(TEST_EOF) != TEST_EOF) { vlibc_test_say(2, "case mapping of EOF did not return EOF unchanged\n"); failures++; } } /* 3. Every negative signed-char value: classify false, case-map to itself. */ static void negative_char_sweep(void) { signed char sc; size_t k; for (sc = (signed char)-128; (int)sc < 0; sc++) { int c = (int)sc; for (k = 0; k < sizeof(cls_preds) / sizeof(cls_preds[0]); k++) { if (cls_preds[k](c) != 0) { vlibc_test_say(2, "negative value classified nonzero by "); vlibc_test_say(2, cls_names[k]); vlibc_test_say(2, "\n"); failures++; } } if (tolower(c) != c || toupper(c) != c) { vlibc_test_say(2, "negative value case-mapped instead of passing through\n"); failures++; } #if VLIBC_LEVEL_GE(2) if (isascii(c) != 0) { vlibc_test_say(2, "negative value classified ASCII by isascii\n"); failures++; } if (toascii(c) != (c & 0x7f)) { vlibc_test_say(2, "toascii mismatch on a negative value\n"); failures++; } #endif /* VLIBC_LEVEL_GE(2) */ } } /* 4. Acceptance-list spot checks. */ static void spot_checks(void) { TEST_ASSERT_EQ(tolower('A'), 'a'); TEST_ASSERT_EQ(toupper('a'), 'A'); TEST_ASSERT_EQ(tolower('a'), 'a'); /* lowercase identity */ TEST_ASSERT_EQ(toupper('Z'), 'Z'); /* uppercase identity */ TEST_ASSERT_EQ(tolower('Z'), 'z'); TEST_ASSERT_EQ(toupper('z'), 'Z'); TEST_ASSERT_EQ(tolower('0'), '0'); /* digit identity */ TEST_ASSERT_TRUE(isupper('A')); TEST_ASSERT_TRUE(islower('a')); TEST_ASSERT_TRUE(isalpha(TEST_EOF) == 0); TEST_ASSERT_TRUE(isprint('\n') == 0); /* negative QA: no false positive */ TEST_ASSERT_TRUE(isdigit('5')); TEST_ASSERT_TRUE(!isalpha('5')); TEST_ASSERT_TRUE(isspace(' ')); TEST_ASSERT_TRUE(isspace('\n')); TEST_ASSERT_TRUE(ispunct('!')); TEST_ASSERT_TRUE(!ispunct('A')); TEST_ASSERT_TRUE(isgraph('~')); TEST_ASSERT_TRUE(!isgraph(' ')); TEST_ASSERT_TRUE(isprint(' ')); TEST_ASSERT_TRUE(isblank('\t')); TEST_ASSERT_TRUE(isblank(' ')); TEST_ASSERT_TRUE(!isblank('x')); TEST_ASSERT_TRUE(iscntrl(0x7f)); TEST_ASSERT_TRUE(!iscntrl(' ')); TEST_ASSERT_TRUE(isxdigit('f')); TEST_ASSERT_TRUE(isxdigit('F')); TEST_ASSERT_TRUE(!isxdigit('g')); TEST_ASSERT_TRUE(!isalnum('@')); TEST_ASSERT_TRUE(isalnum('A')); TEST_ASSERT_TRUE(isalpha((char)0xe9) == 0); /* negative char: (char)0xe9 == -23 */ #if VLIBC_LEVEL_GE(2) TEST_ASSERT_TRUE(!isascii(0x80)); TEST_ASSERT_TRUE(isascii(0x00)); TEST_ASSERT_TRUE(isascii(0x7f)); TEST_ASSERT_TRUE(!isascii(TEST_EOF)); TEST_ASSERT_EQ(toascii('A'), 'A'); /* c & 0x7f, not a predicate */ TEST_ASSERT_EQ(toascii(0x80), 0); TEST_ASSERT_EQ(toascii(TEST_EOF), 0x7f); #endif /* VLIBC_LEVEL_GE(2) */ } /* * Failure scenario (-f): the negative QA from the todo. Exits 0 only when * each defect is absent. */ static int failure_scenario(void) { if (isprint('\n') != 0) { vlibc_test_say(1, "isprint('\\n') != 0 (false positive)\n"); return 1; } if (isalpha(TEST_EOF) != 0) { vlibc_test_say(1, "isalpha(EOF) != 0\n"); return 1; } if (tolower(TEST_EOF) != TEST_EOF || toupper(TEST_EOF) != TEST_EOF) { vlibc_test_say(1, "tolower/toupper(EOF) did not return EOF unchanged\n"); return 1; } vlibc_test_say(1, "negative QA: isprint('\\n') == 0, isalpha(EOF) == 0, " "tolower/toupper(EOF) == EOF\n"); return 0; } int main(int argc, char **argv) { if (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'f') { return failure_scenario(); } classification_sweep(); case_mapping_sweep(); negative_char_sweep(); spot_checks(); if (failures > 0 || vlibc_test_failures > 0) { vlibc_test_say(2, "FAILED ("); // NOLINTNEXTLINE(bugprone-misplaced-widening-cast) vlibc_test_say_dec(2, (unsigned long)(failures + vlibc_test_failures)); vlibc_test_say(2, " check(s))\n"); return 1; } vlibc_test_say(1, "all ctype tests passed\n"); return 0; }