Files
LuaCangjia_api/test/test_lua_cj_api.cpp

292 lines
8.9 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.

#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(), NAPI_LOAD_FILE_ERROR);
}
// 测试超过最大库数量限制
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(), NAPI_LUALIB_LOAD_OVER_STACK);
}
// 测试卸载库
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(), NAPI_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(), NAPI_SCRIPT_ERROR);
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(), NAPI_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, "LoadedViaPkgPath");
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}