2008年2月25日星期一

Lua 的 Unit Testing

今天成功把 Lua 嵌入Engine (其實只是十數行代碼及設定連結等),之後開了一個新 C++ project 執行一個 Lua 檔案去測試 Engine 提供的介面。

在網上找了一會,選擇了 luaUnit 來做 Unit Testing。luaUnit 的好處是使用 Lua 的 "Reflection" (其實只是找 table 吧),自動找尋所有 test suites。使用起來真的很簡單。以下是我做的 unit test。

require('luaunit')

TestVector3 = {}

function TestVector3:setUp()
self.a = Math.Vector3(1, 2, 3)
self.b = Math.Vector3(4, 6, 8)
end

function TestVector3:tearDown()
self.a = nil
self.b = nil
end

function TestVector3:testComponent()
assertEquals(self.a.x, 1)
assertEquals(self.a.y, 2)
assertEquals(self.a.z, 3)
end

function TestVector3:testCopy()
local c = Math.Vector3(self.a);
assertEquals(c.x, 1)
assertEquals(c.y, 2)
assertEquals(c.z, 3)
end

function TestVector3:testNegation()
print(-self.a)
assertEquals(c.x, -1)
assertEquals(c.y, -2)
assertEquals(c.z, -3)
end

function TestVector3:testAdd()
local c = self.a + self.b
assertEquals(c.x, 5)
assertEquals(c.y, 8)
assertEquals(c.z, 11)
end

-- Other operators, skipped here

function TestVector3:testSquredLength()
assertEquals(self.a:GetSquaredLength(), 14)
end

function TestVector3:testLength()
assert(math.abs(self.a:GetLength() - math.sqrt(14)) < 0.00001)
end

function TestVector3:testNormalize()
local c = self.a:GetNormalized();
assert(math.abs(c:GetLength() - 1) < 0.00001)

local d = c * self.a:GetLength()
assert((d - self.a):GetLength() < 0.00001)

local e = Math.Vector3(self.a)
e:Normalize()
assertEquals(c, e)
end

function TestVector3:testDot()
assertEquals(40, Math.Vector3_Dot(self.a, self.b))
end

LuaUnit:run()

執行結果
>>>>>>>>> TestVector3
>>> TestVector3:testAdd
>>> TestVector3:testComponent
>>> TestVector3:testCopy
>>> TestVector3:testDivide
>>> TestVector3:testDot
>>> TestVector3:testEquality
>>> TestVector3:testInequality
>>> TestVector3:testLength
>>> TestVector3:testMultiply
>>> TestVector3:testNegation
Error in operator - expected 1..1 args, got 2
stack traceback:
.\luaunit.lua:288: in function <.\luaunit.lua:287>
[C]: ?
main.lua:29: in function 'testNegation'
[string "TestVector3:testNegation()"]:1: in main chunk
[C]: in function 'xpcall'
.\luaunit.lua:292: in function 'runTestMethod'
.\luaunit.lua:309: in function 'runTestMethodName'
.\luaunit.lua:337: in function 'runTestClassByName'
.\luaunit.lua:369: in function 'run'
main.lua:95: in main chunk
Failed
>>> TestVector3:testNormalize
>>> TestVector3:testSquredLength
>>> TestVector3:testSubtract

=========================================================
Failed tests:
-------------
>>> TestVector3:testNegation failed
Error in operator - expected 1..1 args, got 2
stack traceback:
.\luaunit.lua:288: in function <.\luaunit.lua:287>
[C]: ?
main.lua:29: in function 'testNegation'
[string "TestVector3:testNegation()"]:1: in main chunk
[C]: in function 'xpcall'
.\luaunit.lua:292: in function 'runTestMethod'
.\luaunit.lua:309: in function 'runTestMethodName'
.\luaunit.lua:337: in function 'runTestClassByName'
.\luaunit.lua:369: in function 'run'
main.lua:95: in main chunk

Success : 92% - 12 / 13

找到錯誤


Oh! 竟然有一個錯誤! (當然是debug了其他錯誤後還有一個不能解決,而不是第一次寫 Lua 就沒錯)

找了一會,發現有文章講述 Lua 5.1 在呼叫使用者定義的 unary minus operator (__unm) 時傳入兩個 self 的 operands。因此 SWIG 會發生問題。

原本只是想用來測試系統架構及嘗試寫 unit test,今次,又再次證實 unit testing 真的是很有用啊。又想起這句名言
「唔試過又點知佢work呢?」
後話

Vector3 在 Lua 的語意上是 reference type,c = a 是指兩個變數都是指著同一個物件。要寫 c = Math.Vector3(a) 才能做 copy。這可能會很麻煩而且容易出錯。Lua又好像不能 overload assignment operator。有無甚麼好提議呢?

此外,static member function 像 Dot() 好似不太好用。還是用 % 及 ^ 做 dot 及 cross product 吧。這兩個 operator Lua 都支持。

沒有留言: