阶段2-2:MachineConfig 重写为 V2(强校验 5 项)+ TypeInfo/machine_test V2 适配
- ConfigType:base/kind/func(V1 的 tag/range 删除);ConfigOp:prefix/op/fmt/ func_mode(V1 的 opcode/class 删除);ConfigFb:fb_id(V1 的 opcode 删除) - 强校验:meta(version=2) / type 19 条全集·base 命中·kind 合法·func 与 (width,signed,float) 互锁 / op prefix 合法·prefix↔fmt 互锁·(prefix,op) 分区 唯一·func 类别·params 互锁 / fb 15 条全集·fb_id 唯一·字段命中 type 表 / op-fb 互锁(CAL↔fb 表) - TypeMeta:tag/range → func/kind - machine_test:V2 断言(正例 68 op/19 type/15 fb + 14 个负例) - 状态:compiler 库暂挂于 V1 Codec/Codegen/Stb/main(下一步按 Codec → Stb → Codegen → main 顺序 V2 化)
This commit is contained in:
+224
-167
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @file machine_test.cpp
|
||||
* @brief machine.toml 加载与强校验测试(阶段 A 步骤 3)
|
||||
* @brief machine.toml V2 加载与强校验测试(阶段 2)
|
||||
* @author
|
||||
* @date 2026-08-21
|
||||
*/
|
||||
@@ -64,16 +64,51 @@ static bool expect_load_err(const char* name, const char* content, const char* k
|
||||
return true;
|
||||
}
|
||||
|
||||
// 合法内容模板(可注入损坏)
|
||||
static const char* kGood =
|
||||
"[meta]\nname = \"STATOR\"\nversion = 1\n"
|
||||
"[[type]]\nname = \"BOOL\"\nbase = \"uint8\"\nrange = [0, 1]\ntag = 0\n"
|
||||
"[[type]]\nname = \"INT\"\nbase = \"int16\"\ntag = 1\n"
|
||||
"[[type]]\nname = \"TIME\"\nbase = \"int64\"\ntag = 2\n"
|
||||
"[[op]]\nname = \"MOVE\"\nopcode = 0\nclass = \"plain\"\nformat = \"RR\"\nparams = [\"rd\", \"rs\"]\nenabled = true\n"
|
||||
"[[op]]\nname = \"CAL_TON\"\nopcode = 24\nclass = \"instance\"\nformat = \"CAL\"\nparams = [\"instance\"]\nenabled = true\n"
|
||||
"[[op]]\nname = \"RET\"\nopcode = 33\nclass = \"plain\"\nformat = \"NONE\"\nparams = []\nenabled = true\n"
|
||||
"[[fb]]\nname = \"ton\"\nopcode = 24\nfields = [[\"in\", \"BOOL\"], [\"pt\", \"TIME\"], [\"q\", \"BOOL\"], [\"et\", \"TIME\"]]\n";
|
||||
// 合法 type 模板(19 条全集,可注入损坏;缺 DT 时用于"缺全集"负例)
|
||||
static const char* kTypesAll =
|
||||
"[[type]]\nname = \"BOOL\"\nbase = \"uint8\"\nkind = \"bool\"\nfunc = 0\n"
|
||||
"[[type]]\nname = \"BYTE\"\nbase = \"uint8\"\nkind = \"bit\"\nfunc = 0\n"
|
||||
"[[type]]\nname = \"WORD\"\nbase = \"uint16\"\nkind = \"bit\"\nfunc = 2\n"
|
||||
"[[type]]\nname = \"DWORD\"\nbase = \"uint32\"\nkind = \"bit\"\nfunc = 4\n"
|
||||
"[[type]]\nname = \"LWORD\"\nbase = \"uint64\"\nkind = \"bit\"\nfunc = 6\n"
|
||||
"[[type]]\nname = \"SINT\"\nbase = \"int8\"\nkind = \"int\"\nfunc = 1\n"
|
||||
"[[type]]\nname = \"INT\"\nbase = \"int16\"\nkind = \"int\"\nfunc = 3\n"
|
||||
"[[type]]\nname = \"DINT\"\nbase = \"int32\"\nkind = \"int\"\nfunc = 5\n"
|
||||
"[[type]]\nname = \"LINT\"\nbase = \"int64\"\nkind = \"int\"\nfunc = 7\n"
|
||||
"[[type]]\nname = \"USINT\"\nbase = \"uint8\"\nkind = \"uint\"\nfunc = 0\n"
|
||||
"[[type]]\nname = \"UINT\"\nbase = \"uint16\"\nkind = \"uint\"\nfunc = 2\n"
|
||||
"[[type]]\nname = \"UDINT\"\nbase = \"uint32\"\nkind = \"uint\"\nfunc = 4\n"
|
||||
"[[type]]\nname = \"ULINT\"\nbase = \"uint64\"\nkind = \"uint\"\nfunc = 6\n"
|
||||
"[[type]]\nname = \"TIME\"\nbase = \"int64\"\nkind = \"time\"\nfunc = 7\n"
|
||||
"[[type]]\nname = \"REAL\"\nbase = \"float32\"\nkind = \"float\"\nfunc = 8\n"
|
||||
"[[type]]\nname = \"LREAL\"\nbase = \"float64\"\nkind = \"float\"\nfunc = 9\n"
|
||||
"[[type]]\nname = \"DATE\"\nbase = \"uint32\"\nkind = \"date\"\nfunc = 4\n"
|
||||
"[[type]]\nname = \"TOD\"\nbase = \"uint32\"\nkind = \"date\"\nfunc = 4\n"
|
||||
"[[type]]\nname = \"DT\"\nbase = \"uint64\"\nkind = \"date\"\nfunc = 6\n";
|
||||
|
||||
// 合法 op 模板(含 CAL 与 RET,可注入损坏)
|
||||
static const char* kOpsBasic =
|
||||
"[[op]]\nname = \"MOVE\"\nprefix = \"11\"\nop = 0\nfmt = \"RR\"\nfunc = \"width\"\nparams = [\"rd\", \"rs\"]\nenabled = true\n"
|
||||
"[[op]]\nname = \"CAL\"\nprefix = \"010101\"\nop = 0\nfmt = \"CAL\"\nfunc = \"none\"\nparams = [\"fb_id\", \"slot\"]\nenabled = true\n"
|
||||
"[[op]]\nname = \"RET\"\nprefix = \"10\"\nop = 1\nfmt = \"NONE\"\nfunc = \"none\"\nparams = []\nenabled = true\n";
|
||||
|
||||
// 合法 fb 模板(15 条全集,可注入损坏)
|
||||
static const char* kFbsAll =
|
||||
"[[fb]]\nname = \"TON\"\nfb_id = 0\nfields = [[\"in\", \"BOOL\"], [\"pt\", \"TIME\"], [\"q\", \"BOOL\"], [\"et\", \"TIME\"]]\n"
|
||||
"[[fb]]\nname = \"TOF\"\nfb_id = 1\nfields = [[\"in\", \"BOOL\"], [\"pt\", \"TIME\"], [\"q\", \"BOOL\"], [\"et\", \"TIME\"]]\n"
|
||||
"[[fb]]\nname = \"TP\"\nfb_id = 2\nfields = [[\"in\", \"BOOL\"], [\"pt\", \"TIME\"], [\"q\", \"BOOL\"], [\"et\", \"TIME\"]]\n"
|
||||
"[[fb]]\nname = \"CTU\"\nfb_id = 3\nfields = [[\"cu\", \"BOOL\"], [\"r\", \"BOOL\"], [\"pv\", \"INT\"], [\"q\", \"BOOL\"], [\"cv\", \"INT\"]]\n"
|
||||
"[[fb]]\nname = \"DCTU\"\nfb_id = 4\nfields = [[\"cu\", \"BOOL\"], [\"r\", \"BOOL\"], [\"pv\", \"DINT\"], [\"q\", \"BOOL\"], [\"cv\", \"DINT\"]]\n"
|
||||
"[[fb]]\nname = \"CTD\"\nfb_id = 5\nfields = [[\"cd\", \"BOOL\"], [\"ld\", \"BOOL\"], [\"pv\", \"INT\"], [\"q\", \"BOOL\"], [\"cv\", \"INT\"]]\n"
|
||||
"[[fb]]\nname = \"DCTD\"\nfb_id = 6\nfields = [[\"cd\", \"BOOL\"], [\"ld\", \"BOOL\"], [\"pv\", \"DINT\"], [\"q\", \"BOOL\"], [\"cv\", \"DINT\"]]\n"
|
||||
"[[fb]]\nname = \"CTUD\"\nfb_id = 7\nfields = [[\"cu\", \"BOOL\"], [\"cd\", \"BOOL\"], [\"r\", \"BOOL\"], [\"lu\", \"BOOL\"], [\"pv\", \"INT\"], [\"qu\", \"BOOL\"], [\"qd\", \"BOOL\"], [\"cv\", \"INT\"]]\n"
|
||||
"[[fb]]\nname = \"DCTUD\"\nfb_id = 8\nfields = [[\"cu\", \"BOOL\"], [\"cd\", \"BOOL\"], [\"r\", \"BOOL\"], [\"lu\", \"BOOL\"], [\"pv\", \"DINT\"], [\"qu\", \"BOOL\"], [\"qd\", \"BOOL\"], [\"cv\", \"DINT\"]]\n"
|
||||
"[[fb]]\nname = \"R_TRIG\"\nfb_id = 9\nfields = [[\"clk\", \"BOOL\"], [\"q\", \"BOOL\"]]\n"
|
||||
"[[fb]]\nname = \"F_TRIG\"\nfb_id = 10\nfields = [[\"clk\", \"BOOL\"], [\"q\", \"BOOL\"]]\n"
|
||||
"[[fb]]\nname = \"SR\"\nfb_id = 11\nfields = [[\"S1\", \"BOOL\"], [\"R\", \"BOOL\"], [\"Q1\", \"BOOL\"]]\n"
|
||||
"[[fb]]\nname = \"RS\"\nfb_id = 12\nfields = [[\"SET\", \"BOOL\"], [\"RESET1\", \"BOOL\"], [\"Q1\", \"BOOL\"]]\n"
|
||||
"[[fb]]\nname = \"PWM\"\nfb_id = 13\nfields = [[\"in\", \"BOOL\"], [\"pt\", \"TIME\"], [\"duty\", \"REAL\"], [\"q\", \"BOOL\"], [\"et\", \"TIME\"]]\n"
|
||||
"[[fb]]\nname = \"RTC\"\nfb_id = 14\nfields = [[\"enable\", \"BOOL\"], [\"date\", \"DATE\"], [\"tod\", \"TOD\"]]\n";
|
||||
|
||||
// ---- 1. 正例:仓库 machine.toml ----
|
||||
|
||||
@@ -83,31 +118,45 @@ static bool test_positive() {
|
||||
CHECK(cfg.load(std::string(REPO_ROOT) + "/compiler/machine.toml", &err));
|
||||
CHECK(cfg.ok());
|
||||
CHECK(cfg.model_name() == "STATOR");
|
||||
CHECK(cfg.version() == 1);
|
||||
CHECK(cfg.version() == 2);
|
||||
|
||||
CHECK(cfg.types().size() == 3);
|
||||
CHECK(cfg.types().size() == 19);
|
||||
const compiler::ConfigType* bt = cfg.find_type("BOOL");
|
||||
CHECK(bt != nullptr && bt->base == "uint8" && bt->has_range && bt->range_min == 0 &&
|
||||
bt->range_max == 1 && bt->tag == 0);
|
||||
CHECK(cfg.find_type("INT") != nullptr && cfg.find_type("INT")->tag == 1);
|
||||
CHECK(cfg.find_type("TIME") != nullptr && cfg.find_type("TIME")->tag == 2);
|
||||
CHECK(cfg.find_type("REAL") == nullptr);
|
||||
CHECK(bt != nullptr && bt->base == "uint8" && bt->kind == "bool" && bt->func == 0);
|
||||
CHECK(cfg.find_type("INT") != nullptr && cfg.find_type("INT")->base == "int16" &&
|
||||
cfg.find_type("INT")->kind == "int" && cfg.find_type("INT")->func == 3);
|
||||
CHECK(cfg.find_type("REAL") != nullptr && cfg.find_type("REAL")->func == 8);
|
||||
CHECK(cfg.find_type("LREAL") != nullptr && cfg.find_type("LREAL")->func == 9);
|
||||
CHECK(cfg.find_type("DATE") != nullptr && cfg.find_type("DATE")->func == 4);
|
||||
CHECK(cfg.find_type("TOD") != nullptr && cfg.find_type("TOD")->func == 4);
|
||||
CHECK(cfg.find_type("DT") != nullptr && cfg.find_type("DT")->func == 6);
|
||||
CHECK(cfg.find_type("NOPE") == nullptr);
|
||||
|
||||
CHECK(cfg.ops().size() == 34);
|
||||
const compiler::ConfigOp* add = cfg.find_op("ADD");
|
||||
CHECK(add != nullptr && add->opcode == 5 && add->format == "RRR" && !add->is_instance &&
|
||||
add->params.size() == 3);
|
||||
const compiler::ConfigOp* ton = cfg.find_op("CAL_TON");
|
||||
CHECK(ton != nullptr && ton->opcode == 24 && ton->is_instance && ton->format == "CAL");
|
||||
CHECK(cfg.find_op("CAL_F_TRIG") != nullptr && cfg.find_op("CAL_F_TRIG")->opcode == 31);
|
||||
CHECK(cfg.find_op_by_code(33) != nullptr && cfg.find_op_by_code(33)->name == "RET");
|
||||
CHECK(cfg.op_enabled(0) && cfg.op_enabled(24));
|
||||
CHECK(cfg.ops().size() == 68);
|
||||
const compiler::ConfigOp* move = cfg.find_op("MOVE");
|
||||
CHECK(move != nullptr && move->prefix == "11" && move->op == 0 && move->fmt == "RR" &&
|
||||
move->func_mode == "width" && move->params.size() == 2);
|
||||
const compiler::ConfigOp* load = cfg.find_op("LOAD");
|
||||
CHECK(load != nullptr && load->prefix == "010001" && load->fmt == "SLOT" &&
|
||||
load->func_mode == "full");
|
||||
const compiler::ConfigOp* cal = cfg.find_op("CAL");
|
||||
CHECK(cal != nullptr && cal->prefix == "010101" && cal->fmt == "CAL" &&
|
||||
cal->func_mode == "none" && cal->params.size() == 2);
|
||||
CHECK(cfg.find_op_by_key("11", 0) != nullptr &&
|
||||
cfg.find_op_by_key("11", 0)->name == "MOVE");
|
||||
CHECK(cfg.find_op_by_key("010001", 1) != nullptr &&
|
||||
cfg.find_op_by_key("010001", 1)->name == "STORE");
|
||||
CHECK(cfg.op_enabled("11", 0) && cfg.op_enabled("010101", 0));
|
||||
CHECK(!cfg.op_enabled("11", 63));
|
||||
CHECK(cfg.find_op("NOPE") == nullptr);
|
||||
|
||||
CHECK(cfg.fbs().size() == 8);
|
||||
const compiler::ConfigFb* ctud = cfg.find_fb("ctud");
|
||||
CHECK(ctud != nullptr && ctud->opcode == 29 && ctud->fields.size() == 8);
|
||||
CHECK(cfg.find_fb("r_trig") != nullptr && cfg.find_fb("r_trig")->fields.size() == 2);
|
||||
CHECK(cfg.fbs().size() == 15);
|
||||
const compiler::ConfigFb* ton = cfg.find_fb("TON");
|
||||
CHECK(ton != nullptr && ton->fb_id == 0 && ton->fields.size() == 4);
|
||||
CHECK(cfg.find_fb("DCTU") != nullptr && cfg.find_fb("DCTU")->fb_id == 4 &&
|
||||
cfg.find_fb("DCTU")->fields[2].type == "DINT");
|
||||
CHECK(cfg.find_fb_by_id(14) != nullptr && cfg.find_fb_by_id(14)->name == "RTC");
|
||||
CHECK(cfg.find_fb_by_id(15) == nullptr);
|
||||
|
||||
// 基元表
|
||||
CHECK(compiler::MachineConfig::find_prim("int16") != nullptr);
|
||||
@@ -118,178 +167,187 @@ static bool test_positive() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---- 2. 负例:9 类 ----
|
||||
// ---- 2. 负例:V2 校验项 ----
|
||||
|
||||
static bool test_negative() {
|
||||
// 缺属性(enabled 缺失)
|
||||
if (!expect_load_err(
|
||||
"mc_bad1.toml",
|
||||
"[meta]\nname = \"S\"\nversion = 1\n"
|
||||
"[[type]]\nname = \"BOOL\"\nbase = \"uint8\"\ntag = 0\n"
|
||||
"[[type]]\nname = \"INT\"\nbase = \"int16\"\ntag = 1\n"
|
||||
"[[type]]\nname = \"TIME\"\nbase = \"int64\"\ntag = 2\n"
|
||||
"[[op]]\nname = \"MOVE\"\nopcode = 0\nclass = \"plain\"\nformat = \"RR\"\nparams = [\"rd\", \"rs\"]\n",
|
||||
"missing field 'enabled'")) {
|
||||
const std::string m = "[meta]\nname = \"STATOR\"\nversion = 2\n";
|
||||
// version != 2
|
||||
if (!expect_load_err("mc_v2_bad1.toml",
|
||||
"[meta]\nname = \"STATOR\"\nversion = 1\n" + std::string(kTypesAll) +
|
||||
kOpsBasic + kFbsAll,
|
||||
"version must be 2")) {
|
||||
return false;
|
||||
}
|
||||
// type 缺全集(去掉 DT)
|
||||
{
|
||||
std::string types = kTypesAll;
|
||||
const std::string dt = "[[type]]\nname = \"DT\"\nbase = \"uint64\"\nkind = \"date\"\nfunc = 6\n";
|
||||
const size_t pos = types.find(dt);
|
||||
types.erase(pos, dt.size());
|
||||
if (!expect_load_err("mc_v2_bad2.toml", m + types + kOpsBasic + kFbsAll,
|
||||
"missing 'DT'")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// base 未命中基元
|
||||
if (!expect_load_err(
|
||||
"mc_bad2.toml",
|
||||
"[meta]\nname = \"S\"\nversion = 1\n"
|
||||
"[[type]]\nname = \"BOOL\"\nbase = \"bigint\"\ntag = 0\n"
|
||||
"[[type]]\nname = \"INT\"\nbase = \"int16\"\ntag = 1\n"
|
||||
"[[type]]\nname = \"TIME\"\nbase = \"int64\"\ntag = 2\n"
|
||||
"[[op]]\nname = \"RET\"\nopcode = 33\nclass = \"plain\"\nformat = \"NONE\"\nparams = []\nenabled = true\n",
|
||||
"unknown base")) {
|
||||
return false;
|
||||
{
|
||||
std::string types = kTypesAll;
|
||||
const std::string old = "[[type]]\nname = \"BYTE\"\nbase = \"uint8\"\nkind = \"bit\"\nfunc = 0\n";
|
||||
const std::string bad = "[[type]]\nname = \"BYTE\"\nbase = \"bigint\"\nkind = \"bit\"\nfunc = 0\n";
|
||||
types.replace(types.find(old), old.size(), bad);
|
||||
if (!expect_load_err("mc_v2_bad3.toml", m + types + kOpsBasic + kFbsAll,
|
||||
"unknown base")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// range min > max
|
||||
if (!expect_load_err(
|
||||
"mc_bad3.toml",
|
||||
"[meta]\nname = \"S\"\nversion = 1\n"
|
||||
"[[type]]\nname = \"BOOL\"\nbase = \"uint8\"\nrange = [1, 0]\ntag = 0\n"
|
||||
"[[type]]\nname = \"INT\"\nbase = \"int16\"\ntag = 1\n"
|
||||
"[[type]]\nname = \"TIME\"\nbase = \"int64\"\ntag = 2\n"
|
||||
"[[op]]\nname = \"RET\"\nopcode = 33\nclass = \"plain\"\nformat = \"NONE\"\nparams = []\nenabled = true\n",
|
||||
"range min > max")) {
|
||||
return false;
|
||||
// func 与 base 互锁破坏(INT func=0)
|
||||
{
|
||||
std::string types = kTypesAll;
|
||||
const std::string old = "[[type]]\nname = \"INT\"\nbase = \"int16\"\nkind = \"int\"\nfunc = 3\n";
|
||||
const std::string bad = "[[type]]\nname = \"INT\"\nbase = \"int16\"\nkind = \"int\"\nfunc = 0\n";
|
||||
types.replace(types.find(old), old.size(), bad);
|
||||
if (!expect_load_err("mc_v2_bad4.toml", m + types + kOpsBasic + kFbsAll,
|
||||
"func 0 mismatches base")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// tag 契约破坏(BOOL tag=5)
|
||||
if (!expect_load_err(
|
||||
"mc_bad4.toml",
|
||||
"[meta]\nname = \"S\"\nversion = 1\n"
|
||||
"[[type]]\nname = \"BOOL\"\nbase = \"uint8\"\ntag = 5\n"
|
||||
"[[type]]\nname = \"INT\"\nbase = \"int16\"\ntag = 1\n"
|
||||
"[[type]]\nname = \"TIME\"\nbase = \"int64\"\ntag = 2\n"
|
||||
"[[op]]\nname = \"RET\"\nopcode = 33\nclass = \"plain\"\nformat = \"NONE\"\nparams = []\nenabled = true\n",
|
||||
"tag out of contract")) {
|
||||
return false;
|
||||
// kind 非法
|
||||
{
|
||||
std::string types = kTypesAll;
|
||||
const std::string old = "[[type]]\nname = \"BOOL\"\nbase = \"uint8\"\nkind = \"bool\"\nfunc = 0\n";
|
||||
const std::string bad = "[[type]]\nname = \"BOOL\"\nbase = \"uint8\"\nkind = \"weird\"\nfunc = 0\n";
|
||||
types.replace(types.find(old), old.size(), bad);
|
||||
if (!expect_load_err("mc_v2_bad5.toml", m + types + kOpsBasic + kFbsAll,
|
||||
"bad kind")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// tag 契约破坏(INT 占 tag 0)
|
||||
if (!expect_load_err(
|
||||
"mc_bad5.toml",
|
||||
"[meta]\nname = \"S\"\nversion = 1\n"
|
||||
"[[type]]\nname = \"BOOL\"\nbase = \"uint8\"\ntag = 1\n"
|
||||
"[[type]]\nname = \"INT\"\nbase = \"int16\"\ntag = 0\n"
|
||||
"[[type]]\nname = \"TIME\"\nbase = \"int64\"\ntag = 2\n"
|
||||
"[[op]]\nname = \"RET\"\nopcode = 33\nclass = \"plain\"\nformat = \"NONE\"\nparams = []\nenabled = true\n",
|
||||
"type contract broken")) {
|
||||
return false;
|
||||
// prefix 非法
|
||||
{
|
||||
std::string ops = kOpsBasic;
|
||||
const std::string old = "[[op]]\nname = \"MOVE\"\nprefix = \"11\"\nop = 0\nfmt = \"RR\"\nfunc = \"width\"\nparams = [\"rd\", \"rs\"]\nenabled = true\n";
|
||||
const std::string bad = "[[op]]\nname = \"MOVE\"\nprefix = \"99\"\nop = 0\nfmt = \"RR\"\nfunc = \"width\"\nparams = [\"rd\", \"rs\"]\nenabled = true\n";
|
||||
ops.replace(ops.find(old), old.size(), bad);
|
||||
if (!expect_load_err("mc_v2_bad6.toml", m + kTypesAll + ops + kFbsAll,
|
||||
"bad prefix")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// opcode 重号
|
||||
if (!expect_load_err(
|
||||
"mc_bad6.toml",
|
||||
"[meta]\nname = \"S\"\nversion = 1\n"
|
||||
"[[type]]\nname = \"BOOL\"\nbase = \"uint8\"\ntag = 0\n"
|
||||
"[[type]]\nname = \"INT\"\nbase = \"int16\"\ntag = 1\n"
|
||||
"[[type]]\nname = \"TIME\"\nbase = \"int64\"\ntag = 2\n"
|
||||
"[[op]]\nname = \"MOVE\"\nopcode = 0\nclass = \"plain\"\nformat = \"RR\"\nparams = [\"rd\", \"rs\"]\nenabled = true\n"
|
||||
"[[op]]\nname = \"NOPE\"\nopcode = 0\nclass = \"plain\"\nformat = \"RR\"\nparams = [\"a\", \"b\"]\nenabled = true\n",
|
||||
"duplicate opcode")) {
|
||||
return false;
|
||||
// prefix-fmt 互锁破坏(11 + RRR)
|
||||
{
|
||||
std::string ops = kOpsBasic;
|
||||
const std::string old = "[[op]]\nname = \"MOVE\"\nprefix = \"11\"\nop = 0\nfmt = \"RR\"\nfunc = \"width\"\nparams = [\"rd\", \"rs\"]\nenabled = true\n";
|
||||
const std::string bad = "[[op]]\nname = \"MOVE\"\nprefix = \"11\"\nop = 0\nfmt = \"RRR\"\nfunc = \"width\"\nparams = [\"rd\", \"ra\", \"rb\"]\nenabled = true\n";
|
||||
ops.replace(ops.find(old), old.size(), bad);
|
||||
if (!expect_load_err("mc_v2_bad7.toml", m + kTypesAll + ops + kFbsAll,
|
||||
"must pair with fmt")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 类别-格式互锁破坏(instance + RRR)
|
||||
if (!expect_load_err(
|
||||
"mc_bad7.toml",
|
||||
"[meta]\nname = \"S\"\nversion = 1\n"
|
||||
"[[type]]\nname = \"BOOL\"\nbase = \"uint8\"\ntag = 0\n"
|
||||
"[[type]]\nname = \"INT\"\nbase = \"int16\"\ntag = 1\n"
|
||||
"[[type]]\nname = \"TIME\"\nbase = \"int64\"\ntag = 2\n"
|
||||
"[[op]]\nname = \"BAD\"\nopcode = 24\nclass = \"instance\"\nformat = \"RRR\"\nparams = [\"a\", \"b\", \"c\"]\nenabled = true\n",
|
||||
"class-format mismatch")) {
|
||||
return false;
|
||||
// (prefix, op) 分区内重复
|
||||
{
|
||||
std::string ops = kOpsBasic;
|
||||
ops += "[[op]]\nname = \"DUP\"\nprefix = \"11\"\nop = 0\nfmt = \"RR\"\nfunc = \"width\"\nparams = [\"a\", \"b\"]\nenabled = true\n";
|
||||
if (!expect_load_err("mc_v2_bad8.toml", m + kTypesAll + ops + kFbsAll,
|
||||
"duplicate (prefix, op)")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// params 数量与 format 不符
|
||||
if (!expect_load_err(
|
||||
"mc_bad8.toml",
|
||||
"[meta]\nname = \"S\"\nversion = 1\n"
|
||||
"[[type]]\nname = \"BOOL\"\nbase = \"uint8\"\ntag = 0\n"
|
||||
"[[type]]\nname = \"INT\"\nbase = \"int16\"\ntag = 1\n"
|
||||
"[[type]]\nname = \"TIME\"\nbase = \"int64\"\ntag = 2\n"
|
||||
"[[op]]\nname = \"MOVE\"\nopcode = 0\nclass = \"plain\"\nformat = \"RR\"\nparams = [\"rd\"]\nenabled = true\n",
|
||||
"params count")) {
|
||||
return false;
|
||||
// func 类别非法
|
||||
{
|
||||
std::string ops = kOpsBasic;
|
||||
const std::string old = "[[op]]\nname = \"MOVE\"\nprefix = \"11\"\nop = 0\nfmt = \"RR\"\nfunc = \"width\"\nparams = [\"rd\", \"rs\"]\nenabled = true\n";
|
||||
const std::string bad = "[[op]]\nname = \"MOVE\"\nprefix = \"11\"\nop = 0\nfmt = \"RR\"\nfunc = \"magic\"\nparams = [\"rd\", \"rs\"]\nenabled = true\n";
|
||||
ops.replace(ops.find(old), old.size(), bad);
|
||||
if (!expect_load_err("mc_v2_bad9.toml", m + kTypesAll + ops + kFbsAll,
|
||||
"bad func mode")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// fb.opcode 不匹配 instance op
|
||||
if (!expect_load_err(
|
||||
"mc_bad9.toml",
|
||||
"[meta]\nname = \"S\"\nversion = 1\n"
|
||||
"[[type]]\nname = \"BOOL\"\nbase = \"uint8\"\ntag = 0\n"
|
||||
"[[type]]\nname = \"INT\"\nbase = \"int16\"\ntag = 1\n"
|
||||
"[[type]]\nname = \"TIME\"\nbase = \"int64\"\ntag = 2\n"
|
||||
"[[op]]\nname = \"RET\"\nopcode = 33\nclass = \"plain\"\nformat = \"NONE\"\nparams = []\nenabled = true\n"
|
||||
"[[fb]]\nname = \"ton\"\nopcode = 33\nfields = [[\"in\", \"BOOL\"]]\n",
|
||||
"must match an instance op")) {
|
||||
return false;
|
||||
// params 数量与 fmt 不符(CAL 应为 2)
|
||||
{
|
||||
std::string ops = kOpsBasic;
|
||||
const std::string old = "[[op]]\nname = \"CAL\"\nprefix = \"010101\"\nop = 0\nfmt = \"CAL\"\nfunc = \"none\"\nparams = [\"fb_id\", \"slot\"]\nenabled = true\n";
|
||||
const std::string bad = "[[op]]\nname = \"CAL\"\nprefix = \"010101\"\nop = 0\nfmt = \"CAL\"\nfunc = \"none\"\nparams = [\"fb_id\"]\nenabled = true\n";
|
||||
ops.replace(ops.find(old), old.size(), bad);
|
||||
if (!expect_load_err("mc_v2_bad10.toml", m + kTypesAll + ops + kFbsAll,
|
||||
"params count")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// fb 字段类型未命中 type 表
|
||||
if (!expect_load_err(
|
||||
"mc_bad10.toml",
|
||||
"[meta]\nname = \"S\"\nversion = 1\n"
|
||||
"[[type]]\nname = \"BOOL\"\nbase = \"uint8\"\ntag = 0\n"
|
||||
"[[type]]\nname = \"INT\"\nbase = \"int16\"\ntag = 1\n"
|
||||
"[[type]]\nname = \"TIME\"\nbase = \"int64\"\ntag = 2\n"
|
||||
"[[op]]\nname = \"CAL_TON\"\nopcode = 24\nclass = \"instance\"\nformat = \"CAL\"\nparams = [\"instance\"]\nenabled = true\n"
|
||||
"[[fb]]\nname = \"ton\"\nopcode = 24\nfields = [[\"in\", \"REAL\"]]\n",
|
||||
"unknown field type")) {
|
||||
return false;
|
||||
// fb 缺全集(去掉 RTC)
|
||||
{
|
||||
std::string fbs = kFbsAll;
|
||||
const std::string rtc = "[[fb]]\nname = \"RTC\"\nfb_id = 14\nfields = [[\"enable\", \"BOOL\"], [\"date\", \"DATE\"], [\"tod\", \"TOD\"]]\n";
|
||||
fbs.erase(fbs.find(rtc), rtc.size());
|
||||
if (!expect_load_err("mc_v2_bad11.toml", m + kTypesAll + kOpsBasic + fbs,
|
||||
"missing fb_id 14")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// fb 字段类型未知
|
||||
{
|
||||
std::string fbs = kFbsAll;
|
||||
const std::string old = "[[\"in\", \"BOOL\"]], [\"pt\", \"TIME\"]";
|
||||
const std::string bad = "[[\"in\", \"REAL\"]], [\"pt\", \"TIME\"]";
|
||||
fbs.replace(fbs.find(old), old.size(), bad);
|
||||
if (!expect_load_err("mc_v2_bad12.toml", m + kTypesAll + kOpsBasic + fbs,
|
||||
"unknown field type")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 缺 CAL(op-fb 互锁)
|
||||
{
|
||||
std::string ops = kOpsBasic;
|
||||
const std::string cal = "[[op]]\nname = \"CAL\"\nprefix = \"010101\"\nop = 0\nfmt = \"CAL\"\nfunc = \"none\"\nparams = [\"fb_id\", \"slot\"]\nenabled = true\n";
|
||||
ops.erase(ops.find(cal), cal.size());
|
||||
if (!expect_load_err("mc_v2_bad13.toml", m + kTypesAll + ops + kFbsAll,
|
||||
"op-fb interlock")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 语法错误
|
||||
if (!expect_load_err("mc_bad11.toml", "[meta\nname = \"S\"\n", "parse error")) {
|
||||
if (!expect_load_err("mc_v2_bad14.toml", "[meta\nname = \"S\"\n", "parse error")) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---- 3. 合法最小配置正例 ----
|
||||
|
||||
static bool test_positive_min() {
|
||||
const std::string path = write_tmp("mc_good.toml", kGood);
|
||||
compiler::MachineConfig cfg;
|
||||
std::string err;
|
||||
CHECK(cfg.load(path, &err));
|
||||
CHECK(cfg.ops().size() == 3);
|
||||
CHECK(cfg.fbs().size() == 1);
|
||||
CHECK(cfg.find_fb("ton")->fields.size() == 4);
|
||||
std::remove(path.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---- 4. 类型元数据(TypeInfo)----
|
||||
// ---- 3. 类型元数据(TypeInfo,V2)----
|
||||
|
||||
static bool test_type_meta() {
|
||||
compiler::MachineConfig cfg;
|
||||
std::string err;
|
||||
CHECK(cfg.load(std::string(REPO_ROOT) + "/compiler/machine.toml", &err));
|
||||
|
||||
// BOOL:base=uint8 → 1 字节、无符号、非浮点、tag 0、range [0,1]
|
||||
// BOOL:1 字节、无符号、kind bool、func 0
|
||||
const compiler::TypeMeta b = compiler::type_meta(cfg, "BOOL");
|
||||
CHECK(b && b.width() == 1 && !b.is_signed() && !b.is_float() && b.tag() == 0);
|
||||
CHECK(b.has_range() && b.value_in_range(0) && b.value_in_range(1));
|
||||
CHECK(!b.value_in_range(2) && !b.value_in_range(-1));
|
||||
CHECK(b && b.width() == 1 && !b.is_signed() && !b.is_float() && b.func() == 0);
|
||||
CHECK(b.kind() == "bool");
|
||||
// 大小写不敏感(Linker type_name 是小写)
|
||||
const compiler::TypeMeta b2 = compiler::type_meta(cfg, "bool");
|
||||
CHECK(b2 && b2.tag() == 0 && b2.width() == 1);
|
||||
CHECK(b2 && b2.func() == 0 && b2.width() == 1);
|
||||
|
||||
// INT:int16 → 2 字节、有符号、tag 1、无 range
|
||||
// INT:2 字节、有符号、kind int、func 3
|
||||
const compiler::TypeMeta i = compiler::type_meta(cfg, "int");
|
||||
CHECK(i && i.width() == 2 && i.is_signed() && !i.is_float() && i.tag() == 1);
|
||||
CHECK(!i.has_range() || i.value_in_range(30000));
|
||||
CHECK(i && i.width() == 2 && i.is_signed() && !i.is_float() && i.func() == 3);
|
||||
CHECK(i.kind() == "int");
|
||||
|
||||
// TIME:int64 → 8 字节、有符号、tag 2
|
||||
const compiler::TypeMeta t = compiler::type_meta(cfg, "TIME");
|
||||
CHECK(t && t.width() == 8 && t.is_signed() && !t.is_float() && t.tag() == 2);
|
||||
// REAL:4 字节、浮点、func 8;TOD:4 字节、func 4
|
||||
const compiler::TypeMeta r = compiler::type_meta(cfg, "REAL");
|
||||
CHECK(r && r.width() == 4 && r.is_float() && r.func() == 8);
|
||||
const compiler::TypeMeta tod = compiler::type_meta(cfg, "TOD");
|
||||
CHECK(tod && tod.width() == 4 && tod.func() == 4);
|
||||
|
||||
// 未定义类型 → 空
|
||||
CHECK(!compiler::type_meta(cfg, "REAL"));
|
||||
CHECK(!compiler::type_meta(cfg, "NOPE"));
|
||||
CHECK(!compiler::type_meta(cfg, ""));
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---- 5. SHA-256 已知向量 + 型号标识 ----
|
||||
// ---- 4. SHA-256 已知向量 + 型号标识 ----
|
||||
|
||||
static bool test_sha256() {
|
||||
// 标准测试向量
|
||||
{
|
||||
uint8_t out[compiler::kSha256Size];
|
||||
compiler::sha256(nullptr, 0, out);
|
||||
@@ -309,10 +367,10 @@ static bool test_sha256() {
|
||||
0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad};
|
||||
CHECK(std::memcmp(out, want, 32) == 0);
|
||||
}
|
||||
// 型号标识:STATOR + 1 → "STATOR1" 补 '\0'
|
||||
// 型号标识:STATOR + 2 → "STATOR2" 补 '\0'
|
||||
char mid[compiler::kModelIdSize];
|
||||
compiler::fill_model_id("STATOR", 1, mid);
|
||||
CHECK(std::strncmp(mid, "STATOR1", 7) == 0);
|
||||
compiler::fill_model_id("STATOR", 2, mid);
|
||||
CHECK(std::strncmp(mid, "STATOR2", 7) == 0);
|
||||
CHECK(mid[7] == '\0');
|
||||
CHECK(mid[31] == '\0');
|
||||
return true;
|
||||
@@ -321,7 +379,6 @@ static bool test_sha256() {
|
||||
int main() {
|
||||
if (!test_positive()) return 1;
|
||||
if (!test_negative()) return 1;
|
||||
if (!test_positive_min()) return 1;
|
||||
if (!test_type_meta()) return 1;
|
||||
if (!test_sha256()) return 1;
|
||||
std::printf("machine_test: %d checks passed\n", g_checks);
|
||||
|
||||
Reference in New Issue
Block a user