diff --git a/README.md b/README.md index 5cb2bfc..9869a4d 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,14 @@ # Lua Runner for Cangjie -[![License](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](LICENSE) +[![License](https://img.shields.io/badge/License-LGPL%20v3-blue.svg)](LICENSE) [![Lua](https://img.shields.io/badge/Lua-5.4-blue)](https://www.lua.org/) [![Cangjie](https://img.shields.io/badge/Cangjie-SDK-orange)](https://cangjie-lang.cn/) Lua Runner for Cangjie 是一个专为仓颉语言设计的轻量级、高性能 Lua 脚本执行引擎。它通过 C FFI 桥接 C++,提供了稳定且易用的 Lua 虚拟机管理能力。 **注:本项目是开发原生鸿蒙应用时产生的副产物,当前版本依然存在局限性与不足,请详细检查后再使用。** +安全与发布说明:仓库中不应提交任何中心仓访问令牌、私有凭据或其他敏感配置。发布前请使用本地环境变量、用户级配置或 CI 密钥注入方式提供认证信息,并检查待上传制品中不包含明文凭据。 + 除了基础的脚本嵌入功能外,该引擎的核心特色在于支持一种独特的 **“栈式管道执行模式”**,能够实现脚本间的隐式参数传递,非常适合构建数据处理管道、游戏脚本系统或插件化架构。同时,它提供了完善的异常处理机制和灵活的 I/O 重定向功能。 ## 特性 @@ -180,7 +182,12 @@ println(finalResult) // 最终输出: [PREFIX] HELLO [SUFFIX] - **容量限制**:内部使用固定数组管理加载的库,上限为 **20 个**,超过将报错 `5016`。 - **I/O 性能**:I/O 重定向依赖于文件系统交换,相对于纯内存交互存在微小的性能开销。 +## 第三方组件与合规说明 + +- 本项目仓库内包含 Lua 5.4.8 源码副本,用于构建底层原生运行时。 +- Lua 5.4.8 使用 MIT License;其原始版权与许可声明可在 `lib/lua/` 对应源码中查看。 +- 本项目自身以 `LGPL-3.0-or-later` 方式发布,使用或分发时请同时遵守项目本身以及所包含第三方组件的许可证要求。 + ## 许可证 -本项目基于 GPLv3 协议开源。这意味着您可以自由地使用、修改和分发本软件,但任何衍生作品也必须以相同的 GPLv3 协议开源。详情请参阅 [LICENSE](LICENSE) 文件。 - +本项目基于 LGPL-3.0-or-later 协议开源。仓库同时包含 Lua 5.4.8 的 MIT Licensed 源码副本,用于构建底层原生运行时。分发和集成时,请一并检查 [LICENSE](LICENSE) 以及 `lib/lua/` 中随源码附带的许可声明。 diff --git a/cjpm.toml b/cjpm.toml index 729d430..da3053c 100644 --- a/cjpm.toml +++ b/cjpm.toml @@ -3,7 +3,7 @@ name = "luarunner" version = "0.0.1" description = "Lite and High performance LuaRunner" cjc-version = "1.1.0" -license = "GPLv3" +license = "LGPL-3.0-or-later" output-type ="dynamic" [dependencies] diff --git a/src/lua_runner_test.cj b/src/lua_runner_test.cj index 5f461b4..6c74176 100644 --- a/src/lua_runner_test.cj +++ b/src/lua_runner_test.cj @@ -4,6 +4,22 @@ import std.unittest.* import std.fs.* let scriptsDir = "./test/scripts" +let generatedScriptsDir = "./test/generated_scripts" + +func scriptPath(name: String): String { + scriptsDir + "/" + name +} + +func generatedScriptPath(name: String): String { + generatedScriptsDir + "/" + name +} + +func ensureGeneratedScriptsDir(): Unit { + let dir = Path(generatedScriptsDir) + if (!exists(dir)) { + Directory.create(dir, recursive: true) + } +} // ==================== // 1. 基础运行与参数测试 @@ -12,7 +28,7 @@ let scriptsDir = "./test/scripts" @Test func testRunScript(): Unit { let runner = LuaRunner() - let path = scriptsDir + "/simple_return.lua" + let path = scriptPath("simple_return.lua") let res = runner.runScript(path, "") @Expect(res, "hello") } @@ -20,7 +36,7 @@ func testRunScript(): Unit { @Test func testRunScriptWithArg(): Unit { let runner = LuaRunner() - let path = scriptsDir + "/args_test.lua" + let path = scriptPath("args_test.lua") let res = runner.runScript(path, "world") @Expect(res, "world") } @@ -32,7 +48,7 @@ func testRunScriptWithArg(): Unit { @Test func testRunScriptSyntaxError(): Unit { let runner = LuaRunner() - let path = scriptsDir + "/syntax_error.lua" + let path = scriptPath("syntax_error.lua") try { runner.runScript(path, "") fail("Should throw syntax error") @@ -44,7 +60,7 @@ func testRunScriptSyntaxError(): Unit { @Test func testRunScriptRuntimeError(): Unit { let runner = LuaRunner() - let path = scriptsDir + "/runtime_error.lua" + let path = scriptPath("runtime_error.lua") try { runner.runScript(path, "") fail("Should throw runtime error") @@ -60,7 +76,7 @@ func testRunScriptRuntimeError(): Unit { @Test func testLoadLib(): Unit { let runner = LuaRunner() - let path = scriptsDir + "/mylib.lua" + let path = scriptPath("mylib.lua") runner.load(path, "mylib") } @@ -78,7 +94,7 @@ func testLoadLibFileNotFound(): Unit { @Test func testUnloadLib(): Unit { let runner = LuaRunner() - let path = scriptsDir + "/mylib.lua" + let path = scriptPath("mylib.lua") runner.load(path, "mylib") runner.unload("mylib") } @@ -101,8 +117,8 @@ func testUnloadLibNotFound(): Unit { @Test func testCleanup(): Unit { let runner = LuaRunner() - let setPath = scriptsDir + "/set_global.lua" - let getPath = scriptsDir + "/get_global.lua" + let setPath = scriptPath("set_global.lua") + let getPath = scriptPath("get_global.lua") runner.runScript(setPath, "") runner.clear() @@ -113,14 +129,92 @@ func testCleanup(): Unit { @Test func testCrossCallDataPassing(): Unit { let runner = LuaRunner() - let setPath = scriptsDir + "/cross_call_set.lua" - let getPath = scriptsDir + "/cross_call_get.lua" + let setPath = scriptPath("cross_call_set.lua") + let getPath = scriptPath("cross_call_get.lua") runner.runScript(setPath, "") let res = runner.runScript(getPath, "") @Expect(res, "1,2,3") } +@Test +func testPipelineDocumentedFlow(): Unit { + let runner = LuaRunner() + runner.load(scriptPath("pipeline_add_suffix.lua"), "add_suffix") + .load(scriptPath("pipeline_to_upper.lua"), "to_upper") + .load(scriptPath("pipeline_add_prefix.lua"), "add_prefix") + + let initialData = runner.runScript(scriptPath("pipeline_generate_data.lua"), "hello") + @Expect(initialData, "hello") + + let afterA = runner.runScript("", "") + @Expect(afterA, "[PREFIX] hello") + + let afterB = runner.runScript("", "") + @Expect(afterB, "[PREFIX] HELLO") + + let finalResult = runner.runScript("", "") + @Expect(finalResult, "[PREFIX] HELLO [SUFFIX]") +} + +@Test +func testPipelineStepByStepResults(): Unit { + let runner = LuaRunner() + runner.load(scriptPath("pipeline_add_suffix.lua"), "add_suffix") + .load(scriptPath("pipeline_to_upper.lua"), "to_upper") + .load(scriptPath("pipeline_add_prefix.lua"), "add_prefix") + + let initialData = runner.runScript(scriptPath("pipeline_generate_data.lua"), "hello") + @Expect(initialData, "hello") + + let afterPrefix = runner.runScript("", "") + @Expect(afterPrefix, "[PREFIX] hello") + + let afterUpper = runner.runScript("", "") + @Expect(afterUpper, "[PREFIX] HELLO") + + let afterSuffix = runner.runScript("", "") + @Expect(afterSuffix, "[PREFIX] HELLO [SUFFIX]") +} + +@Test +func testLoadLibOverflow(): Unit { + ensureGeneratedScriptsDir() + let runner = LuaRunner() + + for (i in 0..20) { + let name = "overflow_lib_${i}" + let path = generatedScriptPath(name + ".lua") + File.writeTo(path, ("return \"" + name + "\"").toArray()) + runner.load(path, name) + } + + let overflowName = "overflow_lib_20" + let overflowPath = generatedScriptPath(overflowName + ".lua") + File.writeTo(overflowPath, "return \"overflow\"".toArray()) + + try { + runner.load(overflowPath, overflowName) + fail("Should throw overflow error") + } catch (e: LuaError) { + @Expect(e.code, 5016) + } +} + +@Test +func testPackagePathConfig(): Unit { + let runner = LuaRunner(pkgpath: scriptsDir + "/pkgmods/?.lua") + let res = runner.runScript(scriptPath("pkgpath_require_mylib.lua"), "") + @Expect(res, "LoadedViaPkgPath") +} + +@Test +func testPackagePathInitLuaPattern(): Unit { + let runner = LuaRunner(pkgpath: scriptsDir + "/pkgmods_init/?/init.lua") + let res = runner.runScript(scriptPath("pkgpath_require_nested.lua"), "") + @Expect(res, "LoadedViaInit") +} + // ==================== // 5. I/O 重定向测试 // ==================== @@ -128,14 +222,14 @@ func testCrossCallDataPassing(): Unit { @Test func testRedirectPrint(): Unit { let runner = LuaRunner(pathio: "/tmp") - let path = scriptsDir + "/print_test.lua" + let path = scriptPath("print_test.lua") runner.runScript(path, "") } @Test func testRedirectIoWrite(): Unit { let runner = LuaRunner(pathio: "/tmp") - let path = scriptsDir + "/io_write_test.lua" + let path = scriptPath("io_write_test.lua") runner.runScript(path, "") } @@ -151,7 +245,7 @@ func testRedirectIoRead(): Unit { // 2. 运行测试 let runner = LuaRunner(pathio: "/tmp") - let path = scriptsDir + "/io_read_test.lua" + let path = scriptPath("io_read_test.lua") let res = runner.runScript(path, "") @Expect(res, "MockInputData") diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3e2ec8c..d1fef00 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -3,6 +3,9 @@ project(LuaCjApiTest) set(CMAKE_CXX_STANDARD 17) +set(SCRIPT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/scripts" CACHE PATH "Path to Lua test scripts") +add_compile_definitions(SCRIPT_DIR="${SCRIPT_DIR}") + # ==================== 核心修改 ==================== # 1. 使用 add_subdirectory 直接包含 lib 源码目录 # 这会自动继承 lib 中定义的 luacjapi 目标及其依赖路径 diff --git a/test/generated_scripts/overflow_lib_0.lua b/test/generated_scripts/overflow_lib_0.lua new file mode 100644 index 0000000..42a377b --- /dev/null +++ b/test/generated_scripts/overflow_lib_0.lua @@ -0,0 +1 @@ +return "overflow_lib_0" \ No newline at end of file diff --git a/test/generated_scripts/overflow_lib_1.lua b/test/generated_scripts/overflow_lib_1.lua new file mode 100644 index 0000000..5bff305 --- /dev/null +++ b/test/generated_scripts/overflow_lib_1.lua @@ -0,0 +1 @@ +return "overflow_lib_1" \ No newline at end of file diff --git a/test/generated_scripts/overflow_lib_10.lua b/test/generated_scripts/overflow_lib_10.lua new file mode 100644 index 0000000..8559643 --- /dev/null +++ b/test/generated_scripts/overflow_lib_10.lua @@ -0,0 +1 @@ +return "overflow_lib_10" \ No newline at end of file diff --git a/test/generated_scripts/overflow_lib_11.lua b/test/generated_scripts/overflow_lib_11.lua new file mode 100644 index 0000000..3e5c769 --- /dev/null +++ b/test/generated_scripts/overflow_lib_11.lua @@ -0,0 +1 @@ +return "overflow_lib_11" \ No newline at end of file diff --git a/test/generated_scripts/overflow_lib_12.lua b/test/generated_scripts/overflow_lib_12.lua new file mode 100644 index 0000000..fe55cfe --- /dev/null +++ b/test/generated_scripts/overflow_lib_12.lua @@ -0,0 +1 @@ +return "overflow_lib_12" \ No newline at end of file diff --git a/test/generated_scripts/overflow_lib_13.lua b/test/generated_scripts/overflow_lib_13.lua new file mode 100644 index 0000000..aef197a --- /dev/null +++ b/test/generated_scripts/overflow_lib_13.lua @@ -0,0 +1 @@ +return "overflow_lib_13" \ No newline at end of file diff --git a/test/generated_scripts/overflow_lib_14.lua b/test/generated_scripts/overflow_lib_14.lua new file mode 100644 index 0000000..fc3485f --- /dev/null +++ b/test/generated_scripts/overflow_lib_14.lua @@ -0,0 +1 @@ +return "overflow_lib_14" \ No newline at end of file diff --git a/test/generated_scripts/overflow_lib_15.lua b/test/generated_scripts/overflow_lib_15.lua new file mode 100644 index 0000000..8a43440 --- /dev/null +++ b/test/generated_scripts/overflow_lib_15.lua @@ -0,0 +1 @@ +return "overflow_lib_15" \ No newline at end of file diff --git a/test/generated_scripts/overflow_lib_16.lua b/test/generated_scripts/overflow_lib_16.lua new file mode 100644 index 0000000..b65d4d8 --- /dev/null +++ b/test/generated_scripts/overflow_lib_16.lua @@ -0,0 +1 @@ +return "overflow_lib_16" \ No newline at end of file diff --git a/test/generated_scripts/overflow_lib_17.lua b/test/generated_scripts/overflow_lib_17.lua new file mode 100644 index 0000000..8180d1e --- /dev/null +++ b/test/generated_scripts/overflow_lib_17.lua @@ -0,0 +1 @@ +return "overflow_lib_17" \ No newline at end of file diff --git a/test/generated_scripts/overflow_lib_18.lua b/test/generated_scripts/overflow_lib_18.lua new file mode 100644 index 0000000..f281621 --- /dev/null +++ b/test/generated_scripts/overflow_lib_18.lua @@ -0,0 +1 @@ +return "overflow_lib_18" \ No newline at end of file diff --git a/test/generated_scripts/overflow_lib_19.lua b/test/generated_scripts/overflow_lib_19.lua new file mode 100644 index 0000000..28d6480 --- /dev/null +++ b/test/generated_scripts/overflow_lib_19.lua @@ -0,0 +1 @@ +return "overflow_lib_19" \ No newline at end of file diff --git a/test/generated_scripts/overflow_lib_2.lua b/test/generated_scripts/overflow_lib_2.lua new file mode 100644 index 0000000..10debe8 --- /dev/null +++ b/test/generated_scripts/overflow_lib_2.lua @@ -0,0 +1 @@ +return "overflow_lib_2" \ No newline at end of file diff --git a/test/generated_scripts/overflow_lib_20.lua b/test/generated_scripts/overflow_lib_20.lua new file mode 100644 index 0000000..8139bd1 --- /dev/null +++ b/test/generated_scripts/overflow_lib_20.lua @@ -0,0 +1 @@ +return "overflow" \ No newline at end of file diff --git a/test/generated_scripts/overflow_lib_3.lua b/test/generated_scripts/overflow_lib_3.lua new file mode 100644 index 0000000..ee56516 --- /dev/null +++ b/test/generated_scripts/overflow_lib_3.lua @@ -0,0 +1 @@ +return "overflow_lib_3" \ No newline at end of file diff --git a/test/generated_scripts/overflow_lib_4.lua b/test/generated_scripts/overflow_lib_4.lua new file mode 100644 index 0000000..3054229 --- /dev/null +++ b/test/generated_scripts/overflow_lib_4.lua @@ -0,0 +1 @@ +return "overflow_lib_4" \ No newline at end of file diff --git a/test/generated_scripts/overflow_lib_5.lua b/test/generated_scripts/overflow_lib_5.lua new file mode 100644 index 0000000..d7d3462 --- /dev/null +++ b/test/generated_scripts/overflow_lib_5.lua @@ -0,0 +1 @@ +return "overflow_lib_5" \ No newline at end of file diff --git a/test/generated_scripts/overflow_lib_6.lua b/test/generated_scripts/overflow_lib_6.lua new file mode 100644 index 0000000..9b98577 --- /dev/null +++ b/test/generated_scripts/overflow_lib_6.lua @@ -0,0 +1 @@ +return "overflow_lib_6" \ No newline at end of file diff --git a/test/generated_scripts/overflow_lib_7.lua b/test/generated_scripts/overflow_lib_7.lua new file mode 100644 index 0000000..62f53f0 --- /dev/null +++ b/test/generated_scripts/overflow_lib_7.lua @@ -0,0 +1 @@ +return "overflow_lib_7" \ No newline at end of file diff --git a/test/generated_scripts/overflow_lib_8.lua b/test/generated_scripts/overflow_lib_8.lua new file mode 100644 index 0000000..8067fe3 --- /dev/null +++ b/test/generated_scripts/overflow_lib_8.lua @@ -0,0 +1 @@ +return "overflow_lib_8" \ No newline at end of file diff --git a/test/generated_scripts/overflow_lib_9.lua b/test/generated_scripts/overflow_lib_9.lua new file mode 100644 index 0000000..1ebee3a --- /dev/null +++ b/test/generated_scripts/overflow_lib_9.lua @@ -0,0 +1 @@ +return "overflow_lib_9" \ No newline at end of file diff --git a/test/scripts/pipeline_add_prefix.lua b/test/scripts/pipeline_add_prefix.lua new file mode 100644 index 0000000..543c050 --- /dev/null +++ b/test/scripts/pipeline_add_prefix.lua @@ -0,0 +1,2 @@ +local input = ... +return "[PREFIX] " .. input diff --git a/test/scripts/pipeline_add_suffix.lua b/test/scripts/pipeline_add_suffix.lua new file mode 100644 index 0000000..1bd914a --- /dev/null +++ b/test/scripts/pipeline_add_suffix.lua @@ -0,0 +1,2 @@ +local input = ... +return input .. " [SUFFIX]" diff --git a/test/scripts/pipeline_generate_data.lua b/test/scripts/pipeline_generate_data.lua new file mode 100644 index 0000000..01f02dc --- /dev/null +++ b/test/scripts/pipeline_generate_data.lua @@ -0,0 +1,2 @@ +local input = ... +return input diff --git a/test/scripts/pipeline_to_upper.lua b/test/scripts/pipeline_to_upper.lua new file mode 100644 index 0000000..f82b7a8 --- /dev/null +++ b/test/scripts/pipeline_to_upper.lua @@ -0,0 +1,2 @@ +local input = ... +return string.upper(input) diff --git a/test/scripts/pkgmods/mylib.lua b/test/scripts/pkgmods/mylib.lua new file mode 100644 index 0000000..22b1af5 --- /dev/null +++ b/test/scripts/pkgmods/mylib.lua @@ -0,0 +1 @@ +return "LoadedViaPkgPath" diff --git a/test/scripts/pkgmods_init/nestedpkg/init.lua b/test/scripts/pkgmods_init/nestedpkg/init.lua new file mode 100644 index 0000000..997a78b --- /dev/null +++ b/test/scripts/pkgmods_init/nestedpkg/init.lua @@ -0,0 +1 @@ +return "LoadedViaInit" diff --git a/test/scripts/pkgpath_require_mylib.lua b/test/scripts/pkgpath_require_mylib.lua new file mode 100644 index 0000000..1bd0463 --- /dev/null +++ b/test/scripts/pkgpath_require_mylib.lua @@ -0,0 +1 @@ +return require("mylib") diff --git a/test/scripts/pkgpath_require_nested.lua b/test/scripts/pkgpath_require_nested.lua new file mode 100644 index 0000000..fe6dd42 --- /dev/null +++ b/test/scripts/pkgpath_require_nested.lua @@ -0,0 +1 @@ +return require("nestedpkg") diff --git a/test/test_lua_cj_api.cpp b/test/test_lua_cj_api.cpp index fb29c1f..b8b439f 100644 --- a/test/test_lua_cj_api.cpp +++ b/test/test_lua_cj_api.cpp @@ -7,22 +7,17 @@ #include #include #include -#include // 新增:用于异常处理 +#include extern "C" { #include "lua_cj_api.h" #include "errors.h" } -// 辅助函数:创建临时目录 static std::string create_temp_dir() { - // 修正点: - // 1. mkdtemp 要求模板字符串最后6个字符必须是 'XXXXXX' - // 2. 使用 /tmp 目录确保路径存在,避免 "test/" 目录不存在导致的失败 - char tmpl[] = "/tmp/lua_cj_test_XXXXXX"; + char tmpl[] = "/tmp/lua_cj_test_XXXXXX"; char *dir = mkdtemp(tmpl); - // 修正点:检查 mkdtemp 是否成功,防止传入 NULL 给 std::string 导致崩溃 if (dir == nullptr) { perror("mkdtemp failed"); throw std::runtime_error("Failed to create temp directory"); @@ -31,15 +26,17 @@ static std::string create_temp_dir() { return std::string(dir); } -// 全局计数器,用于验证回调是否被调用 static int callback_invoke_count = 0; -// 模拟输入/输出回调 static int mock_callback() { callback_invoke_count++; return 0; } +static std::string get_script_path(const std::string& script_name) { + return std::string(SCRIPT_DIR) + "/" + script_name; +} + class LuaCjApiTest : public ::testing::Test { protected: void* runner; @@ -47,10 +44,7 @@ protected: void SetUp() override { temp_dir = create_temp_dir(); - callback_invoke_count = 0; // 重置计数器 - - // 修改:传入 temp_dir 作为 pathio,确保 I/O 重定向路径有效 - // 仓颉层封装中 pathio 和 pkgpath 都是可配置的,这里测试基础情况 + callback_invoke_count = 0; runner = init_lua_runner(temp_dir.c_str(), NULL, mock_callback, mock_callback); ASSERT_NE(runner, nullptr) << "init_lua_runner failed, errno=" << get_errno(); } @@ -63,16 +57,7 @@ protected: std::string cmd = "rm -rf " + temp_dir; system(cmd.c_str()); } - - void write_lua(const std::string& filename, const std::string& content) { - std::string path = temp_dir + "/" + filename; - FILE* f = fopen(path.c_str(), "w"); - ASSERT_NE(f, nullptr); - fprintf(f, "%s", content.c_str()); - fclose(f); - } - // 辅助函数:读取文件内容 std::string read_file(const std::string& filename) { std::string path = temp_dir + "/" + filename; std::ifstream t(path); @@ -89,8 +74,7 @@ TEST_F(LuaCjApiTest, InitFree) { // 测试加载库 TEST_F(LuaCjApiTest, LoadLib) { - write_lua("mylib.lua", "function foo() return 42 end"); - int ret = load_lib(runner, (temp_dir + "/mylib.lua").c_str(), "mylib"); + int ret = load_lib(runner, get_script_path("mylib.lua").c_str(), "mylib"); EXPECT_EQ(ret, 0); } @@ -106,14 +90,20 @@ TEST_F(LuaCjApiTest, LoadLibOverflow) { for (int i = 0; i < MAX_LUA_LIB; ++i) { char name[20]; snprintf(name, sizeof(name), "lib%d", i); - std::string fname = std::string(name) + ".lua"; - write_lua(fname, "return {}"); - std::string path = temp_dir + "/" + fname; + std::string content = "return " + std::to_string(i); + std::string path = temp_dir + "/" + std::string(name) + ".lua"; + FILE* f = fopen(path.c_str(), "w"); + ASSERT_NE(f, nullptr); + fprintf(f, "%s", content.c_str()); + fclose(f); int ret = load_lib(runner, path.c_str(), name); EXPECT_EQ(ret, 0); } - write_lua("extra.lua", "return {}"); std::string path = temp_dir + "/extra.lua"; + FILE* f = fopen(path.c_str(), "w"); + ASSERT_NE(f, nullptr); + fprintf(f, "return {}"); + fclose(f); int ret = load_lib(runner, path.c_str(), "extra"); EXPECT_EQ(ret, -1); EXPECT_EQ(get_errno(), NAPI_LUALIB_LOAD_OVER_STACK); @@ -121,9 +111,7 @@ TEST_F(LuaCjApiTest, LoadLibOverflow) { // 测试卸载库 TEST_F(LuaCjApiTest, UnloadLib) { - write_lua("mylib.lua", "function foo() end"); - std::string path = temp_dir + "/mylib.lua"; - ASSERT_EQ(load_lib(runner, path.c_str(), "mylib"), 0); + ASSERT_EQ(load_lib(runner, get_script_path("mylib.lua").c_str(), "mylib"), 0); int ret = unload_lib(runner, "mylib"); EXPECT_EQ(ret, 0); } @@ -137,8 +125,7 @@ TEST_F(LuaCjApiTest, UnloadLibNotFound) { // 测试运行脚本 TEST_F(LuaCjApiTest, RunScript) { - write_lua("test.lua", "return 'hello'"); - int ret = run(runner, (temp_dir + "/test.lua").c_str(), nullptr); + int ret = run(runner, get_script_path("simple_return.lua").c_str(), nullptr); EXPECT_EQ(ret, 0); char* res = getresult(runner); EXPECT_STREQ(res, "hello"); @@ -146,12 +133,7 @@ TEST_F(LuaCjApiTest, RunScript) { // 测试带参数的脚本 TEST_F(LuaCjApiTest, RunScriptWithArg) { - // 修正点:Lua 中 ... 不能作为变量名。 - // 使用 select(1, ...) 或者直接 return ... - // 这里使用 "return (...)" 返回第一个参数 - write_lua("test.lua", "return (...)"); - - int ret = run(runner, (temp_dir + "/test.lua").c_str(), "world"); + int ret = run(runner, get_script_path("args_test.lua").c_str(), "world"); EXPECT_EQ(ret, 0); char* res = getresult(runner); EXPECT_STREQ(res, "world"); @@ -160,8 +142,7 @@ TEST_F(LuaCjApiTest, RunScriptWithArg) { // 测试脚本语法错误 TEST_F(LuaCjApiTest, RunScriptSyntaxError) { - write_lua("bad.lua", "for i=1,10 print(i) end"); - int ret = run(runner, (temp_dir + "/bad.lua").c_str(), NULL); + int ret = run(runner, get_script_path("syntax_error.lua").c_str(), NULL); EXPECT_EQ(ret, -1); EXPECT_EQ(get_errno(), NAPI_SCRIPT_ERROR); char* res = getresult(runner); @@ -170,8 +151,7 @@ TEST_F(LuaCjApiTest, RunScriptSyntaxError) { // 测试脚本运行时错误 TEST_F(LuaCjApiTest, RunScriptRuntimeError) { - write_lua("bad.lua", "error('oops')"); - int ret = run(runner, (temp_dir + "/bad.lua").c_str(), nullptr); + int ret = run(runner, get_script_path("runtime_error.lua").c_str(), nullptr); EXPECT_EQ(ret, -1); EXPECT_EQ(get_errno(), NAPI_SCRIPT_ERROR); char* res = getresult(runner); @@ -180,12 +160,10 @@ TEST_F(LuaCjApiTest, RunScriptRuntimeError) { // 测试 cleanup 重置状态 TEST_F(LuaCjApiTest, Cleanup) { - write_lua("set.lua", "g = 123"); - ASSERT_EQ(run(runner, (temp_dir + "/set.lua").c_str(), nullptr), 0); + ASSERT_EQ(run(runner, get_script_path("set_global.lua").c_str(), nullptr), 0); int ret = cleanup(runner); EXPECT_EQ(ret, 0); - write_lua("get.lua", "return tostring(g or 'nil')"); - ret = run(runner, (temp_dir + "/get.lua").c_str(), nullptr); + ret = run(runner, get_script_path("get_global.lua").c_str(), nullptr); EXPECT_EQ(ret, 0); char* res = getresult(runner); EXPECT_STREQ(res, "nil"); @@ -193,10 +171,8 @@ TEST_F(LuaCjApiTest, Cleanup) { // 测试跨调用数据传递 TEST_F(LuaCjApiTest, CrossCallDataPassing) { - write_lua("set.lua", "data = {1,2,3}"); - ASSERT_EQ(run(runner, (temp_dir + "/set.lua").c_str(), nullptr), 0); - write_lua("get.lua", "return table.concat(data, ',')"); - ASSERT_EQ(run(runner, (temp_dir + "/get.lua").c_str(), nullptr), 0); + ASSERT_EQ(run(runner, get_script_path("cross_call_set.lua").c_str(), nullptr), 0); + ASSERT_EQ(run(runner, get_script_path("cross_call_get.lua").c_str(), nullptr), 0); char* res = getresult(runner); EXPECT_STREQ(res, "1,2,3"); } @@ -207,9 +183,11 @@ TEST_F(LuaCjApiTest, MultipleLoadUnload) { for (int i = 0; i < 5; ++i) { char name[20]; snprintf(name, sizeof(name), "L%d", i); - std::string fname = std::string(name) + ".lua"; - write_lua(fname, "return " + std::to_string(i)); - std::string path = temp_dir + "/" + fname; + std::string path = temp_dir + "/" + std::string(name) + ".lua"; + FILE* f = fopen(path.c_str(), "w"); + ASSERT_NE(f, nullptr); + fprintf(f, "return %d", i); + fclose(f); ASSERT_EQ(load_lib(runner, path.c_str(), name), 0); } @@ -219,12 +197,15 @@ TEST_F(LuaCjApiTest, MultipleLoadUnload) { ASSERT_EQ(unload_lib(runner, name), 0); for (int j = 0; j < i; ++j) { - char libname[20], scriptname[20]; + char libname[20]; snprintf(libname, sizeof(libname), "L%d", j); - snprintf(scriptname, sizeof(scriptname), "check%d.lua", j); std::string script = "return require '" + std::string(libname) + "'"; - write_lua(scriptname, script); - int ret = run(runner, (temp_dir + "/" + scriptname).c_str(), nullptr); + std::string scriptname = temp_dir + "/check" + std::to_string(j) + ".lua"; + FILE* f = fopen(scriptname.c_str(), "w"); + ASSERT_NE(f, nullptr); + fprintf(f, "%s", script.c_str()); + fclose(f); + int ret = run(runner, scriptname.c_str(), nullptr); EXPECT_EQ(ret, -1) << "Failed for lib " << libname << " after unloading " << name; if (ret == 0) { char expected[10]; @@ -239,22 +220,18 @@ TEST_F(LuaCjApiTest, MultipleLoadUnload) { // 测试 I/O 重定向:Print TEST_F(LuaCjApiTest, RedirectPrint) { - write_lua("print_test.lua", "print('Hello Cangjie')"); - int ret = run(runner, (temp_dir + "/print_test.lua").c_str(), nullptr); + int ret = run(runner, get_script_path("print_test.lua").c_str(), nullptr); EXPECT_EQ(ret, 0); - // 验证:回调函数应该被调用 EXPECT_GT(callback_invoke_count, 0) << "Output callback should be invoked by print"; - // 验证:temp_dir 下应生成 output 文件 std::string content = read_file("output"); EXPECT_NE(content.find("Hello Cangjie"), std::string::npos); } // 测试 I/O 重定向:io.write TEST_F(LuaCjApiTest, RedirectIoWrite) { - write_lua("write_test.lua", "io.write('Data from lua')"); - int ret = run(runner, (temp_dir + "/write_test.lua").c_str(), nullptr); + int ret = run(runner, get_script_path("io_write_test.lua").c_str(), nullptr); EXPECT_EQ(ret, 0); EXPECT_GT(callback_invoke_count, 0) << "Output callback should be invoked by io.write"; @@ -264,51 +241,44 @@ TEST_F(LuaCjApiTest, RedirectIoWrite) { // 测试 I/O 重定向:io.read TEST_F(LuaCjApiTest, RedirectIoRead) { - // 准备:模拟外部向 input 文件写入数据 std::string input_file = temp_dir + "/input"; FILE* f = fopen(input_file.c_str(), "w"); ASSERT_NE(f, nullptr); fprintf(f, "MockInputData"); fclose(f); - // Lua 脚本读取输入并返回 - write_lua("read_test.lua", "local s = io.read('*a'); return s"); - - int ret = run(runner, (temp_dir + "/read_test.lua").c_str(), nullptr); + int ret = run(runner, get_script_path("io_read_test.lua").c_str(), nullptr); EXPECT_EQ(ret, 0); - // 验证:输入回调被调用 EXPECT_GT(callback_invoke_count, 0) << "Input callback should be invoked by io.read"; - // 验证:结果正确 char* res = getresult(runner); EXPECT_STREQ(res, "MockInputData"); } // 测试 pkgpath 配置是否生效 TEST_F(LuaCjApiTest, PackagePathConfig) { - // 创建子目录存放库 std::string lib_dir = temp_dir + "/mypkgs"; mkdir(lib_dir.c_str(), 0755); - // 在子目录创建库 std::string lib_path = lib_dir + "/mylib.lua"; FILE* f = fopen(lib_path.c_str(), "w"); fprintf(f, "return 'LoadedViaPkgPath'"); fclose(f); - // 销毁旧 runner free_lua(runner); runner = nullptr; - // 创建新 runner,配置 pkgpath std::string pkg_path_pattern = lib_dir + "/?.lua"; runner = init_lua_runner(temp_dir.c_str(), pkg_path_pattern.c_str(), mock_callback, mock_callback); ASSERT_NE(runner, nullptr); - // 脚本直接 require,不指定路径 - write_lua("main.lua", "return require 'mylib'"); - int ret = run(runner, (temp_dir + "/main.lua").c_str(), nullptr); + std::string main_script = temp_dir + "/main.lua"; + f = fopen(main_script.c_str(), "w"); + fprintf(f, "return require 'mylib'"); + fclose(f); + + int ret = run(runner, main_script.c_str(), nullptr); EXPECT_EQ(ret, 0) << "Require failed, maybe pkgpath not set correctly. Errno: " << get_errno(); char* res = getresult(runner);