- 添加完整的参数解析API,支持子命令、选项和参数定义 - 实现多种数据类型支持:字符串、布尔值、整数、浮点数、枚举等 - 添加约束规范结构体,支持必填项、多值、隐藏选项等功能 - 实现国际化支持,包含中英文错误提示和帮助信息 - 添加模糊匹配功能,当用户输入错误参数时提供相似建议 - 实现详细的帮助信息打印功能,包括使用方法、选项说明等 - 修改底层optparse库,优化选项处理和错误报告机制 - 添加向量类型支持用于管理参数、选项和子命令集合
728 lines
23 KiB
C
728 lines
23 KiB
C
#include <optparse.h>
|
||
#include <utest/acutest.h>
|
||
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
|
||
// 测试用例配置
|
||
static scc_optparse_opt_t test_opts[] = {
|
||
SCC_OPTPARSE_OPT('-', 'h', "help", 0, 0),
|
||
SCC_OPTPARSE_OPT('-', 'h', "help", 0, 0),
|
||
SCC_OPTPARSE_OPT('-', 'v', "verbose", 0, 0),
|
||
SCC_OPTPARSE_OPT('-', 'f', "file", 1, 1),
|
||
SCC_OPTPARSE_OPT('-', 'o', "output", 1, 1),
|
||
SCC_OPTPARSE_OPT('-', 'l', "list", 0, 10),
|
||
SCC_OPTPARSE_OPT('-', 'c', "count", 1, 3),
|
||
SCC_OPTPARSE_OPT_END(),
|
||
};
|
||
|
||
#define INIT_OPTPARSE(argv) \
|
||
scc_optparse_init(&parser, sizeof(argv) / sizeof(argv[0]), argv); \
|
||
scc_optparse_set(&parser, test_opts);
|
||
|
||
// 测试1: 基础初始化
|
||
void test_init(void) {
|
||
scc_optparse_t parser;
|
||
const char *argv[] = {"program", "-h"};
|
||
INIT_OPTPARSE(argv);
|
||
scc_optparse_set(&parser, 0);
|
||
TEST_CHECK(parser.argc == 2);
|
||
TEST_CHECK(parser.argv == argv);
|
||
TEST_CHECK(parser.handle_positional == 0);
|
||
TEST_CHECK(parser.current.arg_pos == 1);
|
||
TEST_CHECK(parser.current.opt_pos == 0);
|
||
TEST_CHECK(parser.opts == 0);
|
||
TEST_CHECK(parser.current.opt == 0);
|
||
}
|
||
|
||
// 测试2: 简单短选项
|
||
void test_simple_short_options(void) {
|
||
scc_optparse_t parser;
|
||
scc_optparse_result_t res;
|
||
const char *argv[] = {"program", "-h", "-v"};
|
||
INIT_OPTPARSE(argv);
|
||
|
||
// 解析第一个选项 -h
|
||
TEST_CHECK(scc_optparse_parse(&parser, &res) != 0);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt != NULL);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'h');
|
||
TEST_CHECK(res.value == NULL);
|
||
|
||
// 解析第二个选项 -v
|
||
TEST_CHECK(scc_optparse_parse(&parser, &res) != 0);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt != NULL);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'v');
|
||
TEST_CHECK(res.value == NULL);
|
||
|
||
// 没有更多选项
|
||
TEST_CHECK(scc_optparse_parse(&parser, &res) == 0);
|
||
TEST_CHECK(res.opt == NULL);
|
||
TEST_CHECK(res.value == NULL);
|
||
}
|
||
|
||
void test_simple_position_arg(void) {
|
||
scc_optparse_t parser;
|
||
scc_optparse_result_t res;
|
||
const char *argv[] = {"program", "a", "b"};
|
||
INIT_OPTPARSE(argv);
|
||
// 解析第一个选项
|
||
TEST_CHECK(scc_optparse_parse(&parser, &res) != 0);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt == NULL);
|
||
TEST_CHECK(strcmp(res.value, "a") == 0);
|
||
|
||
// 解析第二个选项
|
||
TEST_CHECK(scc_optparse_parse(&parser, &res) != 0);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt == NULL);
|
||
TEST_CHECK(strcmp(res.value, "b") == 0);
|
||
|
||
// 没有更多选项
|
||
TEST_CHECK(scc_optparse_parse(&parser, &res) == 0);
|
||
TEST_CHECK(res.opt == NULL);
|
||
TEST_CHECK(res.value == NULL);
|
||
}
|
||
|
||
// 测试3: 带参数的短选项
|
||
void test_short_options_with_args(void) {
|
||
scc_optparse_t parser;
|
||
scc_optparse_result_t res;
|
||
const char *argv[] = {"program", "-f", "file.txt", "-o", "output.txt"};
|
||
INIT_OPTPARSE(argv);
|
||
|
||
// 解析 -f file.txt
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt != NULL);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'f');
|
||
TEST_CHECK(strcmp(res.value, "file.txt") == 0);
|
||
|
||
// 解析 -o output.txt
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt != NULL);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'o');
|
||
TEST_CHECK(strcmp(res.value, "output.txt") == 0);
|
||
}
|
||
|
||
// 测试4: 长选项
|
||
void test_long_options(void) {
|
||
scc_optparse_t parser;
|
||
scc_optparse_result_t res;
|
||
const char *argv[] = {"program", "--help", "--file=test.txt", "--output",
|
||
"out.txt"};
|
||
|
||
INIT_OPTPARSE(argv);
|
||
|
||
// 解析 --help
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt != NULL);
|
||
TEST_CHECK(res.opt && strcmp(res.opt->long_name, "help") == 0);
|
||
|
||
// 解析 --file=test.txt
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt != NULL);
|
||
TEST_CHECK(res.opt && strcmp(res.opt->long_name, "file") == 0);
|
||
TEST_CHECK(strcmp(res.value, "test.txt") == 0);
|
||
|
||
// 解析 --output out.txt
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt != NULL);
|
||
TEST_CHECK(res.opt && strcmp(res.opt->long_name, "output") == 0);
|
||
TEST_CHECK(strcmp(res.value, "out.txt") == 0);
|
||
}
|
||
|
||
// 测试5: 混合选项和位置参数
|
||
void test_mixed_options_positional(void) {
|
||
scc_optparse_t parser;
|
||
scc_optparse_result_t res;
|
||
const char *argv[] = {"program", "-h", "-f",
|
||
"input.txt", "positional1", "positional2"};
|
||
|
||
INIT_OPTPARSE(argv);
|
||
|
||
// 解析 -h
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'h');
|
||
|
||
// 解析 -f input.txt
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'f');
|
||
TEST_CHECK(strcmp(res.value, "input.txt") == 0);
|
||
|
||
// 解析第一个位置参数
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt == NULL);
|
||
TEST_CHECK(strcmp(res.value, "positional1") == 0);
|
||
|
||
// 解析第二个位置参数
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt == NULL);
|
||
TEST_CHECK(strcmp(res.value, "positional2") == 0);
|
||
}
|
||
|
||
// 测试6: 短选项连写
|
||
void test_combined_short_options(void) {
|
||
scc_optparse_t parser;
|
||
scc_optparse_result_t res;
|
||
const char *argv[] = {"program", "-vh"};
|
||
|
||
INIT_OPTPARSE(argv);
|
||
|
||
// 解析 -v
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'v');
|
||
|
||
// 解析 -h
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'h');
|
||
}
|
||
|
||
// 测试7: 错误处理 - 未知选项
|
||
void test_unknown_options(void) {
|
||
scc_optparse_t parser;
|
||
scc_optparse_result_t res;
|
||
const char *argv[] = {"program", "-x", "--unknown"};
|
||
|
||
INIT_OPTPARSE(argv);
|
||
|
||
// 解析 -x (未知短选项)
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NOT_FOUND_SHORT_ARG);
|
||
|
||
// 重置解析器测试长选项
|
||
const char *argv2[] = {"program", "--unknown"};
|
||
INIT_OPTPARSE(argv2);
|
||
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NOT_FOUND_LONG_ARG);
|
||
}
|
||
|
||
// 测试8: 选项值紧跟在短选项后
|
||
void test_short_option_with_attached_value(void) {
|
||
scc_optparse_t parser;
|
||
scc_optparse_result_t res;
|
||
const char *argv[] = {"program", "-finput.txt", "-ooutput.txt"};
|
||
|
||
INIT_OPTPARSE(argv);
|
||
|
||
// 解析 -finput.txt
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'f');
|
||
TEST_CHECK(strcmp(res.value, "input.txt") == 0);
|
||
|
||
// 解析 -ooutput.txt
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'o');
|
||
TEST_CHECK(strcmp(res.value, "output.txt") == 0);
|
||
}
|
||
|
||
// 测试9: 选项终止符 --
|
||
void test_option_terminator(void) {
|
||
scc_optparse_t parser;
|
||
scc_optparse_result_t res;
|
||
const char *argv[] = {"program", "-h", "--", "-f", "file.txt"};
|
||
|
||
INIT_OPTPARSE(argv);
|
||
|
||
// 解析 -h
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'h');
|
||
|
||
// 解析 -- (应该触发位置参数模式)
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(parser.handle_positional == 1);
|
||
|
||
// 解析 -f (作为位置参数)
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt == NULL);
|
||
TEST_CHECK(strcmp(res.value, "-f") == 0);
|
||
|
||
// 解析 file.txt (作为位置参数)
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt == NULL);
|
||
TEST_CHECK(strcmp(res.value, "file.txt") == 0);
|
||
}
|
||
|
||
// 测试10: 空参数和边界情况
|
||
void test_edge_cases(void) {
|
||
scc_optparse_t parser;
|
||
scc_optparse_result_t res;
|
||
|
||
// 测试空参数数组
|
||
const char *argv1[] = {"program"};
|
||
INIT_OPTPARSE(argv1);
|
||
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.opt == NULL);
|
||
TEST_CHECK(res.value == NULL);
|
||
|
||
// 测试空字符串参数
|
||
const char *argv2[] = {"program", "-f", "", "-o", " "};
|
||
INIT_OPTPARSE(argv2);
|
||
|
||
// 解析 -f ""
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'f');
|
||
TEST_CHECK(strcmp(res.value, "") == 0);
|
||
|
||
// 解析 -o " "
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'o');
|
||
TEST_CHECK(strcmp(res.value, " ") == 0);
|
||
}
|
||
|
||
// 测试11: 多参数选项
|
||
void test_multi_argument_option(void) {
|
||
scc_optparse_t parser;
|
||
scc_optparse_result_t res;
|
||
const char *argv[] = {"program", "-l", "item1", "item2", "item3"};
|
||
|
||
INIT_OPTPARSE(argv);
|
||
|
||
// 解析 -l
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'l');
|
||
TEST_CHECK(res.value == NULL);
|
||
|
||
// 由于 -l 可以接受多个参数,后续的参数应该作为 -l 的值
|
||
// 但根据当前实现,可能需要多次调用
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt != NULL);
|
||
TEST_CHECK(strcmp(res.value, "item1") == 0);
|
||
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt != NULL);
|
||
TEST_CHECK(strcmp(res.value, "item2") == 0);
|
||
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt != NULL);
|
||
TEST_CHECK(strcmp(res.value, "item3") == 0);
|
||
}
|
||
|
||
// 测试12: 参数不足的错误
|
||
void test_insufficient_arguments(void) {
|
||
scc_optparse_t parser;
|
||
scc_optparse_result_t res;
|
||
const char *argv[] = {"program", "-f"};
|
||
|
||
INIT_OPTPARSE(argv);
|
||
|
||
scc_optparse_parse(&parser, &res);
|
||
// 注意:当前实现可能需要额外检查
|
||
// TEST_CHECK(res.error == SCC_OPT_ERROR_NOT_ENOUGH_ARGS);
|
||
}
|
||
|
||
// 测试13: 长选项带等号但无值
|
||
void test_long_option_with_equal_no_value(void) {
|
||
scc_optparse_t parser;
|
||
scc_optparse_result_t res;
|
||
const char *argv[] = {"program", "--file="};
|
||
INIT_OPTPARSE(argv);
|
||
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NOT_ENOUGH_ARGS);
|
||
TEST_CHECK(res.opt != NULL);
|
||
TEST_CHECK(res.opt && strcmp(res.opt->long_name, "file") == 0);
|
||
}
|
||
|
||
// 测试14: 重置和重用解析器
|
||
void test_parser_reuse(void) {
|
||
scc_optparse_t parser;
|
||
scc_optparse_result_t res;
|
||
|
||
// 第一次使用
|
||
const char *argv1[] = {"program", "-h", "-v"};
|
||
INIT_OPTPARSE(argv1);
|
||
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'h');
|
||
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'v');
|
||
|
||
// 重置并重用
|
||
const char *argv2[] = {"program", "-f", "test.txt"};
|
||
INIT_OPTPARSE(argv2);
|
||
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'f');
|
||
TEST_CHECK(strcmp(res.value, "test.txt") == 0);
|
||
}
|
||
// 测试15: 短选项连写中带参数的情况
|
||
void test_combined_short_with_arg(void) {
|
||
scc_optparse_t parser;
|
||
scc_optparse_result_t res;
|
||
const char *argv[] = {"program", "-vfinput.txt"};
|
||
|
||
INIT_OPTPARSE(argv);
|
||
|
||
// 解析 -v
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt != NULL);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'v');
|
||
TEST_CHECK(res.value == NULL);
|
||
|
||
// 解析 -finput.txt
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt != NULL);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'f');
|
||
TEST_CHECK(strcmp(res.value, "input.txt") == 0);
|
||
}
|
||
|
||
// 测试16: 参数超过最大值的情况
|
||
void test_too_many_arguments(void) {
|
||
scc_optparse_t parser;
|
||
scc_optparse_result_t res;
|
||
const char *argv[] = {"program", "-f", "file1", "file2"};
|
||
|
||
INIT_OPTPARSE(argv);
|
||
|
||
// 解析 -f file1
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt != NULL);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'f');
|
||
TEST_CHECK(strcmp(res.value, "file1") == 0);
|
||
|
||
// 尝试给 -f 第二个参数,应该返回错误
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt == NULL);
|
||
TEST_CHECK(strcmp(res.value, "file2") == 0);
|
||
}
|
||
|
||
// 测试17: 混合短选项和位置参数
|
||
void test_mixed_short_and_positional(void) {
|
||
scc_optparse_t parser;
|
||
scc_optparse_result_t res;
|
||
const char *argv[] = {"program", "-v", "-f", "input.txt", "positional"};
|
||
|
||
INIT_OPTPARSE(argv);
|
||
|
||
// 解析 -v
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'v');
|
||
|
||
// 解析 -f input.txt
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'f');
|
||
TEST_CHECK(strcmp(res.value, "input.txt") == 0);
|
||
|
||
// 解析位置参数
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt == NULL);
|
||
TEST_CHECK(strcmp(res.value, "positional") == 0);
|
||
}
|
||
|
||
// 测试18: 复杂的多参数选项
|
||
void test_complex_multi_argument(void) {
|
||
scc_optparse_t parser;
|
||
scc_optparse_result_t res;
|
||
const char *argv[] = {"program", "-c", "1", "2", "3", "extra"};
|
||
|
||
INIT_OPTPARSE(argv);
|
||
|
||
// 解析 -c 1
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'c');
|
||
TEST_CHECK(strcmp(res.value, "1") == 0);
|
||
|
||
// 解析 -c 2
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'c');
|
||
TEST_CHECK(strcmp(res.value, "2") == 0);
|
||
|
||
// 解析 -c 3
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'c');
|
||
TEST_CHECK(strcmp(res.value, "3") == 0);
|
||
|
||
// 第4个参数应该是位置参数
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt == NULL);
|
||
TEST_CHECK(strcmp(res.value, "extra") == 0);
|
||
}
|
||
|
||
// 测试19: 长选项带多个参数
|
||
void test_long_option_multi_args(void) {
|
||
scc_optparse_t parser;
|
||
scc_optparse_result_t res;
|
||
const char *argv[] = {"program", "--list", "item1", "item2", "item3"};
|
||
|
||
INIT_OPTPARSE(argv);
|
||
|
||
// 解析 --list
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt && strcmp(res.opt->long_name, "list") == 0);
|
||
TEST_CHECK(res.value == NULL);
|
||
|
||
// 解析 item1
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt && strcmp(res.opt->long_name, "list") == 0);
|
||
TEST_CHECK(strcmp(res.value, "item1") == 0);
|
||
|
||
// 解析 item2
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt && strcmp(res.opt->long_name, "list") == 0);
|
||
TEST_CHECK(strcmp(res.value, "item2") == 0);
|
||
|
||
// 解析 item3
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt && strcmp(res.opt->long_name, "list") == 0);
|
||
TEST_CHECK(strcmp(res.value, "item3") == 0);
|
||
}
|
||
|
||
// 测试20: 空长选项名(只有--)
|
||
void test_empty_long_name(void) {
|
||
scc_optparse_t parser;
|
||
scc_optparse_result_t res;
|
||
const char *argv[] = {"program", "--"};
|
||
|
||
INIT_OPTPARSE(argv);
|
||
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(parser.handle_positional == 1);
|
||
TEST_CHECK(res.opt == NULL);
|
||
TEST_CHECK(strcmp(res.value, "--") == 0);
|
||
}
|
||
|
||
// 测试21: 选项前缀非'-'的情况
|
||
void test_non_dash_prefix(void) {
|
||
scc_optparse_t parser;
|
||
scc_optparse_result_t res;
|
||
|
||
static scc_optparse_opt_t custom_opts[] = {
|
||
SCC_OPTPARSE_OPT('/', 'h', "help", 0, 0),
|
||
SCC_OPTPARSE_OPT_END(),
|
||
};
|
||
|
||
const char *argv[] = {"program", "/h"};
|
||
INIT_OPTPARSE(argv);
|
||
parser.opts = custom_opts;
|
||
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt != NULL);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'h');
|
||
TEST_CHECK(res.opt && res.opt->prefix == '/');
|
||
}
|
||
|
||
// 测试22: 默认值功能测试
|
||
// TODO
|
||
void test_default_value(void) {
|
||
// scc_optparse_t parser;
|
||
// scc_optparse_result_t res;
|
||
|
||
// static scc_optparse_opt_t default_opts[] = {
|
||
// SCC_OPTPARSE_OPT('-', 'o', "output", 0, 1, "default.txt", NULL),
|
||
// SCC_OPTPARSE_OPT_END(),
|
||
// };
|
||
|
||
// // 测试1: 不提供选项,应该有默认值
|
||
// const char *argv1[] = {"program"};
|
||
// INIT_OPTPARSE(argv1);
|
||
// parser.opts = default_opts;
|
||
|
||
// scc_optparse_parse(&parser, &res);
|
||
// // 注意:这个测试需要修改解析器以支持默认值
|
||
// // 当前实现不支持,所以只是演示
|
||
|
||
// // 测试2: 提供选项但没有值,应该使用默认值
|
||
// const char *argv2[] = {"program", "-o"};
|
||
// INIT_OPTPARSE(argv2);
|
||
// parser.opts = default_opts;
|
||
|
||
// scc_optparse_parse(&parser, &res);
|
||
// TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
// TEST_CHECK(res.opt && res.opt->short_name == 'o');
|
||
// // 这里可以检查默认值,但当前实现不支持
|
||
}
|
||
|
||
// 测试23: 回调函数测试
|
||
static int callback_called = 0;
|
||
static void test_callback(void *value) {
|
||
(void)value;
|
||
callback_called = 1;
|
||
}
|
||
// TODO
|
||
void test_callback_function(void) {
|
||
scc_optparse_t parser;
|
||
scc_optparse_result_t res;
|
||
|
||
static scc_optparse_opt_t callback_opts[] = {
|
||
SCC_OPTPARSE_OPT('-', 'h', "help", 0, 0),
|
||
SCC_OPTPARSE_OPT_END(),
|
||
};
|
||
|
||
const char *argv[] = {"program", "--help"};
|
||
INIT_OPTPARSE(argv);
|
||
parser.opts = callback_opts;
|
||
|
||
callback_called = 0;
|
||
scc_optparse_parse(&parser, &res);
|
||
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt && strcmp(res.opt->long_name, "help") == 0);
|
||
|
||
// // 调用回调函数
|
||
// if (res.opt->invoke) {
|
||
// res.opt->invoke(NULL);
|
||
// TEST_CHECK(callback_called == 1);
|
||
// }
|
||
}
|
||
|
||
// 测试24: 复杂的短选项组合
|
||
void test_complex_short_combination(void) {
|
||
scc_optparse_t parser;
|
||
scc_optparse_result_t res;
|
||
const char *argv[] = {"program", "-vhfinput.txt"};
|
||
|
||
INIT_OPTPARSE(argv);
|
||
scc_optparse_set(&parser, test_opts);
|
||
|
||
// 解析 -v
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'v');
|
||
|
||
// 解析 -h
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'h');
|
||
|
||
// 解析 -finput.txt
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'f');
|
||
TEST_CHECK(strcmp(res.value, "input.txt") == 0);
|
||
}
|
||
|
||
// 测试25: 边界情况 - 单个横杠
|
||
void test_single_dash(void) {
|
||
scc_optparse_t parser;
|
||
scc_optparse_result_t res;
|
||
const char *argv[] = {"program", "-", "--", "arg"};
|
||
|
||
INIT_OPTPARSE(argv);
|
||
scc_optparse_set(&parser, test_opts);
|
||
|
||
// 解析单个横杠(通常表示标准输入)
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.opt == NULL);
|
||
TEST_CHECK(strcmp(res.value, "-") == 0);
|
||
|
||
// 解析 --
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(parser.handle_positional == 1);
|
||
|
||
// 解析 arg
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(strcmp(res.value, "arg") == 0);
|
||
}
|
||
|
||
// 测试26: 重置解析器状态
|
||
void test_parser_state_reset(void) {
|
||
scc_optparse_t parser;
|
||
scc_optparse_result_t res;
|
||
const char *argv[] = {"program", "-f", "file1", "--", "arg1", "arg2"};
|
||
|
||
INIT_OPTPARSE(argv);
|
||
scc_optparse_set(&parser, test_opts);
|
||
|
||
// 解析 -f file1
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'f');
|
||
|
||
// 解析 --
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(parser.handle_positional == 1);
|
||
|
||
// 解析 arg1
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(strcmp(res.value, "arg1") == 0);
|
||
|
||
// 解析 arg2
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(strcmp(res.value, "arg2") == 0);
|
||
|
||
// 重置解析器
|
||
const char *argv2[] = {"program", "-v", "-h"};
|
||
INIT_OPTPARSE(argv2);
|
||
|
||
// 应该能正确解析新的参数
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'v');
|
||
|
||
scc_optparse_parse(&parser, &res);
|
||
TEST_CHECK(res.opt && res.opt->short_name == 'h');
|
||
}
|
||
|
||
// 更新测试列表
|
||
TEST_LIST = {
|
||
{"test_init", test_init},
|
||
{"test_simple_short_options", test_simple_short_options},
|
||
{"test_simple_position_arg", test_simple_position_arg},
|
||
{"test_short_options_with_args", test_short_options_with_args},
|
||
{"test_long_options", test_long_options},
|
||
{"test_mixed_options_positional", test_mixed_options_positional},
|
||
{"test_combined_short_options", test_combined_short_options},
|
||
{"test_unknown_options", test_unknown_options},
|
||
{"test_short_option_with_attached_value",
|
||
test_short_option_with_attached_value},
|
||
{"test_option_terminator", test_option_terminator},
|
||
{"test_edge_cases", test_edge_cases},
|
||
{"test_multi_argument_option", test_multi_argument_option},
|
||
{"test_insufficient_arguments", test_insufficient_arguments},
|
||
{"test_long_option_with_equal_no_value",
|
||
test_long_option_with_equal_no_value},
|
||
{"test_parser_reuse", test_parser_reuse},
|
||
{"test_combined_short_with_arg", test_combined_short_with_arg},
|
||
{"test_too_many_arguments", test_too_many_arguments},
|
||
{"test_mixed_short_and_positional", test_mixed_short_and_positional},
|
||
{"test_complex_multi_argument", test_complex_multi_argument},
|
||
{"test_long_option_multi_args", test_long_option_multi_args},
|
||
{"test_empty_long_name", test_empty_long_name},
|
||
{"test_non_dash_prefix", test_non_dash_prefix},
|
||
{"test_default_value", test_default_value},
|
||
{"test_callback_function", test_callback_function},
|
||
{"test_complex_short_combination", test_complex_short_combination},
|
||
{"test_single_dash", test_single_dash},
|
||
{"test_parser_state_reset", test_parser_state_reset},
|
||
{NULL, NULL},
|
||
}; |