Files
Interpreter/tests/src/machine_test.cpp
T
Admin a308ae713d 阶段 B 步骤 6:.stb 写侧新格式(头 104 + 型号标识 + SHA-256 文件尾)。
- Stb:kHeaderSize 104、kSha256Size 32;SHA-256 实现(FIPS 180-4,含标准测试向量验证);
  fill_model_id(name+version 拼 32 字节补 '\0');StbView 解析新格式 +
  model_id()/model_matches()/sha_ok()(数据段不含尾、缺尾报错)
- Codegen:assemble_image 写型号标识(偏移 72)+ 文件尾 SHA-256
- main.cpp:--disasm 显示 model/sha;写后自检(ok+sha+型号匹配,失败报错)
- 测试:machine_test +SHA-256 已知向量(空串/abc)与型号标识断言(53 断言);
  codegen_test data_len 断言 +32(SHA 尾);ctest 12/12
- 验证:line1 288 字节 model=STATOR1 sha=ok;篡改一字节 sha=BAD
2026-08-21 22:20:35 +08:00

330 lines
14 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file machine_test.cpp
* @brief machine.toml 加载与强校验测试(阶段 A 步骤 3)
* @author
* @date 2026-08-21
*/
#include <cstdio>
#include <cstring>
#include <filesystem>
#include <string>
#include "compiler/MachineConfig.h"
#include "compiler/Stb.h"
#include "compiler/TypeInfo.h"
#ifndef REPO_ROOT
#define REPO_ROOT "."
#endif
static int g_checks = 0;
#define CHECK(cond) \
do { \
if (!(cond)) { \
std::printf("FAIL %s:%d: %s\n", __FILE__, __LINE__, #cond); \
return false; \
} \
++g_checks; \
} while (0)
// ---- 写临时 toml ----
static std::string write_tmp(const char* name, const char* content) {
const std::filesystem::path dir = std::filesystem::temp_directory_path();
const std::filesystem::path path = dir / name;
std::FILE* f = std::fopen(path.string().c_str(), "w");
std::fputs(content, f);
std::fclose(f);
return path.string();
}
static bool expect_load_err(const char* name, const char* content, const char* keyword) {
const std::string path = write_tmp(name, content);
compiler::MachineConfig cfg;
std::string err;
if (cfg.load(path, &err)) {
std::printf("FAIL %s: loaded ok\n", name);
std::remove(path.c_str());
return false;
}
if (err.find("machine error") != 0) {
std::printf("FAIL %s: want 'machine error', got '%s'\n", name, err.c_str());
std::remove(path.c_str());
return false;
}
if (err.find(keyword) == std::string::npos) {
std::printf("FAIL %s: want '%s', got '%s'\n", name, keyword, err.c_str());
std::remove(path.c_str());
return false;
}
++g_checks;
std::remove(path.c_str());
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";
// ---- 1. 正例:仓库 machine.toml ----
static bool test_positive() {
compiler::MachineConfig cfg;
std::string err;
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.types().size() == 3);
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(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.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(compiler::MachineConfig::find_prim("int16") != nullptr);
const compiler::PrimType* p = compiler::MachineConfig::find_prim("uint8");
CHECK(p != nullptr && p->width == 1 && !p->is_signed && !p->is_float);
CHECK(compiler::MachineConfig::find_prim("float64") != nullptr);
CHECK(compiler::MachineConfig::find_prim("bfloat16") == nullptr);
return true;
}
// ---- 2. 负例:9 类 ----
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'")) {
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;
}
// 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;
}
// 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;
}
// 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;
}
// 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;
}
// 类别-格式互锁破坏(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;
}
// 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;
}
// 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;
}
// 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;
}
// 语法错误
if (!expect_load_err("mc_bad11.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----
static bool test_type_meta() {
compiler::MachineConfig cfg;
std::string err;
CHECK(cfg.load(std::string(REPO_ROOT) + "/compiler/machine.toml", &err));
// BOOLbase=uint8 → 1 字节、无符号、非浮点、tag 0、range [0,1]
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));
// 大小写不敏感(Linker type_name 是小写)
const compiler::TypeMeta b2 = compiler::type_meta(cfg, "bool");
CHECK(b2 && b2.tag() == 0 && b2.width() == 1);
// INTint16 → 2 字节、有符号、tag 1、无 range
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));
// TIMEint64 → 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);
// 未定义类型 → 空
CHECK(!compiler::type_meta(cfg, "REAL"));
CHECK(!compiler::type_meta(cfg, ""));
return true;
}
// ---- 5. SHA-256 已知向量 + 型号标识 ----
static bool test_sha256() {
// 标准测试向量
{
uint8_t out[compiler::kSha256Size];
compiler::sha256(nullptr, 0, out);
const uint8_t want[32] = {0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14,
0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24,
0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c,
0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55};
CHECK(std::memcmp(out, want, 32) == 0);
}
{
const uint8_t abc[] = {'a', 'b', 'c'};
uint8_t out[compiler::kSha256Size];
compiler::sha256(abc, 3, out);
const uint8_t want[32] = {0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad};
CHECK(std::memcmp(out, want, 32) == 0);
}
// 型号标识:STATOR + 1 → "STATOR1" 补 '\0'
char mid[compiler::kModelIdSize];
compiler::fill_model_id("STATOR", 1, mid);
CHECK(std::strncmp(mid, "STATOR1", 7) == 0);
CHECK(mid[7] == '\0');
CHECK(mid[31] == '\0');
return true;
}
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);
return 0;
}