Files
ligent/tests/test_attach.c
T
2026-10-04 13:50:33 +08:00

199 lines
7.2 KiB
C

/* test_attach.c - /include 的暂存、glob 展开与体积/类型判断
*
* 交互式挑选 (attach_pick)要真终端才能测, 由 e2e 第 55 段覆盖;
* 这里测的是不需要人操作的那一半.
*/
#include "attach.h"
#include "image.h"
#include "json.h"
#include "util.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
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"); \
} \
} while (0)
static void write_file(const char *path, const void *data, size_t n) {
FILE *f = fopen(path, "wb");
if (!f) {
perror(path);
exit(2);
}
if (n && fwrite(data, 1, n, f) != n) {
perror("fwrite");
exit(2);
}
fclose(f);
}
static const unsigned char PNG[] = {0x89, 'P', 'N', 'G', 0x0D, 0x0A, 0x1A, 0x0A,
'p', 'a', 'y', 'l', 'o', 'a', 'd'};
/* 数一数暂存里某类块有几个 */
static size_t count_type(const JsonValue *parts, const char *type) {
if (!parts)
return 0;
size_t n = 0;
for (size_t i = 0; i < json_array_size((JsonValue *)parts); i++) {
const char *t =
json_get_string(json_array_get((JsonValue *)parts, i), "type");
if (t && strcmp(t, type) == 0)
n++;
}
return n;
}
/* 记一下回调被问了几次 —— 它只在遇到图片时该被问到.
* 答案和计数放在一起, 以免"计数版只实现了一种答案"这种测试自己的错. */
static int ask_count;
static int ask_answer;
static int counting_ask(void *ud) {
(void)ud;
ask_count++;
return ask_answer;
}
int main(void) {
char tmpl[] = "/tmp/ligent_attach_XXXXXX";
char *dir = mkdtemp(tmpl);
if (!dir) {
perror("mkdtemp");
return 2;
}
/* 备份目录也指到临时目录, 别污染仓库 */
image_set_bindir(dir);
char *f_c = xasprintf("%s/a.c", dir);
char *f_txt = xasprintf("%s/b.txt", dir);
char *f_bin = xasprintf("%s/bin.dat", dir);
char *f_png = xasprintf("%s/pic.png", dir);
char *f_big = xasprintf("%s/big.txt", dir);
write_file(f_c, "int main(void){return 0;}\n", 26);
write_file(f_txt, "hello\n", 6);
write_file(f_bin, "\x00\x01\x02binary", 9);
write_file(f_png, PNG, sizeof PNG);
{
/* 超过单个文件上限 (256 KB) */
size_t n = ATTACH_MAX_FILE + 1024;
char *big = xmalloc(n);
memset(big, 'x', n);
write_file(f_big, big, n);
free(big);
}
puts("glob 展开: 命中几个就暂存几个");
attach_clear();
size_t skipped = 0;
char err[256];
int added = attach_glob(f_c, NULL, NULL, &skipped, err, sizeof err);
CHECK(added == 1 && attach_count() == 1, "命中一个文件");
CHECK(skipped == 0, "没有跳过任何东西");
JsonValue *parts = attach_take_parts();
CHECK(parts && json_array_size(parts) == 1, "取到 1 个块");
JsonValue *p0 = parts ? json_array_get(parts, 0) : NULL;
CHECK(p0 && strcmp(json_get_string(p0, "type"), "text") == 0,
"文本文件应当是 text 块");
const char *txt = p0 ? json_get_string(p0, "text") : NULL;
CHECK(txt && strstr(txt, "int main") != NULL, "内容带上了");
CHECK(txt && strncmp(txt, "===== ", 6) == 0, "块开头标了来源路径");
CHECK(txt && strchr(txt, '\n') != NULL, "结尾有换行");
/* 只在本地记的来源, 绝不能进块里 (那会跟着发给服务端) */
CHECK(p0 && json_get_string(p0, "source") == NULL,
"块里不该有自造的 source 字段");
json_free(parts);
CHECK(attach_count() == 0, "取走之后暂存清空");
puts("取走即清空, clear 也是");
attach_glob(f_c, NULL, NULL, &skipped, err, sizeof err);
CHECK(attach_count() == 1, "再附带一次");
attach_clear();
CHECK(attach_count() == 0, "attach_clear 清空");
CHECK(attach_take_parts() == NULL, "清空之后取不到东西");
puts("二进制文件跳过");
attach_clear();
added = attach_glob(f_bin, NULL, NULL, &skipped, err, sizeof err);
CHECK(added == 0 && skipped == 1, "二进制被跳过 (added=%d skipped=%zu)",
added, skipped);
puts("超过单个文件上限的跳过");
attach_clear();
added = attach_glob(f_big, NULL, NULL, &skipped, err, sizeof err);
CHECK(added == 0 && skipped == 1, "太大的文本被跳过 (added=%d)", added);
puts("图片: 认内容不认扩展名, 并走备份引用");
attach_clear();
added = attach_glob(f_png, NULL, NULL, &skipped, err, sizeof err);
CHECK(added == 1, "png 被收下");
CHECK(attach_has_image(), "暂存里有图片");
parts = attach_take_parts();
CHECK(count_type(parts, "ligent_image") == 1,
"应当是一个 ligent_image 引用块");
json_free(parts);
puts("模型不收图时跳过图片 (回调只在遇到图片时被问)");
attach_clear();
ask_count = 0;
ask_answer = 1;
added = attach_glob(f_txt, counting_ask, NULL, &skipped, err, sizeof err);
CHECK(added == 1, "纯文本照带");
CHECK(ask_count == 0, "纯文本不该去问'能不能收图' (问了一次就是白探测)");
attach_clear();
ask_count = 0;
ask_answer = 0;
added = attach_glob(f_png, counting_ask, NULL, &skipped, err, sizeof err);
CHECK(added == 0 && skipped == 1, "回调说不行就跳过");
CHECK(ask_count == 1, "遇到图片时问了一次, 实际 %d", ask_count);
puts("匹配不到 / 参数非法");
attach_clear();
added = attach_glob("/tmp/绝对不存在的模式*.zzz", NULL, NULL, &skipped, err,
sizeof err);
CHECK(added == -1 && err[0], "匹配不到返回 -1 并给出原因: %s", err);
added = attach_glob(NULL, NULL, NULL, &skipped, err, sizeof err);
CHECK(added == -1, "NULL 模式返回 -1");
added = attach_glob("", NULL, NULL, &skipped, err, sizeof err);
CHECK(added == -1, "空模式返回 -1");
puts("多个匹配会累加");
attach_clear();
char *pat = xasprintf("%s/*.txt", dir);
added = attach_glob(pat, NULL, NULL, &skipped, err, sizeof err);
CHECK(added == 1, "只有 b.txt 能带 (big.txt 超限), 实际 %d", added);
CHECK(skipped == 1, "big.txt 被跳过");
free(pat);
attach_clear();
free(f_c);
free(f_txt);
free(f_bin);
free(f_png);
free(f_big);
image_set_bindir(".");
char *cmd = xasprintf("rm -rf %s", tmpl);
if (system(cmd) != 0) {
}
free(cmd);
printf("\n%d 项检查, %d 项失败\n", checks, failures);
return failures ? 1 : 0;
}