mirror of
https://gitcode.com/JianFeeeee/LuaCangjia_api.git
synced 2026-09-19 16:42:48 +00:00
- C++ 层: callfunction_int/num/bool/void + run_int/num/bool/void - 新增 capture_result()/finish_call() 正确计算 lua_pcall 实际返回值数量, 修复无返回值场景下误读栈残留的问题 - C FFI 层: 11 个新导出函数 + lua_runner 结构体 tresult_* 镜像字段 - 仓颉层: callFunctionInt/Num/Bool, runScriptInt/Num/Bool, resultType() 带严格类型校验(不匹配抛 LUAERR_RESULT_TYPE) - 大整数跨 2^53 保真(字符串路径做不到) - 管道模式语义保留给字符串 API; typed run_* 为独立调用保持栈清洁 - GTest 新增 10 个 typed 测试用例(共 44 个全部通过) - 独立程序验证仓颉层运行时正确性(int/num/bool直通/大整数保真/错误路径)
654 lines
22 KiB
C++
654 lines
22 KiB
C++
#include <gtest/gtest.h>
|
||
#include <cstdio>
|
||
#include <cstdlib>
|
||
#include <cstring>
|
||
#include <unistd.h>
|
||
#include <sys/stat.h>
|
||
#include <string>
|
||
#include <fstream>
|
||
#include <streambuf>
|
||
#include <stdexcept>
|
||
|
||
extern "C" {
|
||
#include "lua_cj_api.h"
|
||
#include "errors.h"
|
||
}
|
||
|
||
static std::string create_temp_dir() {
|
||
char tmpl[] = "/tmp/lua_cj_test_XXXXXX";
|
||
char *dir = mkdtemp(tmpl);
|
||
|
||
if (dir == nullptr) {
|
||
perror("mkdtemp failed");
|
||
throw std::runtime_error("Failed to create temp directory");
|
||
}
|
||
|
||
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;
|
||
std::string temp_dir;
|
||
|
||
void SetUp() override {
|
||
temp_dir = create_temp_dir();
|
||
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();
|
||
}
|
||
|
||
void TearDown() override {
|
||
if (runner) {
|
||
free_lua(runner);
|
||
runner = nullptr;
|
||
}
|
||
std::string cmd = "rm -rf " + temp_dir;
|
||
system(cmd.c_str());
|
||
}
|
||
|
||
std::string read_file(const std::string& filename) {
|
||
std::string path = temp_dir + "/" + filename;
|
||
std::ifstream t(path);
|
||
std::string str((std::istreambuf_iterator<char>(t)),
|
||
std::istreambuf_iterator<char>());
|
||
return str;
|
||
}
|
||
};
|
||
|
||
// 基础生命周期测试
|
||
TEST_F(LuaCjApiTest, InitFree) {
|
||
SUCCEED();
|
||
}
|
||
|
||
// 测试加载库
|
||
TEST_F(LuaCjApiTest, LoadLib) {
|
||
int ret = load_lib(runner, get_script_path("mylib.lua").c_str(), "mylib");
|
||
EXPECT_EQ(ret, 0);
|
||
}
|
||
|
||
// 测试加载不存在的文件
|
||
TEST_F(LuaCjApiTest, LoadLibFileNotFound) {
|
||
int ret = load_lib(runner, "/no/such/file.lua", "bad");
|
||
EXPECT_EQ(ret, -1);
|
||
EXPECT_EQ(get_errno(), LUAERR_LOAD_FILE);
|
||
}
|
||
|
||
// 测试超过最大库数量限制
|
||
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 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);
|
||
}
|
||
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(), LUAERR_LIB_OVERFLOW);
|
||
}
|
||
|
||
// 测试卸载库
|
||
TEST_F(LuaCjApiTest, UnloadLib) {
|
||
ASSERT_EQ(load_lib(runner, get_script_path("mylib.lua").c_str(), "mylib"), 0);
|
||
int ret = unload_lib(runner, "mylib");
|
||
EXPECT_EQ(ret, 0);
|
||
}
|
||
|
||
// 测试卸载未加载的库
|
||
TEST_F(LuaCjApiTest, UnloadLibNotFound) {
|
||
int ret = unload_lib(runner, "nosuch");
|
||
EXPECT_EQ(ret, -1);
|
||
EXPECT_EQ(get_errno(), LUAERR_UNLOADLIB_FAIL);
|
||
}
|
||
|
||
// 测试运行脚本
|
||
TEST_F(LuaCjApiTest, RunScript) {
|
||
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");
|
||
}
|
||
|
||
// 测试带参数的脚本
|
||
TEST_F(LuaCjApiTest, RunScriptWithArg) {
|
||
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");
|
||
}
|
||
|
||
|
||
// 测试脚本编译期语法错误按加载错误上报
|
||
TEST_F(LuaCjApiTest, RunScriptSyntaxError) {
|
||
int ret = run(runner, get_script_path("syntax_error.lua").c_str(), NULL);
|
||
EXPECT_EQ(ret, -1);
|
||
EXPECT_EQ(get_errno(), LUAERR_LOAD_FILE);
|
||
char* res = getresult(runner);
|
||
EXPECT_NE(strstr(res, "'do' expected"), nullptr);
|
||
}
|
||
|
||
// 测试脚本运行时错误
|
||
TEST_F(LuaCjApiTest, RunScriptRuntimeError) {
|
||
int ret = run(runner, get_script_path("runtime_error.lua").c_str(), nullptr);
|
||
EXPECT_EQ(ret, -1);
|
||
EXPECT_EQ(get_errno(), LUAERR_SCRIPT_ERROR);
|
||
char* res = getresult(runner);
|
||
EXPECT_NE(strstr(res, "oops"), nullptr);
|
||
}
|
||
|
||
// 测试 cleanup 重置状态
|
||
TEST_F(LuaCjApiTest, Cleanup) {
|
||
ASSERT_EQ(run(runner, get_script_path("set_global.lua").c_str(), nullptr), 0);
|
||
int ret = cleanup(runner);
|
||
EXPECT_EQ(ret, 0);
|
||
ret = run(runner, get_script_path("get_global.lua").c_str(), nullptr);
|
||
EXPECT_EQ(ret, 0);
|
||
char* res = getresult(runner);
|
||
EXPECT_STREQ(res, "nil");
|
||
}
|
||
|
||
// 测试跨调用数据传递
|
||
TEST_F(LuaCjApiTest, CrossCallDataPassing) {
|
||
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");
|
||
}
|
||
|
||
|
||
// 测试多次加载卸载后栈索引正确
|
||
TEST_F(LuaCjApiTest, MultipleLoadUnload) {
|
||
for (int i = 0; i < 5; ++i) {
|
||
char name[20];
|
||
snprintf(name, sizeof(name), "L%d", i);
|
||
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);
|
||
}
|
||
|
||
for (int i = 4; i >= 0; --i) {
|
||
char name[20];
|
||
snprintf(name, sizeof(name), "L%d", i);
|
||
ASSERT_EQ(unload_lib(runner, name), 0);
|
||
|
||
for (int j = 0; j < i; ++j) {
|
||
char libname[20];
|
||
snprintf(libname, sizeof(libname), "L%d", j);
|
||
std::string script = "return require '" + std::string(libname) + "'";
|
||
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];
|
||
snprintf(expected, sizeof(expected), "%d", j);
|
||
EXPECT_STREQ(getresult(runner), expected);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// ==================== 新增测试:针对仓颉封装覆盖的功能 ====================
|
||
|
||
// 测试 I/O 重定向:Print
|
||
TEST_F(LuaCjApiTest, RedirectPrint) {
|
||
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";
|
||
|
||
std::string content = read_file("output");
|
||
EXPECT_NE(content.find("Hello Cangjie"), std::string::npos);
|
||
}
|
||
|
||
// 测试 I/O 重定向:io.write
|
||
TEST_F(LuaCjApiTest, RedirectIoWrite) {
|
||
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";
|
||
std::string content = read_file("output");
|
||
EXPECT_NE(content.find("Data from lua"), std::string::npos);
|
||
}
|
||
|
||
// 测试 I/O 重定向:io.read
|
||
TEST_F(LuaCjApiTest, RedirectIoRead) {
|
||
std::string input_file = temp_dir + "/input";
|
||
FILE* f = fopen(input_file.c_str(), "w");
|
||
ASSERT_NE(f, nullptr);
|
||
fprintf(f, "MockInputData");
|
||
fclose(f);
|
||
|
||
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);
|
||
|
||
free_lua(runner);
|
||
runner = nullptr;
|
||
|
||
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);
|
||
|
||
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);
|
||
EXPECT_STREQ(res, lib_path.c_str());
|
||
}
|
||
|
||
// ==================== 新增测试:doString 功能 ====================
|
||
|
||
// 测试简单表达式
|
||
TEST_F(LuaCjApiTest, DoStringSimple) {
|
||
int ret = dostring(runner, "return 1 + 1");
|
||
EXPECT_EQ(ret, 0);
|
||
char* res = getresult(runner);
|
||
EXPECT_STREQ(res, "2");
|
||
}
|
||
|
||
// 测试全局变量设置与读取
|
||
TEST_F(LuaCjApiTest, DoStringWithGlobal) {
|
||
int ret = dostring(runner, "g = 42; return g");
|
||
EXPECT_EQ(ret, 0);
|
||
char* res = getresult(runner);
|
||
EXPECT_STREQ(res, "42");
|
||
}
|
||
|
||
// 测试语法错误
|
||
TEST_F(LuaCjApiTest, DoStringSyntaxError) {
|
||
int ret = dostring(runner, "if true then");
|
||
EXPECT_EQ(ret, -1);
|
||
EXPECT_EQ(get_errno(), LUAERR_CALLBACK);
|
||
char* res = getresult(runner);
|
||
EXPECT_NE(strstr(res, "'end' expected"), nullptr);
|
||
}
|
||
|
||
// 测试无返回值
|
||
TEST_F(LuaCjApiTest, DoStringNoReturn) {
|
||
int ret = dostring(runner, "x = 10");
|
||
EXPECT_EQ(ret, -1);
|
||
EXPECT_EQ(get_errno(), LUAERR_SCRIPT_BAD_RET);
|
||
}
|
||
|
||
// 测试返回 nil
|
||
TEST_F(LuaCjApiTest, DoStringNilReturn) {
|
||
int ret = dostring(runner, "return nil");
|
||
EXPECT_EQ(ret, -1);
|
||
EXPECT_EQ(get_errno(), LUAERR_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(), LUAERR_FUNCTION_NOT_FOUND);
|
||
}
|
||
|
||
// 预加载不存在的文件
|
||
TEST_F(LuaCjApiTest, LoadFunctionFileNotFound) {
|
||
int ret = loadfunction(runner, "/no/such/file.lua", "bad");
|
||
EXPECT_EQ(ret, -1);
|
||
EXPECT_EQ(get_errno(), LUAERR_LOAD_FILE);
|
||
}
|
||
|
||
// 预加载顶层未返回函数的文件
|
||
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(), LUAERR_FUNCTION_INVALID);
|
||
}
|
||
|
||
// 预加载同名函数
|
||
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(), LUAERR_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(), LUAERR_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(), LUAERR_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(), LUAERR_FUNC_OVERFLOW);
|
||
}
|
||
|
||
// 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(), LUAERR_FUNCTION_NOT_FOUND);
|
||
}
|
||
|
||
// ==================== typed interop 测试(v0.2.2)====================
|
||
|
||
// 整型参数直通:Lua 收到 integer,返回 integer+1,仓颉侧读回 Int64
|
||
TEST_F(LuaCjApiTest, TypedCallInt) {
|
||
ASSERT_EQ(loadfunction(runner, get_script_path("func_typed_add.lua").c_str(), "inc"), 0);
|
||
|
||
EXPECT_EQ(callfunction_int(runner, "inc", 41), 0);
|
||
EXPECT_EQ(result_type(runner), CJT_INT);
|
||
EXPECT_EQ(result_int(runner), 42);
|
||
// 字符串形式同步可用(向后兼容)
|
||
EXPECT_STREQ(getresult(runner), "42");
|
||
}
|
||
|
||
// 浮点参数直通:返回 number*2.5,读回 Float64
|
||
TEST_F(LuaCjApiTest, TypedCallNum) {
|
||
ASSERT_EQ(loadfunction(runner, get_script_path("func_typed_mul.lua").c_str(), "mul"), 0);
|
||
|
||
EXPECT_EQ(callfunction_num(runner, "mul", 4.0), 0);
|
||
EXPECT_EQ(result_type(runner), CJT_NUM);
|
||
EXPECT_DOUBLE_EQ(result_num(runner), 10.0);
|
||
EXPECT_STREQ(getresult(runner), "10.0");
|
||
}
|
||
|
||
// 布尔参数直通:返回逻辑非,读回 Bool
|
||
TEST_F(LuaCjApiTest, TypedCallBool) {
|
||
ASSERT_EQ(loadfunction(runner, get_script_path("func_typed_not.lua").c_str(), "lnot"), 0);
|
||
|
||
EXPECT_EQ(callfunction_bool(runner, "lnot", 1), 0);
|
||
EXPECT_EQ(result_type(runner), CJT_BOOL);
|
||
EXPECT_EQ(result_bool(runner), 0);
|
||
EXPECT_STREQ(getresult(runner), "false");
|
||
|
||
EXPECT_EQ(callfunction_bool(runner, "lnot", 0), 0);
|
||
EXPECT_EQ(result_type(runner), CJT_BOOL);
|
||
EXPECT_EQ(result_bool(runner), 1);
|
||
EXPECT_STREQ(getresult(runner), "true");
|
||
}
|
||
|
||
// 无参调用 + 整数返回
|
||
TEST_F(LuaCjApiTest, TypedCallVoid) {
|
||
ASSERT_EQ(loadfunction(runner, get_script_path("func_typed_void.lua").c_str(), "forty_two"), 0);
|
||
|
||
EXPECT_EQ(callfunction_void(runner, "forty_two"), 0);
|
||
EXPECT_EQ(result_type(runner), CJT_INT);
|
||
EXPECT_EQ(result_int(runner), 42);
|
||
}
|
||
|
||
// 回声函数:验证各类型往返保真(类型不丢失)
|
||
TEST_F(LuaCjApiTest, TypedEchoRoundTrip) {
|
||
ASSERT_EQ(loadfunction(runner, get_script_path("func_typed_echo.lua").c_str(), "echo"), 0);
|
||
|
||
// int 往返
|
||
EXPECT_EQ(callfunction_int(runner, "echo", -123456789LL), 0);
|
||
EXPECT_EQ(result_type(runner), CJT_INT);
|
||
EXPECT_EQ(result_int(runner), -123456789LL);
|
||
|
||
// 大整数(超过 double 精确表示范围)往返
|
||
EXPECT_EQ(callfunction_int(runner, "echo", 9007199254740993LL), 0);
|
||
EXPECT_EQ(result_type(runner), CJT_INT);
|
||
EXPECT_EQ(result_int(runner), 9007199254740993LL);
|
||
|
||
// num 往返
|
||
EXPECT_EQ(callfunction_num(runner, "echo", 3.14159), 0);
|
||
EXPECT_EQ(result_type(runner), CJT_NUM);
|
||
EXPECT_DOUBLE_EQ(result_num(runner), 3.14159);
|
||
|
||
// bool 往返
|
||
EXPECT_EQ(callfunction_bool(runner, "echo", 1), 0);
|
||
EXPECT_EQ(result_type(runner), CJT_BOOL);
|
||
EXPECT_EQ(result_bool(runner), 1);
|
||
|
||
// string 往返(走原 callfunction)
|
||
EXPECT_EQ(callfunction(runner, "echo", "hello typed"), 0);
|
||
EXPECT_EQ(result_type(runner), CJT_STR);
|
||
EXPECT_STREQ(getresult(runner), "hello typed");
|
||
}
|
||
|
||
// nil 返回值识别
|
||
TEST_F(LuaCjApiTest, TypedNilResult) {
|
||
ASSERT_EQ(loadfunction(runner, get_script_path("func_typed_nil.lua").c_str(), "nilret"), 0);
|
||
|
||
EXPECT_EQ(callfunction_void(runner, "nilret"), 0);
|
||
EXPECT_EQ(result_type(runner), CJT_NIL);
|
||
EXPECT_EQ(result_int(runner), 0);
|
||
EXPECT_EQ(result_bool(runner), 0);
|
||
}
|
||
|
||
// typed 调用不存在的函数仍报 5023
|
||
TEST_F(LuaCjApiTest, TypedCallNotFound) {
|
||
EXPECT_EQ(callfunction_int(runner, "nonexistent", 1), -1);
|
||
EXPECT_EQ(get_errno(), LUAERR_FUNCTION_NOT_FOUND);
|
||
|
||
EXPECT_EQ(callfunction_void(runner, "nonexistent"), -1);
|
||
EXPECT_EQ(get_errno(), LUAERR_FUNCTION_NOT_FOUND);
|
||
}
|
||
|
||
// runScript 的 typed 变体:args_echo.lua 直接返回入参
|
||
TEST_F(LuaCjApiTest, TypedRunVariants) {
|
||
std::string path = temp_dir + "/args_echo.lua";
|
||
FILE* f = fopen(path.c_str(), "w");
|
||
ASSERT_NE(f, nullptr);
|
||
fprintf(f, "return (...)");
|
||
fclose(f);
|
||
|
||
// int
|
||
EXPECT_EQ(run_int(runner, path.c_str(), 777), 0);
|
||
EXPECT_EQ(result_type(runner), CJT_INT);
|
||
EXPECT_EQ(result_int(runner), 777);
|
||
|
||
// num
|
||
EXPECT_EQ(run_num(runner, path.c_str(), 2.5), 0);
|
||
EXPECT_EQ(result_type(runner), CJT_NUM);
|
||
EXPECT_DOUBLE_EQ(result_num(runner), 2.5);
|
||
|
||
// bool
|
||
EXPECT_EQ(run_bool(runner, path.c_str(), 1), 0);
|
||
EXPECT_EQ(result_type(runner), CJT_BOOL);
|
||
EXPECT_EQ(result_bool(runner), 1);
|
||
|
||
// 无参(无返回时栈顶为空,reslt 应为 "nil")
|
||
std::string void_path = temp_dir + "/no_return.lua";
|
||
f = fopen(void_path.c_str(), "w");
|
||
ASSERT_NE(f, nullptr);
|
||
fprintf(f, "local x = 1");
|
||
fclose(f);
|
||
EXPECT_EQ(run_void(runner, void_path.c_str()), 0);
|
||
EXPECT_EQ(result_type(runner), CJT_NIL);
|
||
}
|
||
|
||
// typed 与字符串 API 混用互不干扰;typed 结果同步到字符串缓冲区
|
||
TEST_F(LuaCjApiTest, TypedAndStringCoexist) {
|
||
ASSERT_EQ(loadfunction(runner, get_script_path("func_add.lua").c_str(), "add"), 0);
|
||
ASSERT_EQ(loadfunction(runner, get_script_path("func_typed_add.lua").c_str(), "inc"), 0);
|
||
|
||
// 字符串 API
|
||
EXPECT_EQ(callfunction(runner, "add", "a"), 0);
|
||
EXPECT_STREQ(getresult(runner), "result: a (call #1)");
|
||
EXPECT_EQ(result_type(runner), CJT_STR);
|
||
|
||
// typed API
|
||
EXPECT_EQ(callfunction_int(runner, "inc", 100), 0);
|
||
EXPECT_EQ(result_int(runner), 101);
|
||
|
||
// 字符串函数的计数器不受影响
|
||
EXPECT_EQ(callfunction(runner, "add", "b"), 0);
|
||
EXPECT_STREQ(getresult(runner), "result: b (call #2)");
|
||
}
|
||
|
||
// cleanup 后 typed 状态重置为 NIL
|
||
TEST_F(LuaCjApiTest, TypedStateAfterCleanup) {
|
||
ASSERT_EQ(loadfunction(runner, get_script_path("func_typed_void.lua").c_str(), "v"), 0);
|
||
EXPECT_EQ(callfunction_void(runner, "v"), 0);
|
||
EXPECT_EQ(result_type(runner), CJT_INT);
|
||
|
||
EXPECT_EQ(cleanup(runner), 0);
|
||
|
||
int ret = callfunction_void(runner, "v");
|
||
EXPECT_EQ(ret, -1);
|
||
EXPECT_EQ(get_errno(), LUAERR_FUNCTION_NOT_FOUND);
|
||
}
|
||
|
||
int main(int argc, char **argv) {
|
||
::testing::InitGoogleTest(&argc, argv);
|
||
return RUN_ALL_TESTS();
|
||
}
|