mirror of
https://gitcode.com/JianFeeeee/LuaCangjia_api.git
synced 2026-09-21 01:18:10 +00:00
feat: 实现loadFunction预加载函数能力 + AI辅助编程标识
- 新增 loadFunction/callFunction/unloadFunction 预加载函数 API - 预加载语义:加载文件→顶层执行一次→返回函数存入Lua Registry→可多次调用 - 与管道模式(load/runScript)存储完全解耦:funcs[]+Registry vs pkgs[]+虚拟栈 - 新增错误码 5023/5024/5025 - 新增单元测试:仓颉侧 8 个 + C++ GTest 侧 11 个 - 本机以 Cangjie 1.1.0 实编译并通过功能运行验证 - README/doc 增加 AI 辅助编程标识及预加载函数模式说明
This commit is contained in:
6
test/scripts/func_add.lua
Normal file
6
test/scripts/func_add.lua
Normal file
@ -0,0 +1,6 @@
|
||||
-- func_add.lua: 顶层执行一次(计数),返回闭包函数供 callFunction 多次调用
|
||||
local callCount = 0
|
||||
return function(input)
|
||||
callCount = callCount + 1
|
||||
return "result: " .. input .. " (call #" .. callCount .. ")"
|
||||
end
|
||||
5
test/scripts/func_multi_call.lua
Normal file
5
test/scripts/func_multi_call.lua
Normal file
@ -0,0 +1,5 @@
|
||||
-- func_multi_call.lua: 顶层执行一次(初始化),返回无参闭包
|
||||
local init = "init-done"
|
||||
return function()
|
||||
return "multi:" .. init
|
||||
end
|
||||
2
test/scripts/func_not_a_function.lua
Normal file
2
test/scripts/func_not_a_function.lua
Normal file
@ -0,0 +1,2 @@
|
||||
-- func_not_a_function.lua: 顶层返回非函数,loadFunction 应报 5025
|
||||
return "not a function"
|
||||
5
test/scripts/func_runtime_error.lua
Normal file
5
test/scripts/func_runtime_error.lua
Normal file
@ -0,0 +1,5 @@
|
||||
-- func_runtime_error.lua: 返回会在运行时抛错的函数
|
||||
return function()
|
||||
local x = nil
|
||||
return x.nonexistent
|
||||
end
|
||||
@ -326,6 +326,166 @@ TEST_F(LuaCjApiTest, DoStringNilReturn) {
|
||||
EXPECT_EQ(get_errno(), NAPI_SCRIPT_BAD_RET);
|
||||
}
|
||||
|
||||
// ==================== loadfunction / callfunction 预加载测试 ====================
|
||||
|
||||
// 基础预加载 + 调用
|
||||
TEST_F(LuaCjApiTest, LoadFunctionAndCall) {
|
||||
int ret = loadfunction(runner, get_script_path("func_add.lua").c_str(), "add");
|
||||
EXPECT_EQ(ret, 0);
|
||||
ret = callfunction(runner, "add", "hello");
|
||||
EXPECT_EQ(ret, 0);
|
||||
EXPECT_STREQ(getresult(runner), "result: hello (call #1)");
|
||||
}
|
||||
|
||||
// 同一函数多次调用:顶层只执行一次,计数器持续累加
|
||||
TEST_F(LuaCjApiTest, CallFunctionMultipleTimes) {
|
||||
ASSERT_EQ(loadfunction(runner, get_script_path("func_add.lua").c_str(), "add"), 0);
|
||||
|
||||
EXPECT_EQ(callfunction(runner, "add", "a"), 0);
|
||||
EXPECT_STREQ(getresult(runner), "result: a (call #1)");
|
||||
|
||||
EXPECT_EQ(callfunction(runner, "add", "b"), 0);
|
||||
EXPECT_STREQ(getresult(runner), "result: b (call #2)");
|
||||
|
||||
EXPECT_EQ(callfunction(runner, "add", "c"), 0);
|
||||
EXPECT_STREQ(getresult(runner), "result: c (call #3)");
|
||||
}
|
||||
|
||||
// 无参函数多次调用
|
||||
TEST_F(LuaCjApiTest, CallFunctionNoArgMultipleTimes) {
|
||||
ASSERT_EQ(loadfunction(runner, get_script_path("func_multi_call.lua").c_str(), "multi"), 0);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
EXPECT_EQ(callfunction(runner, "multi", NULL), 0);
|
||||
EXPECT_STREQ(getresult(runner), "multi:init-done");
|
||||
}
|
||||
}
|
||||
|
||||
// 调用未预加载的函数
|
||||
TEST_F(LuaCjApiTest, CallFunctionNotFound) {
|
||||
int ret = callfunction(runner, "nonexistent", NULL);
|
||||
EXPECT_EQ(ret, -1);
|
||||
EXPECT_EQ(get_errno(), NAPI_FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
// 预加载不存在的文件
|
||||
TEST_F(LuaCjApiTest, LoadFunctionFileNotFound) {
|
||||
int ret = loadfunction(runner, "/no/such/file.lua", "bad");
|
||||
EXPECT_EQ(ret, -1);
|
||||
EXPECT_EQ(get_errno(), NAPI_LOAD_FILE_ERROR);
|
||||
}
|
||||
|
||||
// 预加载顶层未返回函数的文件
|
||||
TEST_F(LuaCjApiTest, LoadFunctionNotAFunction) {
|
||||
int ret = loadfunction(runner, get_script_path("func_not_a_function.lua").c_str(), "bad");
|
||||
EXPECT_EQ(ret, -1);
|
||||
EXPECT_EQ(get_errno(), NAPI_FUNCTION_NOT_VALID);
|
||||
}
|
||||
|
||||
// 预加载同名函数
|
||||
TEST_F(LuaCjApiTest, LoadFunctionDuplicate) {
|
||||
ASSERT_EQ(loadfunction(runner, get_script_path("func_add.lua").c_str(), "dup"), 0);
|
||||
int ret = loadfunction(runner, get_script_path("func_add.lua").c_str(), "dup");
|
||||
EXPECT_EQ(ret, -1);
|
||||
EXPECT_EQ(get_errno(), NAPI_FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
// 运行时错误
|
||||
TEST_F(LuaCjApiTest, CallFunctionRuntimeError) {
|
||||
ASSERT_EQ(loadfunction(runner, get_script_path("func_runtime_error.lua").c_str(), "bad"), 0);
|
||||
int ret = callfunction(runner, "bad", NULL);
|
||||
EXPECT_EQ(ret, -1);
|
||||
EXPECT_EQ(get_errno(), NAPI_SCRIPT_ERROR);
|
||||
}
|
||||
|
||||
// unloadfunction 后再调用应报 5023,且可重新加载
|
||||
TEST_F(LuaCjApiTest, CallFunctionWithUnloadFunction) {
|
||||
ASSERT_EQ(loadfunction(runner, get_script_path("func_add.lua").c_str(), "add"), 0);
|
||||
EXPECT_EQ(callfunction(runner, "add", "before"), 0);
|
||||
EXPECT_STREQ(getresult(runner), "result: before (call #1)");
|
||||
|
||||
EXPECT_EQ(unloadfunction(runner, "add"), 0);
|
||||
|
||||
int ret = callfunction(runner, "add", NULL);
|
||||
EXPECT_EQ(ret, -1);
|
||||
EXPECT_EQ(get_errno(), NAPI_FUNCTION_NOT_FOUND);
|
||||
|
||||
// 卸载后可重新预加载,计数器重置
|
||||
EXPECT_EQ(loadfunction(runner, get_script_path("func_add.lua").c_str(), "add"), 0);
|
||||
EXPECT_EQ(callfunction(runner, "add", "after"), 0);
|
||||
EXPECT_STREQ(getresult(runner), "result: after (call #1)");
|
||||
}
|
||||
|
||||
// 预加载与管道模式共存,互不干扰:
|
||||
// 预加载函数走独立的 funcs[]+Registry,管道走 pkgs[] 栈
|
||||
TEST_F(LuaCjApiTest, LoadFunctionAndPipelineNoInterference) {
|
||||
// 预加载一个函数(独立于管道栈)
|
||||
ASSERT_EQ(loadfunction(runner, get_script_path("func_add.lua").c_str(), "add"), 0);
|
||||
EXPECT_EQ(callfunction(runner, "add", "x"), 0);
|
||||
EXPECT_STREQ(getresult(runner), "result: x (call #1)");
|
||||
|
||||
// 管道模式照常(pkgs 栈)
|
||||
ASSERT_EQ(load_lib(runner, get_script_path("pipeline_add_suffix.lua").c_str(), "add_suffix"), 0);
|
||||
ASSERT_EQ(load_lib(runner, get_script_path("pipeline_to_upper.lua").c_str(), "to_upper"), 0);
|
||||
ASSERT_EQ(load_lib(runner, get_script_path("pipeline_add_prefix.lua").c_str(), "add_prefix"), 0);
|
||||
|
||||
int ret = run(runner, get_script_path("pipeline_generate_data.lua").c_str(), "hello");
|
||||
EXPECT_EQ(ret, 0);
|
||||
EXPECT_STREQ(getresult(runner), "hello");
|
||||
|
||||
ret = run(runner, nullptr, nullptr);
|
||||
EXPECT_EQ(ret, 0);
|
||||
EXPECT_STREQ(getresult(runner), "[PREFIX] hello");
|
||||
|
||||
ret = run(runner, nullptr, nullptr);
|
||||
EXPECT_EQ(ret, 0);
|
||||
EXPECT_STREQ(getresult(runner), "[PREFIX] HELLO");
|
||||
|
||||
ret = run(runner, nullptr, nullptr);
|
||||
EXPECT_EQ(ret, 0);
|
||||
EXPECT_STREQ(getresult(runner), "[PREFIX] HELLO [SUFFIX]");
|
||||
|
||||
// 管道跑完后,预加载函数仍可调用,计数器继续
|
||||
EXPECT_EQ(callfunction(runner, "add", "y"), 0);
|
||||
EXPECT_STREQ(getresult(runner), "result: y (call #2)");
|
||||
}
|
||||
|
||||
// 预加载数量上限:默认 MAX_LUA_FUNC=20
|
||||
TEST_F(LuaCjApiTest, LoadFunctionOverflow) {
|
||||
for (int i = 0; i < MAX_LUA_FUNC; i++) {
|
||||
char name[20];
|
||||
snprintf(name, sizeof(name), "func%d", i);
|
||||
std::string content = "return function() return '" + std::to_string(i) + "' end";
|
||||
std::string path = temp_dir + "/func" + std::to_string(i) + ".lua";
|
||||
FILE* f = fopen(path.c_str(), "w");
|
||||
ASSERT_NE(f, nullptr);
|
||||
fprintf(f, "%s", content.c_str());
|
||||
fclose(f);
|
||||
int ret = loadfunction(runner, path.c_str(), name);
|
||||
EXPECT_EQ(ret, 0);
|
||||
}
|
||||
|
||||
// 第 21 个应报 5024
|
||||
std::string path = temp_dir + "/overflow.lua";
|
||||
FILE* f = fopen(path.c_str(), "w");
|
||||
ASSERT_NE(f, nullptr);
|
||||
fprintf(f, "return function() return 'overflow' end");
|
||||
fclose(f);
|
||||
int ret = loadfunction(runner, path.c_str(), "overflow");
|
||||
EXPECT_EQ(ret, -1);
|
||||
EXPECT_EQ(get_errno(), NAPI_LOAD_FUNCTION_OVER);
|
||||
}
|
||||
|
||||
// cleanup 后预加载函数应失效
|
||||
TEST_F(LuaCjApiTest, CallFunctionAfterCleanup) {
|
||||
ASSERT_EQ(loadfunction(runner, get_script_path("func_add.lua").c_str(), "add"), 0);
|
||||
EXPECT_EQ(callfunction(runner, "add", "x"), 0);
|
||||
EXPECT_EQ(cleanup(runner), 0);
|
||||
|
||||
int ret = callfunction(runner, "add", NULL);
|
||||
EXPECT_EQ(ret, -1);
|
||||
EXPECT_EQ(get_errno(), NAPI_FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
|
||||
Reference in New Issue
Block a user