#include "sashidown.h" #include #include #include #define CHECK(c) \ do { \ if (!(c)) { \ fprintf(stderr, "%s:%d: CHECK(%s) failed\n", __FILE__, __LINE__, #c); \ exit(1); \ } \ } while (0) /* SASHIDOWN_FMT_ANSI_PLAIN theme */ #define ITALIC "\033[3m" #define ITALIC_OFF "\033[23m" #define BOLD "\033[1m" #define BOLD_OFF "\033[22m" #define CODE "\033[7m" static char out[4096], plain[4096]; /* Renders md; `out` keeps the escapes, `plain` has them stripped. */ static void render(const char *md) { size_t n = 0; CHECK(sashidown_convert(md, strlen(md), out, sizeof(out) - 1, &n, SASHIDOWN_FMT_ANSI_PLAIN) == 0); out[n] = 0; size_t o = 0; for (size_t i = 0; i < n;) { if (out[i] == '\033' && out[i + 1] == '[') { i += 2; while (i < n && !(out[i] >= 0x40 && out[i] <= 0x7e)) i++; i++; continue; } plain[o++] = out[i++]; } plain[o] = 0; } static int has(const char *s, const char *needle) { return strstr(s, needle) != NULL; } static void test_intraword(void) { render("see test_apply_patch.c now"); CHECK(has(plain, "test_apply_patch.c")); CHECK(!has(out, ITALIC)); render("snake_case_x and 中文_变量_名 and a__b__c"); CHECK(has(plain, "snake_case_x and 中文_变量_名 and a__b__c")); CHECK(!has(out, ITALIC) && !has(out, BOLD)); render("foo_bar_ baz"); CHECK(has(plain, "foo_bar_ baz") && !has(out, ITALIC)); } static void test_pairs(void) { render("*it* and _it_"); CHECK(has(out, ITALIC "it" ITALIC_OFF " and " ITALIC "it" ITALIC_OFF)); render("**b** __b__"); CHECK(has(out, BOLD "b" BOLD_OFF " " BOLD "b" BOLD_OFF)); render("***x***"); CHECK(has(out, BOLD ITALIC "x" ITALIC_OFF BOLD_OFF)); render("*a_b* end"); CHECK(has(out, ITALIC "a_b" ITALIC_OFF " end")); /* CommonMark: intraword '*' pairs, a standalone __init__ is bold */ render("2*3*4"); CHECK(has(out, "2" ITALIC "3" ITALIC_OFF "4")); render("call __init__ now"); CHECK(has(out, BOLD "init" BOLD_OFF " now")); } static void test_unmatched(void) { render("a * b and 2 * 3 * 4"); CHECK(has(plain, "a * b and 2 * 3 * 4") && !has(out, ITALIC)); render("trailing star*"); CHECK(has(plain, "trailing star*") && !has(out, ITALIC)); render("*mix_ words"); CHECK(has(plain, "*mix_ words") && !has(out, ITALIC)); render("**open bold only"); CHECK(has(plain, "**open bold only") && !has(out, BOLD)); render("a `b"); CHECK(has(plain, "a `b") && !has(out, CODE)); } static void test_code(void) { render("run `a_b_c` and `*x*`"); CHECK(has(plain, "run a_b_c and *x*")); CHECK(has(out, CODE) && !has(out, ITALIC)); } static void test_cjk(void) { render("**粗体**,后文"); CHECK(has(out, BOLD "粗体" BOLD_OFF ",后文")); render("看**“引号”**这里"); CHECK(has(out, BOLD "“引号”" BOLD_OFF "这里")); } int main(void) { test_intraword(); test_pairs(); test_unmatched(); test_code(); test_cjk(); printf("test_markdown: ok\n"); return 0; }