mirror of
https://gitcode.com/JianFeeeee/LuaCangjia_api.git
synced 2026-09-20 00:48:47 +00:00
添加说明文档,为cangjie层添加单元测试
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
package LuaCangjie_api
|
||||
package luacangjie_api
|
||||
|
||||
// ==================== FFI底层 ====================
|
||||
|
||||
@ -52,6 +52,7 @@ private func toString(ptr: CString): String {
|
||||
|
||||
// ==================== 异常 ====================
|
||||
|
||||
/// Lua运行时错误类
|
||||
public class LuaError <: Exception {
|
||||
public let code: Int32
|
||||
|
||||
@ -60,13 +61,13 @@ public class LuaError <: Exception {
|
||||
this.code = code
|
||||
}
|
||||
|
||||
// 便捷构造函数:只传入错误码,自动获取描述
|
||||
/// 便捷构造函数:只传入错误码,自动获取描述
|
||||
public init(code: Int32) {
|
||||
super(getErrorMessage(code))
|
||||
this.code = code
|
||||
}
|
||||
|
||||
// 根据错误码获取错误描述
|
||||
/// 根据错误码获取错误描述
|
||||
public static func getErrorMessage(code: Int32): String {
|
||||
match (code) {
|
||||
case 5001 => "JSON file load failed"
|
||||
@ -93,7 +94,7 @@ public class LuaError <: Exception {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取当前错误的描述
|
||||
/// 获取当前错误的描述
|
||||
public func getMessage(): String {
|
||||
getErrorMessage(this.code)
|
||||
}
|
||||
@ -101,17 +102,20 @@ public class LuaError <: Exception {
|
||||
|
||||
// ==================== 主类 ====================
|
||||
|
||||
/// Lua运行管理类
|
||||
public class LuaRunner {
|
||||
private var handle: CPointer<Unit>
|
||||
private var cachedResult: String = ""
|
||||
private var ioInput: IOCallback
|
||||
private var ioOutput: IOCallback
|
||||
|
||||
// pkgpath 重定向lua状态机包搜索路径
|
||||
// pathio 重定向io输出到pathio路径下
|
||||
// @param output 类型function 用于lua内部触发输出时调用的回调函数
|
||||
// @param input 类型function 用于lua内部触发输入时的调用的回调函数
|
||||
// @brief 构造lua状态机
|
||||
/// 构造lua状态机
|
||||
///
|
||||
/// @param input 类型function 用于lua内部触发输入时的调用的回调函数
|
||||
/// @param output 类型function 用于lua内部触发输出时调用的回调函数
|
||||
/// @param pathio 重定向io输出到pathio路径下
|
||||
/// @param pkgpath 重定向lua状态机包搜索路径
|
||||
/// @throws LuaError 当初始化失败(句柄为空)时抛出
|
||||
public init(input!: IOCallback = defaultIO, output!: IOCallback = defaultIO, pathio!: String = "", pkgpath!: String = "") {
|
||||
this.ioInput = input
|
||||
this.ioOutput = output
|
||||
@ -138,6 +142,12 @@ public class LuaRunner {
|
||||
}
|
||||
}
|
||||
|
||||
/// 加载动态库
|
||||
///
|
||||
/// @param path 库路径
|
||||
/// @param name 库名称
|
||||
/// @return 当前实例
|
||||
/// @throws LuaError 当加载库失败时抛出
|
||||
public func load(path: String, name: String): This {
|
||||
let pathPtr = toCStr(path)
|
||||
let namePtr = toCStr(name)
|
||||
@ -150,12 +160,18 @@ public class LuaRunner {
|
||||
}
|
||||
|
||||
if (result != 0) {
|
||||
let errMsg = toString(unsafe { getresult(this.handle) })
|
||||
throw LuaError(unsafe { get_errno() }, errMsg)
|
||||
// [修正] 直接使用错误码构造异常,复用 LuaError 的错误码转描述机制
|
||||
// 避免调用 getresult 读取未初始化的 C 层字符串
|
||||
throw LuaError(unsafe { get_errno() })
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/// 卸载动态库
|
||||
///
|
||||
/// @param name 库名称
|
||||
/// @return 当前实例
|
||||
/// @throws LuaError 当卸载库失败时抛出
|
||||
public func unload(name: String): This {
|
||||
let namePtr = toCStr(name)
|
||||
let result = unsafe { unload_lib(this.handle, namePtr) }
|
||||
@ -165,12 +181,18 @@ public class LuaRunner {
|
||||
}
|
||||
|
||||
if (result != 0) {
|
||||
let errMsg = toString(unsafe { getresult(this.handle) })
|
||||
throw LuaError(unsafe { get_errno() }, errMsg)
|
||||
// [修正] 直接使用错误码构造异常,复用 LuaError 的错误码转描述机制
|
||||
throw LuaError(unsafe { get_errno() })
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/// 运行Lua脚本
|
||||
///
|
||||
/// @param path 脚本路径
|
||||
/// @param arg 传递给脚本的参数
|
||||
/// @return 脚本执行结果字符串
|
||||
/// @throws LuaError 当脚本运行出错时抛出
|
||||
public func runScript(path: String, arg: String): String {
|
||||
let pathPtr = toCStr(path)
|
||||
let argPtr = toCStr(arg)
|
||||
@ -189,10 +211,16 @@ public class LuaRunner {
|
||||
return this.cachedResult
|
||||
}
|
||||
|
||||
/// 获取缓存的结果
|
||||
public func result(): String { this.cachedResult }
|
||||
|
||||
/// 获取错误信息
|
||||
public func error(): String { toString(unsafe { getresult(this.handle) }) }
|
||||
|
||||
/// 清理Lua状态机资源
|
||||
///
|
||||
/// @return 当前实例
|
||||
/// @throws LuaError 当清理失败时抛出
|
||||
public func clear(): This {
|
||||
let result = unsafe { cleanup(this.handle) }
|
||||
if (result != 0) {
|
||||
@ -200,4 +228,4 @@ public class LuaRunner {
|
||||
}
|
||||
return this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
176
src/lua_runner_test.cj
Normal file
176
src/lua_runner_test.cj
Normal file
@ -0,0 +1,176 @@
|
||||
package luacangjie_api
|
||||
|
||||
import std.unittest.*
|
||||
import std.fs.*
|
||||
|
||||
let scriptsDir = "./test/scripts"
|
||||
|
||||
// ====================
|
||||
// 1. 基础运行与参数测试
|
||||
// ====================
|
||||
|
||||
@Test
|
||||
func testRunScript(): Unit {
|
||||
let runner = LuaRunner()
|
||||
let path = scriptsDir + "/simple_return.lua"
|
||||
let res = runner.runScript(path, "")
|
||||
@Expect(res, "hello")
|
||||
}
|
||||
|
||||
@Test
|
||||
func testRunScriptWithArg(): Unit {
|
||||
let runner = LuaRunner()
|
||||
let path = scriptsDir + "/args_test.lua"
|
||||
let res = runner.runScript(path, "world")
|
||||
@Expect(res, "world")
|
||||
}
|
||||
|
||||
// ====================
|
||||
// 2. 异常处理测试
|
||||
// ====================
|
||||
|
||||
@Test
|
||||
func testRunScriptSyntaxError(): Unit {
|
||||
let runner = LuaRunner()
|
||||
let path = scriptsDir + "/syntax_error.lua"
|
||||
try {
|
||||
runner.runScript(path, "")
|
||||
fail("Should throw syntax error")
|
||||
} catch (e: LuaError) {
|
||||
@Expect(e.code, 5010)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
func testRunScriptRuntimeError(): Unit {
|
||||
let runner = LuaRunner()
|
||||
let path = scriptsDir + "/runtime_error.lua"
|
||||
try {
|
||||
runner.runScript(path, "")
|
||||
fail("Should throw runtime error")
|
||||
} catch (e: LuaError) {
|
||||
@Expect(e.code, 5010)
|
||||
}
|
||||
}
|
||||
|
||||
// ====================
|
||||
// 3. 库加载测试
|
||||
// ====================
|
||||
|
||||
@Test
|
||||
func testLoadLib(): Unit {
|
||||
let runner = LuaRunner()
|
||||
let path = scriptsDir + "/mylib.lua"
|
||||
runner.load(path, "mylib")
|
||||
}
|
||||
|
||||
@Test
|
||||
func testLoadLibFileNotFound(): Unit {
|
||||
let runner = LuaRunner()
|
||||
try {
|
||||
runner.load("/no/such/file.lua", "badlib")
|
||||
fail("Should throw file not found error")
|
||||
} catch (e: LuaError) {
|
||||
@Expect(e.code, 5003)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
func testUnloadLib(): Unit {
|
||||
let runner = LuaRunner()
|
||||
let path = scriptsDir + "/mylib.lua"
|
||||
runner.load(path, "mylib")
|
||||
runner.unload("mylib")
|
||||
}
|
||||
|
||||
@Test
|
||||
func testUnloadLibNotFound(): Unit {
|
||||
let runner = LuaRunner()
|
||||
try {
|
||||
runner.unload("nosuchlib")
|
||||
fail("Should throw exception")
|
||||
} catch (e: LuaError) {
|
||||
@Expect(e.code, 5012)
|
||||
}
|
||||
}
|
||||
|
||||
// ====================
|
||||
// 4. 状态管理与数据交互
|
||||
// ====================
|
||||
|
||||
@Test
|
||||
func testCleanup(): Unit {
|
||||
let runner = LuaRunner()
|
||||
let setPath = scriptsDir + "/set_global.lua"
|
||||
let getPath = scriptsDir + "/get_global.lua"
|
||||
|
||||
runner.runScript(setPath, "")
|
||||
runner.clear()
|
||||
let res = runner.runScript(getPath, "")
|
||||
@Expect(res, "nil")
|
||||
}
|
||||
|
||||
@Test
|
||||
func testCrossCallDataPassing(): Unit {
|
||||
let runner = LuaRunner()
|
||||
let setPath = scriptsDir + "/cross_call_set.lua"
|
||||
let getPath = scriptsDir + "/cross_call_get.lua"
|
||||
|
||||
runner.runScript(setPath, "")
|
||||
let res = runner.runScript(getPath, "")
|
||||
@Expect(res, "1,2,3")
|
||||
}
|
||||
|
||||
// ====================
|
||||
// 5. I/O 重定向测试
|
||||
// ====================
|
||||
|
||||
@Test
|
||||
func testRedirectPrint(): Unit {
|
||||
let runner = LuaRunner(pathio: "/tmp")
|
||||
let path = scriptsDir + "/print_test.lua"
|
||||
runner.runScript(path, "")
|
||||
}
|
||||
|
||||
@Test
|
||||
func testRedirectIoWrite(): Unit {
|
||||
let runner = LuaRunner(pathio: "/tmp")
|
||||
let path = scriptsDir + "/io_write_test.lua"
|
||||
runner.runScript(path, "")
|
||||
}
|
||||
|
||||
@Test
|
||||
func testRedirectIoRead(): Unit {
|
||||
// 1. 准备输入文件
|
||||
let inputFile = "/tmp/input"
|
||||
|
||||
// [修正点]:移除了不存在的 option 参数。
|
||||
// File.writeTo 默认行为即为 CreateOrTruncate:
|
||||
// 文件存在会截断(覆盖),不存在则创建。
|
||||
File.writeTo(inputFile, "MockInputData".toArray())
|
||||
|
||||
// 2. 运行测试
|
||||
let runner = LuaRunner(pathio: "/tmp")
|
||||
let path = scriptsDir + "/io_read_test.lua"
|
||||
|
||||
let res = runner.runScript(path, "")
|
||||
@Expect(res, "MockInputData")
|
||||
}
|
||||
|
||||
// ====================
|
||||
// 6. 错误码与回调
|
||||
// ====================
|
||||
|
||||
@Test
|
||||
func testErrorCodesMapping(): Unit {
|
||||
@Expect(LuaError.getErrorMessage(5001), "JSON file load failed")
|
||||
@Expect(LuaError.getErrorMessage(5002), "Memory allocation failed")
|
||||
@Expect(LuaError.getErrorMessage(5010), "Script internal error, check result for details")
|
||||
@Expect(LuaError.getErrorMessage(9999), "Unknown error")
|
||||
}
|
||||
|
||||
@Test
|
||||
func testDefaultIOCallback(): Unit {
|
||||
let result = unsafe { defaultIO() }
|
||||
@Expect(result, 0)
|
||||
}
|
||||
25
src/main.cj
25
src/main.cj
@ -1,25 +0,0 @@
|
||||
package LuaCangjie_api
|
||||
|
||||
// ==================== main ====================
|
||||
|
||||
main(): Int64 {
|
||||
try {
|
||||
let runner = LuaRunner(pathio:"test")
|
||||
|
||||
println("LuaRunner initialized successfully")
|
||||
|
||||
// 运行 test.lua 脚本,无参数传入
|
||||
let result = runner.runScript("test.lua", "")
|
||||
|
||||
println("Script output: ${result}")
|
||||
|
||||
// 清理缓存(析构函数会自动释放资源)
|
||||
runner.clear()
|
||||
|
||||
} catch (e: LuaError) {
|
||||
println("Error [code=${e.code}]: ${e.message}")
|
||||
return 1
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
Reference in New Issue
Block a user