style: 应用 clang-format
This commit is contained in:
1 parent
016a45bc6e
commit
d1612f1d31
65 files changed
+7288
-5983
No files matched your search
+158
-133
@@ -1,7 +1,7 @@
|
||||
/* test_markdown.c - 流式 Markdown 渲染器测试
|
||||
*
|
||||
* 最关键的不变量: 把同一段文本任意切分后逐片喂入, 输出必须与一次性喂入
|
||||
* 完全一致. SSE 的分片位置是随机的, 这条不成立就会漏出"半个星号".
|
||||
* 完全一致. SSE 的分片位置是随机的, 这条不成立就会漏出"半个星号".
|
||||
*/
|
||||
#include "markdown.h"
|
||||
#include "util.h"
|
||||
@@ -13,27 +13,28 @@
|
||||
static int failures = 0;
|
||||
static int checks = 0;
|
||||
|
||||
#define CHECK(cond, ...) \
|
||||
do { \
|
||||
checks++; \
|
||||
if (!(cond)) { \
|
||||
failures++; \
|
||||
printf(" ✗ %s:%d: ", __FILE__, __LINE__); \
|
||||
printf(__VA_ARGS__); \
|
||||
printf("\n"); \
|
||||
} \
|
||||
#define CHECK(cond, ...) \
|
||||
do { \
|
||||
checks++; \
|
||||
if (!(cond)) { \
|
||||
failures++; \
|
||||
printf(" ✗ %s:%d: ", __FILE__, __LINE__); \
|
||||
printf(__VA_ARGS__); \
|
||||
printf("\n"); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* 去掉 ANSI 转义, 得到"肉眼可见"的文本 */
|
||||
static char *strip_ansi(const char *s)
|
||||
{
|
||||
static char *strip_ansi(const char *s) {
|
||||
StrBuf out;
|
||||
sb_init(&out);
|
||||
for (const char *p = s; *p; ) {
|
||||
for (const char *p = s; *p;) {
|
||||
if (*p == 0x1b && p[1] == '[') {
|
||||
p += 2;
|
||||
while (*p && !(*p >= '@' && *p <= '~')) p++;
|
||||
if (*p) p++;
|
||||
while (*p && !(*p >= '@' && *p <= '~'))
|
||||
p++;
|
||||
if (*p)
|
||||
p++;
|
||||
continue;
|
||||
}
|
||||
sb_putc(&out, *p++);
|
||||
@@ -41,22 +42,24 @@ static char *strip_ansi(const char *s)
|
||||
return sb_detach(&out);
|
||||
}
|
||||
|
||||
static void show_escaped(const char *s)
|
||||
{
|
||||
static void show_escaped(const char *s) {
|
||||
for (const unsigned char *p = (const unsigned char *)s; *p; p++) {
|
||||
if (*p == 0x1b) printf("<ESC>");
|
||||
else if (*p == '\n') printf("\\n");
|
||||
else putchar(*p);
|
||||
if (*p == 0x1b)
|
||||
printf("<ESC>");
|
||||
else if (*p == '\n')
|
||||
printf("\\n");
|
||||
else
|
||||
putchar(*p);
|
||||
}
|
||||
}
|
||||
|
||||
/* 渲染到内存流; chunk == 0 表示一次性喂入 */
|
||||
static char *do_render(const char *input, int enabled, size_t chunk)
|
||||
{
|
||||
static char *do_render(const char *input, int enabled, size_t chunk) {
|
||||
char *buf = NULL;
|
||||
size_t size = 0;
|
||||
FILE *f = open_memstream(&buf, &size);
|
||||
if (!f) return NULL;
|
||||
if (!f)
|
||||
return NULL;
|
||||
MdRenderer *r = md_new(enabled);
|
||||
md_set_out(r, f);
|
||||
|
||||
@@ -67,7 +70,8 @@ static char *do_render(const char *input, int enabled, size_t chunk)
|
||||
for (size_t i = 0; i < len; i += chunk) {
|
||||
size_t n = len - i < chunk ? len - i : chunk;
|
||||
char tmp[64];
|
||||
if (n >= sizeof tmp) n = sizeof tmp - 1;
|
||||
if (n >= sizeof tmp)
|
||||
n = sizeof tmp - 1;
|
||||
memcpy(tmp, input + i, n);
|
||||
tmp[n] = '\0';
|
||||
md_feed(r, tmp);
|
||||
@@ -79,15 +83,13 @@ static char *do_render(const char *input, int enabled, size_t chunk)
|
||||
return buf;
|
||||
}
|
||||
|
||||
static char *render(const char *in, int enabled)
|
||||
{
|
||||
static char *render(const char *in, int enabled) {
|
||||
return do_render(in, enabled, 0);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static void test_split_invariance(void)
|
||||
{
|
||||
static void test_split_invariance(void) {
|
||||
puts("分片不变性 (一次性 / 3 字节 / 逐字节 必须一致)");
|
||||
static const char *samples[] = {
|
||||
"**粗体**\n",
|
||||
@@ -121,36 +123,43 @@ static void test_split_invariance(void)
|
||||
"```\n# 不是标题\n[x](y)\n```\n",
|
||||
"ls -la | grep foo\n",
|
||||
"a | b\nc | d\n",
|
||||
/* 以下三个是模糊测试找到并最小化出来的历史缺陷,
|
||||
/* 以下三个是模糊测试找到并最小化出来的历史缺陷,
|
||||
* 每个都曾让"一次性喂"与"分片喂"结果不一致, 留作回归见证 */
|
||||
"1|`", /* 表格探测挂起整行 -> 行内代码被漏渲染 */
|
||||
"- |", /* try_block 提前 return 跳过了列表规则 */
|
||||
"s|\n-", /* 表格判断依赖分片边界 */
|
||||
"| a |\n|---|\n", /* 表头+分隔行后紧跟 EOF */
|
||||
"| a |\n|---|", /* 末尾无换行的表格 */
|
||||
"| a | b |\n|---|---|\n| 1 | 2 |\n- 列表\n", /* 表格后紧跟列表 */
|
||||
"| a |\n| b |\n| c |\n", /* 有续行但缺分隔行 */
|
||||
"1|`", /* 表格探测挂起整行 -> 行内代码被漏渲染 */
|
||||
"- |", /* try_block 提前 return 跳过了列表规则 */
|
||||
"s|\n-", /* 表格判断依赖分片边界 */
|
||||
"| a |\n|---|\n", /* 表头+分隔行后紧跟 EOF */
|
||||
"| a |\n|---|", /* 末尾无换行的表格 */
|
||||
"| a | b |\n|---|---|\n| 1 | 2 |\n- 列表\n", /* 表格后紧跟列表 */
|
||||
"| a |\n| b |\n| c |\n", /* 有续行但缺分隔行 */
|
||||
};
|
||||
for (size_t k = 0; k < sizeof samples / sizeof samples[0]; k++) {
|
||||
char *whole = render(samples[k], 1);
|
||||
char *by1 = do_render(samples[k], 1, 1);
|
||||
char *by3 = do_render(samples[k], 1, 3);
|
||||
int ok = whole && by1 && by3 &&
|
||||
strcmp(whole, by1) == 0 && strcmp(whole, by3) == 0;
|
||||
int ok = whole && by1 && by3 && strcmp(whole, by1) == 0 &&
|
||||
strcmp(whole, by3) == 0;
|
||||
checks++;
|
||||
if (!ok) {
|
||||
failures++;
|
||||
printf(" ✗ 分片不一致: [%s]\n", samples[k]);
|
||||
printf(" 一次性: "); show_escaped(whole ? whole : "(null)"); printf("\n");
|
||||
printf(" 逐字节: "); show_escaped(by1 ? by1 : "(null)"); printf("\n");
|
||||
printf(" 3 字节: "); show_escaped(by3 ? by3 : "(null)"); printf("\n");
|
||||
printf(" 一次性: ");
|
||||
show_escaped(whole ? whole : "(null)");
|
||||
printf("\n");
|
||||
printf(" 逐字节: ");
|
||||
show_escaped(by1 ? by1 : "(null)");
|
||||
printf("\n");
|
||||
printf(" 3 字节: ");
|
||||
show_escaped(by3 ? by3 : "(null)");
|
||||
printf("\n");
|
||||
}
|
||||
free(whole); free(by1); free(by3);
|
||||
free(whole);
|
||||
free(by1);
|
||||
free(by3);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_headings(void)
|
||||
{
|
||||
static void test_headings(void) {
|
||||
puts("标题");
|
||||
char *o = render("# 一\n## 二\n### 三\n#### 四\n", 1);
|
||||
CHECK(strstr(o, "\033[1;36m# 一\033[0m") != NULL, "H1 应为粗体青");
|
||||
@@ -160,7 +169,8 @@ static void test_headings(void)
|
||||
char *vis = strip_ansi(o);
|
||||
CHECK(strcmp(vis, "# 一\n## 二\n### 三\n#### 四\n") == 0,
|
||||
"可见文本应保留 # 标记: [%s]", vis);
|
||||
free(vis); free(o);
|
||||
free(vis);
|
||||
free(o);
|
||||
|
||||
/* 不是标题的情况必须原样保留 */
|
||||
o = render("#没有空格\n", 1);
|
||||
@@ -176,20 +186,21 @@ static void test_headings(void)
|
||||
free(o);
|
||||
}
|
||||
|
||||
static void test_bold_italic(void)
|
||||
{
|
||||
static void test_bold_italic(void) {
|
||||
puts("粗体 / 斜体");
|
||||
char *o = render("**粗**\n", 1);
|
||||
CHECK(strstr(o, "\033[1m粗\033[0m") != NULL, "粗体样式: [%s]", o);
|
||||
char *vis = strip_ansi(o);
|
||||
CHECK(strcmp(vis, "粗\n") == 0, "粗体标记应被吃掉: [%s]", vis);
|
||||
free(vis); free(o);
|
||||
free(vis);
|
||||
free(o);
|
||||
|
||||
o = render("*斜*\n", 1);
|
||||
CHECK(strstr(o, "\033[3m斜\033[0m") != NULL, "斜体样式: [%s]", o);
|
||||
vis = strip_ansi(o);
|
||||
CHECK(strcmp(vis, "斜\n") == 0, "斜体标记应被吃掉: [%s]", vis);
|
||||
free(vis); free(o);
|
||||
free(vis);
|
||||
free(o);
|
||||
|
||||
/* 字面星号: 乘法 */
|
||||
o = render("2 * 3 * 4\n", 1);
|
||||
@@ -210,43 +221,46 @@ static void test_bold_italic(void)
|
||||
o = render("**粗 *斜* 粗**\n", 1);
|
||||
vis = strip_ansi(o);
|
||||
CHECK(strcmp(vis, "粗 斜 粗\n") == 0, "嵌套可见文本: [%s]", vis);
|
||||
free(vis); free(o);
|
||||
free(vis);
|
||||
free(o);
|
||||
}
|
||||
|
||||
static void test_code(void)
|
||||
{
|
||||
static void test_code(void) {
|
||||
puts("代码");
|
||||
/* 行内代码: 保留反引号 (便于复制), 内容着色 */
|
||||
char *o = render("用 `ls -la` 看看\n", 1);
|
||||
char *vis = strip_ansi(o);
|
||||
CHECK(strcmp(vis, "用 `ls -la` 看看\n") == 0,
|
||||
"行内代码应保留反引号: [%s]", vis);
|
||||
CHECK(strcmp(vis, "用 `ls -la` 看看\n") == 0, "行内代码应保留反引号: [%s]",
|
||||
vis);
|
||||
CHECK(strstr(o, "\033[36m") != NULL, "内容应着色: [%s]", o);
|
||||
free(vis); free(o);
|
||||
free(vis);
|
||||
free(o);
|
||||
|
||||
/* 围栏代码块内部不做行内解析 */
|
||||
o = render("```\n**原样**\n```\n", 1);
|
||||
vis = strip_ansi(o);
|
||||
CHECK(strcmp(vis, "```\n**原样**\n```\n") == 0,
|
||||
"代码块内不应渲染粗体: [%s]", vis);
|
||||
free(vis); free(o);
|
||||
free(vis);
|
||||
free(o);
|
||||
|
||||
/* 带语言标识 */
|
||||
o = render("```c\nx;\n```\n", 1);
|
||||
vis = strip_ansi(o);
|
||||
CHECK(strcmp(vis, "```c\nx;\n```\n") == 0,
|
||||
"语言标识与内容应完整保留: [%s]", vis);
|
||||
free(vis); free(o);
|
||||
CHECK(strcmp(vis, "```c\nx;\n```\n") == 0, "语言标识与内容应完整保留: [%s]",
|
||||
vis);
|
||||
free(vis);
|
||||
free(o);
|
||||
|
||||
/* 未闭合的代码块: 不能吞掉后面的内容 */
|
||||
o = render("```\ncode\n", 1);
|
||||
vis = strip_ansi(o);
|
||||
CHECK(strcmp(vis, "```\ncode\n") == 0, "未闭合代码块: [%s]", vis);
|
||||
free(vis); free(o);
|
||||
free(vis);
|
||||
free(o);
|
||||
}
|
||||
|
||||
static void test_blocks(void)
|
||||
{
|
||||
static void test_blocks(void) {
|
||||
puts("列表 / 引用 / 分隔线");
|
||||
/* 列表标记必须保留模型输出的原始字符, 只上色, 不换成 • */
|
||||
char *o = render("- 甲\n- 乙\n", 1);
|
||||
@@ -254,32 +268,38 @@ static void test_blocks(void)
|
||||
CHECK(strcmp(vis, "- 甲\n- 乙\n") == 0, "列表应保留原始 - : [%s]", vis);
|
||||
CHECK(strstr(o, "\033[36m-\033[0m") != NULL, "列表标记 - 应着色: [%s]", o);
|
||||
CHECK(strstr(o, "•") == NULL, "不应出现非 ASCII 圆点: [%s]", o);
|
||||
free(vis); free(o);
|
||||
free(vis);
|
||||
free(o);
|
||||
|
||||
o = render("* 甲\n+ 乙\n", 1);
|
||||
vis = strip_ansi(o);
|
||||
CHECK(strcmp(vis, "* 甲\n+ 乙\n") == 0, "列表应保留原始 * 和 + : [%s]", vis);
|
||||
CHECK(strcmp(vis, "* 甲\n+ 乙\n") == 0, "列表应保留原始 * 和 + : [%s]",
|
||||
vis);
|
||||
CHECK(strstr(o, "\033[36m*\033[0m") != NULL, "列表标记 * 应着色: [%s]", o);
|
||||
CHECK(strstr(o, "\033[36m+\033[0m") != NULL, "列表标记 + 应着色: [%s]", o);
|
||||
free(vis); free(o);
|
||||
free(vis);
|
||||
free(o);
|
||||
|
||||
/* 表格不受影响: 仍然是框线字符 */
|
||||
o = render("| a |\n|---|\n| 1 |\n", 1);
|
||||
vis = strip_ansi(o);
|
||||
CHECK(strstr(vis, "┌") && strstr(vis, "│") && strstr(vis, "└"),
|
||||
"表格仍用框线字符: [%s]", vis);
|
||||
free(vis); free(o);
|
||||
free(vis);
|
||||
free(o);
|
||||
|
||||
o = render("> 引用\n", 1);
|
||||
vis = strip_ansi(o);
|
||||
CHECK(strcmp(vis, "│ 引用\n") == 0, "引用: [%s]", vis);
|
||||
free(vis); free(o);
|
||||
free(vis);
|
||||
free(o);
|
||||
|
||||
o = render("---\n", 1);
|
||||
vis = strip_ansi(o);
|
||||
CHECK(strcmp(vis, "---\n") == 0, "分隔线可见文本: [%s]", vis);
|
||||
CHECK(strstr(o, "\033[2m") != NULL, "分隔线应暗色: [%s]", o);
|
||||
free(vis); free(o);
|
||||
free(vis);
|
||||
free(o);
|
||||
|
||||
/* 非分隔线、非列表应原样 */
|
||||
o = render("--x\n", 1);
|
||||
@@ -287,8 +307,7 @@ static void test_blocks(void)
|
||||
free(o);
|
||||
}
|
||||
|
||||
static void test_passthrough(void)
|
||||
{
|
||||
static void test_passthrough(void) {
|
||||
puts("关闭渲染时逐字节透传");
|
||||
static const char *samples[] = {
|
||||
"# 标题 **粗体** `code`\n",
|
||||
@@ -300,20 +319,19 @@ static void test_passthrough(void)
|
||||
for (size_t k = 0; k < sizeof samples / sizeof samples[0]; k++) {
|
||||
char *whole = render(samples[k], 0);
|
||||
char *by1 = do_render(samples[k], 0, 1);
|
||||
CHECK(strcmp(whole, samples[k]) == 0,
|
||||
"应完全透传: 期望 [%s] 实际 [%s]", samples[k], whole);
|
||||
CHECK(strcmp(whole, samples[k]) == 0, "应完全透传: 期望 [%s] 实际 [%s]",
|
||||
samples[k], whole);
|
||||
CHECK(strcmp(by1, samples[k]) == 0, "分片透传也应一致");
|
||||
free(whole); free(by1);
|
||||
free(whole);
|
||||
free(by1);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_fence_verbatim(void)
|
||||
{
|
||||
static void test_fence_verbatim(void) {
|
||||
puts("围栏代码块: 全部不渲染");
|
||||
/* 纯代码块: 输出里不应出现任何 ANSI 转义 */
|
||||
char *o = render("```c\nint x = 1; // **不是粗体**\n```\n", 1);
|
||||
CHECK(strstr(o, "\033") == NULL,
|
||||
"代码块内不应有任何 ANSI 转义: [%s]", o);
|
||||
CHECK(strstr(o, "\033") == NULL, "代码块内不应有任何 ANSI 转义: [%s]", o);
|
||||
CHECK(strcmp(o, "```c\nint x = 1; // **不是粗体**\n```\n") == 0,
|
||||
"代码块应逐字节原样: [%s]", o);
|
||||
free(o);
|
||||
@@ -322,27 +340,26 @@ static void test_fence_verbatim(void)
|
||||
o = render("```\n# 不是标题\n- 不是列表\n> 不是引用\n[x](y)\n```\n", 1);
|
||||
CHECK(strstr(o, "\033") == NULL, "代码块内不应有转义: [%s]", o);
|
||||
CHECK(strstr(o, "# 不是标题") && strstr(o, "[x](y)") &&
|
||||
strstr(o, "- 不是列表") && strstr(o, "> 不是引用"),
|
||||
strstr(o, "- 不是列表") && strstr(o, "> 不是引用"),
|
||||
"代码块内容应完整: [%s]", o);
|
||||
free(o);
|
||||
|
||||
/* 代码块结束之后, 普通文本要恢复渲染 */
|
||||
o = render("```\nx\n```\n**加粗**\n", 1);
|
||||
CHECK(strstr(o, "\033[1m加粗\033[0m") != NULL,
|
||||
"代码块之后应恢复渲染: [%s]", o);
|
||||
CHECK(strstr(o, "\033[1m加粗\033[0m") != NULL, "代码块之后应恢复渲染: [%s]",
|
||||
o);
|
||||
free(o);
|
||||
}
|
||||
|
||||
static void test_inline_code_protected(void)
|
||||
{
|
||||
static void test_inline_code_protected(void) {
|
||||
puts("行内代码内容不受行内语法影响");
|
||||
char *o = render("形如 `a**b**c` 的写法\n", 1);
|
||||
CHECK(strstr(o, "\033[1m") == NULL,
|
||||
"行内代码内不应渲染粗体: [%s]", o);
|
||||
CHECK(strstr(o, "\033[1m") == NULL, "行内代码内不应渲染粗体: [%s]", o);
|
||||
char *vis = strip_ansi(o);
|
||||
CHECK(strcmp(vis, "形如 `a**b**c` 的写法\n") == 0,
|
||||
"行内代码可见文本应原样: [%s]", vis);
|
||||
free(vis); free(o);
|
||||
free(vis);
|
||||
free(o);
|
||||
|
||||
o = render("`[不是链接](x)` 和 `_不是斜体_`\n", 1);
|
||||
CHECK(strstr(o, "\033[94m") == NULL, "行内代码内不应高亮 []: [%s]", o);
|
||||
@@ -350,17 +367,18 @@ static void test_inline_code_protected(void)
|
||||
vis = strip_ansi(o);
|
||||
CHECK(strcmp(vis, "`[不是链接](x)` 和 `_不是斜体_`\n") == 0,
|
||||
"可见文本应原样: [%s]", vis);
|
||||
free(vis); free(o);
|
||||
free(vis);
|
||||
free(o);
|
||||
}
|
||||
|
||||
static void test_underscore_italic(void)
|
||||
{
|
||||
static void test_underscore_italic(void) {
|
||||
puts("_斜体_ 与词内下划线保护");
|
||||
char *o = render("这是 _斜体_ 文字\n", 1);
|
||||
CHECK(strstr(o, "\033[3m斜体\033[0m") != NULL, "_斜体_ 应渲染: [%s]", o);
|
||||
char *vis = strip_ansi(o);
|
||||
CHECK(strcmp(vis, "这是 斜体 文字\n") == 0, "可见文本: [%s]", vis);
|
||||
free(vis); free(o);
|
||||
free(vis);
|
||||
free(o);
|
||||
|
||||
/* 中文紧邻下划线也应生效 (非 ASCII 不算词字符) */
|
||||
o = render("中文_斜体_中文\n", 1);
|
||||
@@ -369,15 +387,14 @@ static void test_underscore_italic(void)
|
||||
|
||||
/* 关键保护: 下划线标识符不能被当成斜体 */
|
||||
o = render("foo_bar_baz 和 __init__ 和 a_b_c\n", 1);
|
||||
CHECK(strstr(o, "\033[3m") == NULL,
|
||||
"下划线标识符不应被渲染为斜体: [%s]", o);
|
||||
CHECK(strstr(o, "\033[3m") == NULL, "下划线标识符不应被渲染为斜体: [%s]",
|
||||
o);
|
||||
CHECK(strcmp(o, "foo_bar_baz 和 __init__ 和 a_b_c\n") == 0,
|
||||
"标识符应逐字节保留: [%s]", o);
|
||||
free(o);
|
||||
}
|
||||
|
||||
static void test_link_bracket(void)
|
||||
{
|
||||
static void test_link_bracket(void) {
|
||||
puts("链接: [] 下划线+青色高亮, 其余原样");
|
||||
char *o = render("见 [文档](https://example.com/a) 说明\n", 1);
|
||||
/* 这里是下划线 + 青 (SGR 4;36), 不是亮蓝. 整个项目只用 16 色范围内的
|
||||
@@ -387,13 +404,15 @@ static void test_link_bracket(void)
|
||||
char *vis = strip_ansi(o);
|
||||
CHECK(strcmp(vis, "见 [文档](https://example.com/a) 说明\n") == 0,
|
||||
"可见文本应逐字节保留 (含 URL): [%s]", vis);
|
||||
free(vis); free(o);
|
||||
free(vis);
|
||||
free(o);
|
||||
|
||||
/* 图片也是 [...], 同样只高亮方括号部分, 整段保留 */
|
||||
o = render("\n", 1);
|
||||
vis = strip_ansi(o);
|
||||
CHECK(strcmp(vis, "\n") == 0, "图片应保留原文: [%s]", vis);
|
||||
free(vis); free(o);
|
||||
free(vis);
|
||||
free(o);
|
||||
|
||||
/* 下标写法不应被当成链接 */
|
||||
o = render("arr[i] = br[j]\n", 1);
|
||||
@@ -402,73 +421,79 @@ static void test_link_bracket(void)
|
||||
free(o);
|
||||
}
|
||||
|
||||
static void test_tables(void)
|
||||
{
|
||||
static void test_tables(void) {
|
||||
puts("表格");
|
||||
/* ASCII 表格: 逐字节比对完整边框输出 */
|
||||
char *o = render("| a | b |\n|---|---|\n| 1 | 22 |\n", 1);
|
||||
char *vis = strip_ansi(o);
|
||||
const char *want =
|
||||
"┌───┬────┐\n"
|
||||
"│ a │ b │\n"
|
||||
"├───┼────┤\n"
|
||||
"│ 1 │ 22 │\n"
|
||||
"└───┴────┘\n";
|
||||
CHECK(strcmp(vis, want) == 0, "ASCII 表格:\n期望:\n%s\n实际:\n%s", want, vis);
|
||||
const char *want = "┌───┬────┐\n"
|
||||
"│ a │ b │\n"
|
||||
"├───┼────┤\n"
|
||||
"│ 1 │ 22 │\n"
|
||||
"└───┴────┘\n";
|
||||
CHECK(strcmp(vis, want) == 0, "ASCII 表格:\n期望:\n%s\n实际:\n%s", want,
|
||||
vis);
|
||||
CHECK(strstr(o, "\033[1m") != NULL, "表头应加粗: [%s]", o);
|
||||
free(vis); free(o);
|
||||
free(vis);
|
||||
free(o);
|
||||
|
||||
/* 中文列宽按 2 列计算, 边框必须对齐 */
|
||||
o = render("| 名称 | 值 |\n|---|---|\n| 苹果 | 3 |\n", 1);
|
||||
vis = strip_ansi(o);
|
||||
const char *want2 =
|
||||
"┌──────┬────┐\n"
|
||||
"│ 名称 │ 值 │\n"
|
||||
"├──────┼────┤\n"
|
||||
"│ 苹果 │ 3 │\n"
|
||||
"└──────┴────┘\n";
|
||||
CHECK(strcmp(vis, want2) == 0, "中文表格:\n期望:\n%s\n实际:\n%s", want2, vis);
|
||||
free(vis); free(o);
|
||||
const char *want2 = "┌──────┬────┐\n"
|
||||
"│ 名称 │ 值 │\n"
|
||||
"├──────┼────┤\n"
|
||||
"│ 苹果 │ 3 │\n"
|
||||
"└──────┴────┘\n";
|
||||
CHECK(strcmp(vis, want2) == 0, "中文表格:\n期望:\n%s\n实际:\n%s", want2,
|
||||
vis);
|
||||
free(vis);
|
||||
free(o);
|
||||
|
||||
/* 对齐标记 */
|
||||
o = render("| 左 | 中 | 右 |\n|:---|:---:|---:|\n| a | b | c |\n", 1);
|
||||
vis = strip_ansi(o);
|
||||
const char *want3 =
|
||||
"┌────┬────┬────┐\n"
|
||||
"│ 左 │ 中 │ 右 │\n"
|
||||
"├────┼────┼────┤\n"
|
||||
"│ a │ b │ c │\n"
|
||||
"└────┴────┴────┘\n";
|
||||
CHECK(strcmp(vis, want3) == 0, "对齐表格:\n期望:\n%s\n实际:\n%s", want3, vis);
|
||||
free(vis); free(o);
|
||||
const char *want3 = "┌────┬────┬────┐\n"
|
||||
"│ 左 │ 中 │ 右 │\n"
|
||||
"├────┼────┼────┤\n"
|
||||
"│ a │ b │ c │\n"
|
||||
"└────┴────┴────┘\n";
|
||||
CHECK(strcmp(vis, want3) == 0, "对齐表格:\n期望:\n%s\n实际:\n%s", want3,
|
||||
vis);
|
||||
free(vis);
|
||||
free(o);
|
||||
|
||||
/* 顶格必须写 '|'. 不带首竖线的写法按普通文本处理 ——
|
||||
* 这是为保证"输出与分片方式无关"而做的取舍:
|
||||
* 判断必须在行的第 0 个字节就能做出, 否则分片落在竖线之前时,
|
||||
* 这是为保证"输出与分片方式无关"而做的取舍:
|
||||
* 判断必须在行的第 0 个字节就能做出, 否则分片落在竖线之前时,
|
||||
* 行的前半段已被当作普通文本输出, 结果会随分片方式而变. */
|
||||
o = render("a | b\n--- | ---\n1 | 2\n", 1);
|
||||
vis = strip_ansi(o);
|
||||
CHECK(strcmp(vis, "a | b\n--- | ---\n1 | 2\n") == 0,
|
||||
"无首竖线应原样输出: [%s]", vis);
|
||||
free(vis); free(o);
|
||||
free(vis);
|
||||
free(o);
|
||||
|
||||
/* 首竖线前有缩进同理: 不认 */
|
||||
o = render(" | a |\n |---|\n", 1);
|
||||
vis = strip_ansi(o);
|
||||
CHECK(strstr(vis, "┌") == NULL, "缩进的表格不识别: [%s]", vis);
|
||||
free(vis); free(o);
|
||||
free(vis);
|
||||
free(o);
|
||||
|
||||
/* 误判防护: 普通文本里出现竖线 + 下一行是短横线, 不能当成表格 */
|
||||
o = render("运行 ls |\n-\n", 1);
|
||||
vis = strip_ansi(o);
|
||||
CHECK(strstr(vis, "┌") == NULL, "竖线+短横线不应误判成表格: [%s]", vis);
|
||||
free(vis); free(o);
|
||||
free(vis);
|
||||
free(o);
|
||||
|
||||
/* 单列极简表 */
|
||||
o = render("| x |\n|---|\n| 1 |\n", 1);
|
||||
vis = strip_ansi(o);
|
||||
CHECK(strstr(vis, "┌───┐") != NULL, "单列表格: [%s]", vis);
|
||||
free(vis); free(o);
|
||||
free(vis);
|
||||
free(o);
|
||||
|
||||
/* 没有分隔行就不是表格 */
|
||||
o = render("a | b\nc | d\n", 1);
|
||||
@@ -485,17 +510,18 @@ static void test_tables(void)
|
||||
vis = strip_ansi(o);
|
||||
CHECK(strstr(vis, "└───┴───┘\n后续段落 粗体\n") != NULL,
|
||||
"表格后应恢复普通渲染: [%s]", vis);
|
||||
free(vis); free(o);
|
||||
free(vis);
|
||||
free(o);
|
||||
}
|
||||
|
||||
static void test_long_line_bounded(void)
|
||||
{
|
||||
static void test_long_line_bounded(void) {
|
||||
puts("超长待处理缓冲应有界");
|
||||
/* 一个没有换行的超长 fence 信息行: 渲染器不能无限缓存 */
|
||||
StrBuf sb;
|
||||
sb_init(&sb);
|
||||
sb_puts(&sb, "```");
|
||||
for (int i = 0; i < 200000; i++) sb_putc(&sb, 'x');
|
||||
for (int i = 0; i < 200000; i++)
|
||||
sb_putc(&sb, 'x');
|
||||
char *o = render(sb.data, 1);
|
||||
size_t n = strlen(o);
|
||||
CHECK(n > 100000, "应该已经把内容吐出, 而不是无限缓存 (实际 %zu 字节)", n);
|
||||
@@ -503,8 +529,7 @@ static void test_long_line_bounded(void)
|
||||
sb_free(&sb);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int main(void) {
|
||||
test_split_invariance();
|
||||
test_headings();
|
||||
test_bold_italic();
|
||||
|
||||
Reference in new issue
Block a user