写了些没啥用处的lua Test (#335)

可以玩玩看
This commit is contained in:
notify 2024-04-01 14:53:58 +08:00 committed by GitHub
parent 55703521e9
commit 75d0ad55c8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 931 additions and 20 deletions

53
test/lua/core/pattern.lua Normal file
View File

@ -0,0 +1,53 @@
-- 针对 core/exppattern.lua 的一些测试用例
TestExppattern = {
testMatchExp = function()
local exp1 = Exppattern:Parse("slash,jink")
lu.assertTrue(exp1:matchExp("peack,jink"))
end,
testEasyMatchCard = function()
local exp1 = Exppattern:Parse("slash,jink")
local exp2 = Exppattern:Parse("peach,jink")
local slash = Fk:cloneCard("slash")
lu.assertTrue(exp1:match(slash))
lu.assertFalse(exp2:match(slash))
end,
testMatchWithType = function()
local exp3 = Exppattern:Parse(".|.|.|.|.|normal_trick")
lu.assertFalse(exp3:matchExp("slash,jink"))
lu.assertTrue(exp3:matchExp("peach,ex_nihilo"))
local basic = Exppattern:Parse(".|.|.|.|.|basic")
lu.assertFalse(basic:matchExp("nullification"))
lu.assertTrue(basic:matchExp("slash,vine"))
lu.assertTrue(Exppattern:Parse(".|.|.|.|.|armor"):matchExp("slash,vine"))
lu.assertTrue(Exppattern:Parse(".|.|.|.|.|trick"):matchExp("lightning"))
lu.assertFalse(Exppattern:Parse(".|.|.|.|.|delayed_trick"):matchExp("savage_assault"))
end,
testMatchNeg = function()
lu.assertError(function() Exppattern:Parse("^(a,|1)") end)
local not_nul = Exppattern:Parse("^nullification")
local not_slash_jink = Exppattern:Parse("^(slash,jink)")
local not_basic = Exppattern:Parse(".|.|.|.|.|^basic")
local not_black = Exppattern:Parse(".|.|^(spade,club)")
local slash_jink = Exppattern:Parse("slash,jink")
local no_slash_jink = Exppattern:Parse("^(slash,jink)|.|.|.|.|basic")
local slash = Fk:cloneCard("slash")
lu.assertFalse(not_nul:matchExp("nullification"))
lu.assertTrue(not_basic:matchExp("nullification"))
lu.assertFalse(not_slash_jink:matchExp("jink"))
lu.assertTrue(not_nul:match(slash))
lu.assertFalse(not_slash_jink:match(slash))
lu.assertFalse(not_basic:match(slash))
lu.assertTrue(not_nul:matchExp("peach"))
lu.assertFalse(not_basic:matchExp(no_slash_jink))
lu.assertTrue(not_slash_jink:matchExp(not_basic))
lu.assertFalse(slash_jink:matchExp(not_slash_jink))
lu.assertFalse(not_black:matchExp("slash|A~Q|spade"))
lu.assertTrue(not_black:matchExp("vine|10|^club"))
end,
}

View File

@ -0,0 +1,24 @@
---@diagnostic disable
local testmode = GameMode:new("testmode", 2, 8)
testmode.logic = function()
---@type GameLogic
local l = GameLogic:subclass("testmodelogic")
--[[
function l:chooseGenerals()
local room = self.room
room.current = room.players[1]
for _, p in ipairs(room.players) do
room:setPlayerGeneral(p, "blank_shibing")
end
end
--]]
function l:action()
if type(self.room.action) == "function" then
self.room.action()
end
end
return l
end
Fk.game_modes["testmode"] = testmode
Fk:loadDisabled()

28
test/lua/core/util.lua Normal file
View File

@ -0,0 +1,28 @@
-- 针对 core/util.lua 的一些测试用例
-- 总感觉没啥好测试的
TestUtil = {
testMisc = function()
lu.assertError(function()
Util.DummyTable.a = 4
end)
end,
testString = function()
lu.assertIs("He" + "is", "Heis")
local utf8string = "刘备,天下枭雄"
lu.assertEquals(utf8string:len(), 7)
lu.assertEquals(utf8string:rawlen(), 21)
lu.assertEquals(#utf8string, 21)
local s = "gfsdf%kj.\\ts4!!,34':"
lu.assertFalse(s:endsWith("%"))
end,
testTable = function()
local t = {1, 2, 5}
table.insertIfNeed(t, 2)
lu.assertEquals(t, {1, 2, 5})
end,
}

View File

@ -1,6 +1,7 @@
-- 为纯lua的测试环境捏一个虚拟的fk以便于测试 -- 为纯lua的测试环境捏一个虚拟的fk以便于测试
local fk = {} local fk = {}
local testFail = false
local os, io = os, io local os, io = os, io
@ -20,4 +21,53 @@ function fk.QmlBackend_exists(dir)
return f:read("*a"):startsWith("OK") return f:read("*a"):startsWith("OK")
end end
function fk.GetDisabledPacks()
return "[]"
--[[
local pkgs = fk.QmlBackend_ls("packages")
table.removeOne(pkgs, "test")
return json.encode(pkgs)
--]]
end
function fk.qCritical(msg) print(string.char(27) .. "[91m[Test/C]" ..
string.char(27) .. "[0m " .. msg); testFail = true end
function fk.qInfo(msg) print(string.char(27) .. "[95m[Test/I]" ..
string.char(27) .. "[0m " .. msg) end
function fk.qWarning(msg) print(string.char(27) .. "[94m[Test/W]" ..
string.char(27) .. "[0m " .. msg) end
function fk.qDebug(msg) print(string.char(27) .. "[90m[Test/D]" ..
string.char(27) .. "[0m " .. msg) end
function fk.GetMicroSecond()
return os.time() * 100000
end
function fk.roomtest(croom, f)
local room = Room(croom)
RoomInstance = room
room.action = function() f(room) end
while true do
local over = room:resume()
if over then break else room.in_delay = false end
end
RoomInstance = nil
local fail = testFail
if fail then testFail = false end
lu.assertFalse(fail, "Test failed!")
end
-- terminal color
fk.BOLD = string.char(27) .. "[1m"
fk.GRAY = string.char(27) .. "[90m"
fk.RED = string.char(27) .. "[91m"
fk.GREEN = string.char(27) .. "[92m"
fk.BLUE = string.char(27) .. "[94m"
fk.YELLOW = string.char(27) .. "[93m"
fk.DEEPBLUE = string.char(27) .. "[34m"
fk.PURPLE = string.char(27) .. "[95m"
fk.CYAN = string.char(27) .. "[96m"
fk.RST = string.char(27) .. "[0m"
fk.CARET = string.char(27) .. "[92m => ".. fk.RST
return fk return fk

23
test/lua/lib/room.lua Normal file
View File

@ -0,0 +1,23 @@
-- 仿效 swig 中 class Room 的接口制作,为了便于测试
local Room = class("fk.Room")
function Room:getId() return 1 end
function Room:getPlayers() return self.players end
function Room:getTimeout() return 15 end
function Room:updateWinRate() end
function Room:gameOver() end
function Room:settings()
return json.encode{
enableFreeAssign = false,
enableDeputy = false,
gameMode = "testmode",
disabledPack = {},
generalNum = 2,
luckTime = 0,
password = "",
disabledGenerals = {},
}
end
return Room

View File

View File

@ -0,0 +1,612 @@
-- 仿效 swig 中 class ServerPlayer 的接口制作,为了便于测试
local ServerPlayer = class("fk.ServerPlayer")
local io = fk.io
fk.Player_Invalid = 0
fk.Player_Online = 1
fk.Player_Trust = 2
fk.Player_Run = 3
fk.Player_Leave = 4
fk.Player_Robot = 5
fk.Player_Offline = 6
local function colorConvert(log)
log = log:gsub('<font color="#0598BC"><b>', string.char(27) .. "[34;1m")
log = log:gsub('<font color="#0C8F0C"><b>', string.char(27) .. "[32;1m")
log = log:gsub('<font color="#CC3131"><b>', string.char(27) .. "[31;1m")
log = log:gsub('<font color="red"><b>', string.char(27) .. "[31;1m")
log = log:gsub('<font color="black"><b>', string.char(27) .. "[0;1m")
log = log:gsub('<font color="#0598BC">', string.char(27) .. "[34m")
log = log:gsub('<font color="blue">', string.char(27) .. "[34m")
log = log:gsub('<font color="#0C8F0C">', string.char(27) .. "[32m")
log = log:gsub('<font color="green">', string.char(27) .. "[32m")
log = log:gsub('<font color="#CC3131">', string.char(27) .. "[31m")
log = log:gsub('<font color="red">', string.char(27) .. "[31m")
log = log:gsub('<font color="grey">', string.char(27) .. "[90m")
log = log:gsub("<b>", fk.BOLD)
log = log:gsub("</b></font>", fk.RST)
log = log:gsub("</font>", fk.RST)
log = log:gsub("<b>", fk.BOLD)
log = log:gsub("</b>", fk.RST)
log = log:gsub("<br>", "\n")
log = log:gsub("<br/>", "\n")
log = log:gsub("<br />", "\n")
return log
end
---@param msg LogMessage
local function parseMsg(msg, nocolor)
local self = Fk:currentRoom()
local data = msg
local function getPlayerStr(pid, color)
if nocolor then color = "white" end
if not pid then
return ""
end
local p = self:getPlayerById(pid)
local str = '<font color="%s"><b>%s</b></font>'
if p.general == "anjiang" and (p.deputyGeneral == "anjiang"
or not p.deputyGeneral) then
local ret = Fk:translate("seat#" .. p.seat)
return string.format(str, color, ret)
end
local ret = p.general
ret = Fk:translate(ret)
if p.deputyGeneral and p.deputyGeneral ~= "" then
ret = ret .. "/" .. Fk:translate(p.deputyGeneral)
end
ret = string.format(str, color, ret)
return ret
end
local from = getPlayerStr(data.from, "#0C8F0C")
local to = data.to or Util.DummyTable
local to_str = {}
for _, id in ipairs(to) do
table.insert(to_str, getPlayerStr(id, "#CC3131"))
end
to = table.concat(to_str, ", ")
local card = data.card or Util.DummyTable
local allUnknown = true
local unknownCount = 0
for _, id in ipairs(card) do
if id ~= -1 then
allUnknown = false
else
unknownCount = unknownCount + 1
end
end
if allUnknown then
card = ""
else
local card_str = {}
for _, id in ipairs(card) do
table.insert(card_str, Fk:getCardById(id, true):toLogString())
end
if unknownCount > 0 then
table.insert(card_str, Fk:translate("unknown_card")
.. unknownCount == 1 and "x" .. unknownCount or "")
end
card = table.concat(card_str, ", ")
end
local function parseArg(arg)
arg = arg or ""
arg = Fk:translate(arg)
arg = string.format('<font color="%s"><b>%s</b></font>', nocolor and "white" or "#0598BC", arg)
return arg
end
local arg = parseArg(data.arg)
local arg2 = parseArg(data.arg2)
local arg3 = parseArg(data.arg3)
local log = Fk:translate(data.type)
log = string.gsub(log, "%%from", from)
log = string.gsub(log, "%%to", to)
log = string.gsub(log, "%%card", card)
log = string.gsub(log, "%%arg2", arg2)
log = string.gsub(log, "%%arg3", arg3)
log = string.gsub(log, "%%arg", arg)
return colorConvert(log)
end
local function processPrompt(prompt)
local data = prompt:split(":")
local room = Fk:currentRoom()
local raw = Fk:translate(data[1]);
local src = tonumber(data[2]);
local dest = tonumber(data[3]);
if src then raw = raw:gsub("%%src", Fk:translate(room:getPlayerById(src).general)) end
if dest then raw = raw:gsub("%%dest", Fk:translate(room:getPlayerById(dest).general)) end
if data[5] then raw = raw:gsub("%%arg2", Fk:translate(data[5])) end
if data[4] then raw = raw:gsub("%%arg", Fk:translate(data[4])) end
return colorConvert(raw)
end
--- separated moves to many moves(one card per move)
---@param moves CardsMoveStruct[]
local function separateMoves(moves)
local ret = {} ---@type CardsMoveInfo[]
for _, move in ipairs(moves) do
for _, info in ipairs(move.moveInfo) do
table.insert(ret, {
ids = {info.cardId},
from = move.from,
to = move.to,
toArea = move.toArea,
fromArea = info.fromArea,
moveReason = move.moveReason,
specialName = move.specialName,
fromSpecialName = info.fromSpecialName,
proposer = move.proposer,
})
end
end
return ret
end
--- merge separated moves that information is the same
local function mergeMoves(moves)
local ret = {}
local temp = {}
for _, move in ipairs(moves) do
local info = string.format("%q,%q,%q,%q,%s,%s,%q",
move.from, move.to, move.fromArea, move.toArea,
move.specialName, move.fromSpecialName, move.proposer)
if temp[info] == nil then
temp[info] = {
ids = {},
from = move.from,
to = move.to,
fromArea = move.fromArea,
toArea = move.toArea,
moveReason = move.moveReason,
specialName = move.specialName,
fromSpecialName = move.fromSpecialName,
proposer = move.proposer,
}
end
table.insert(temp[info].ids, move.ids[1])
end
for _, v in pairs(temp) do
table.insert(ret, v)
end
return ret
end
local function sendMoveCardLog(move)
local client = Fk:currentRoom() ---@class Client
if #move.ids == 0 then return end
local hidden = table.contains(move.ids, -1)
local msgtype
if move.toArea == Card.PlayerHand then
if move.fromArea == Card.PlayerSpecial then
print(parseMsg({
type = "$GetCardsFromPile",
from = move.to,
arg = move.fromSpecialName,
arg2 = #move.ids,
card = move.ids,
}))
elseif move.fromArea == Card.DrawPile then
print(parseMsg({
type = "$DrawCards",
from = move.to,
card = move.ids,
arg = #move.ids,
}))
elseif move.fromArea == Card.Processing then
print(parseMsg({
type = "$GotCardBack",
from = move.to,
card = move.ids,
arg = #move.ids,
}))
elseif move.fromArea == Card.DiscardPile then
print(parseMsg({
type = "$RecycleCard",
from = move.to,
card = move.ids,
arg = #move.ids,
}))
elseif move.from then
print(parseMsg({
type = "$MoveCards",
from = move.from,
to = { move.to },
arg = #move.ids,
card = move.ids,
}))
else
print(parseMsg({
type = "$PreyCardsFromPile",
from = move.to,
card = move.ids,
arg = #move.ids,
}))
end
elseif move.toArea == Card.PlayerEquip then
print(parseMsg({
type = "$InstallEquip",
from = move.to,
card = move.ids,
}))
elseif move.toArea == Card.PlayerJudge then
if move.from ~= move.to and move.fromArea == Card.PlayerJudge then
print(parseMsg({
type = "$LightningMove",
from = move.from,
to = { move.to },
card = move.ids,
}))
elseif move.from then
print(parseMsg({
type = "$PasteCard",
from = move.from,
to = { move.to },
card = move.ids,
}))
end
elseif move.toArea == Card.PlayerSpecial then
print(parseMsg({
type = "$AddToPile",
arg = move.specialName,
arg2 = #move.ids,
from = move.to,
card = move.ids,
}))
elseif move.fromArea == Card.PlayerEquip then
print(parseMsg({
type = "$UninstallEquip",
from = move.from,
card = move.ids,
}))
-- elseif move.toArea == Card.Processing then
-- nop
elseif move.from and move.toArea == Card.DrawPile then
msgtype = hidden and "$PutCard" or "$PutKnownCard"
print(parseMsg({
type = msgtype,
from = move.from,
card = move.ids,
arg = #move.ids,
}))
elseif move.toArea == Card.DiscardPile then
if move.moveReason == fk.ReasonDiscard then
if move.proposer and move.proposer ~= move.from then
print(parseMsg({
type = "$DiscardOther",
from = move.from,
to = {move.proposer},
card = move.ids,
arg = #move.ids,
}))
else
print(parseMsg({
type = "$DiscardCards",
from = move.from,
card = move.ids,
arg = #move.ids,
}))
end
elseif move.moveReason == fk.ReasonPutIntoDiscardPile then
print(parseMsg({
type = "$PutToDiscard",
card = move.ids,
arg = #move.ids,
}))
end
-- elseif move.toArea == Card.Void then
-- nop
end
end
function ServerPlayer:initialize(id)
self.id = id
self.screenName = "player" .. id
self.state = fk.Player_Online
self.died = false
self._busy = false
self._thinking = false
end
function ServerPlayer:getId() return self.id end
function ServerPlayer:setId(id) self.id = id end
function ServerPlayer:getScreenName() return self.screenName end
function ServerPlayer:getAvatar() return "zhouyu" end
function ServerPlayer:getState() return self.state end
function ServerPlayer:setState(state) self.state = state end
function ServerPlayer:isDied() return self.died end
function ServerPlayer:setDied(died) self.died = died end
local function tr(str)
return string.format("%s(%s)", Fk:translate(str), str)
end
local function trcid(cid)
local card = Fk:getCardById(cid)
return colorConvert(card:toLogString()) .. "(" .. cid .. ")"
end
local function help_rp_yn()
print(fk.GRAY .." (reply格式n或N表示取消其余确定如reply n)" .. fk.RST)
end
local function help_rp_choices()
print(fk.GRAY .." (reply格式直接输入文本有多个则用空格分隔reply kill)" .. fk.RST)
end
local request_processors = {
["AskForGeneral"] = function(j)
local data = json.decode(j)
io.write(string.format("请选择 %d 名武将: ", data[2]))
for _, g in ipairs(data[1]) do
io.write(tr(g) .. " ")
end
io.write("\n")
help_rp_choices()
end,
["PlayCard"] = function()
print("出牌阶段,请进行操作")
end,
["AskForSkillInvoke"] = function(j)
local data = json.decode(j)
local skill = data[1]
local prompt = data[2]
if prompt then prompt = processPrompt(prompt)
else prompt = string.format("你是否发动技能 %s", tr(skill)) end
print(prompt)
help_rp_yn()
end,
["AskForUseCard"] = function(j)
local cardname, pattern, prompt, _, extra_data, disabledSkillNames =
table.unpack(json.decode(j))
if prompt then prompt = processPrompt(prompt)
else prompt = string.format("请使用卡牌 %s", tr(cardname)) end
print(prompt)
end,
["AskForUseActiveSkill"] = function(j)
local skill_name, prompt, cancelable, extra_data =
table.unpack(json.decode(j))
if prompt then prompt = processPrompt(prompt)
else prompt = string.format("请使用技能 %s", tr(skill_name)) end
print(prompt)
end,
}
function ServerPlayer:doRequest(cmd, j)
if self.id == 1 then
io.write(fk.YELLOW .. "[!] " .. fk.RST)
if request_processors[cmd] then
request_processors[cmd](j)
else
print(cmd, j)
end
end
end
local cmd_help = function()
io.write(""
.. fk.BLUE .." <回车>".. fk.CARET .."重复执行上一条命令\n"
.. fk.BLUE .." help/h".. fk.CARET .."查看这条帮助\n"
.. fk.BLUE .." dbg".. fk.CARET .."使用Debugger\n"
.. fk.BLUE .." reply/rp".. fk.CARET .."发送答复需手搓JSON除非有特殊提示注意无合法性检测\n"
.. fk.GRAY .." -------------------------------\n" .. fk.RST
.. fk.BLUE .." room/r".. fk.CARET .."查看房间概况\n"
.. fk.BLUE .." player/p".. fk.CARET .."查看玩家概况参数为玩家id默认1\n"
.. fk.BLUE .." skill/s".. fk.CARET .."查看技能描述\n"
)
end
local reply_processors = {
["AskForGeneral"] = function(args)
return json.encode(args)
end,
["AskForSkillInvoke"] = function(args)
local ret = args[1]
if ret == 'n' or ret == 'N' then
return ''
end
return '1'
end,
}
local cmd_reply = function(args)
if #args == 0 then return "" end
local room = Fk:currentRoom()
local player = room:getPlayerById(1)
local command = player.ai_data.command
if reply_processors[command] then
return reply_processors[command](args)
else
return args[1]
end
end
local function getRoleStr(str)
if str == "lord" then
return fk.RED .. fk.BOLD .. "" .. fk.RST
elseif str == "loyalist" then
return fk.YELLOW .. fk.BOLD .. "" .. fk.RST
elseif str == "rebel" then
return fk.GREEN .. fk.BOLD .. "" .. fk.RST
elseif str == "renegade" then
return fk.BLUE .. fk.BOLD .. "" .. fk.RST
end
end
local function writeCardList(cidlist)
for _, id in ipairs(cidlist) do
io.write(trcid(id))
io.write(" ")
end
end
local cmd_room = function()
local room = Fk:currentRoom()
if not room.players[3].shield then return end
printf("第%d轮 牌堆剩%d张", room.tag['RoundCount'], #room.draw_pile)
print("\n玩家列表:")
for _, p in ipairs(room.players) do
io.write(string.format("%s%d ID=%d %s %s %d|%d/%d %d牌",
p.id == 1 and "*" or "", p.seat, p.id, getRoleStr(p.role),
(p.dead and fk.GRAY or fk.GREEN) .. Fk:translate(p.general) .. fk.RST,
p.shield, p.hp, p.maxHp, #p.player_cards[Player.Hand]))
if #p.player_cards[Player.Equip] > 0 then io.write(" 有装备") end
if #p.player_cards[Player.Judge] > 0 then io.write(" 有判定") end
io.write("\n")
end
--[[
print("\n摸牌堆:")
writeCardList(room.draw_pile)
print("\n弃牌堆:")
writeCardList(room.discard_pile)
print("\nVoid牌堆")
writeCardList(room.void)
--]]
end
local cmd_player = function(args)
local room = Fk:currentRoom()
if #args == 0 then table.insert(args, "1") end
for _, sid in ipairs(args) do
local p = room:getPlayerById(tonumber(sid))
io.write(fk.BOLD .. tostring(p) .. fk.RST .. " " .. getRoleStr(p.role))
if p.general and p.general ~= "" then
io.write(" " .. tr(p.general))
else
io.write("\n"); return
end
if p.deputyGeneral and p.deputyGeneral ~= "" then
io.write("/" .. tr(p.deputyGeneral))
end
io.write(string.format(" HP: %d|%d/%d", p.shield, p.hp, p.maxHp))
if p.dead then io.write(" 已死亡") end
io.write("\n")
if #p.player_cards[Player.Hand] > 0 then
io.write(string.format("共%d张手牌: ", #p.player_cards[Player.Hand]))
writeCardList(p.player_cards[Player.Hand])
io.write("\n")
else
print("没有手牌")
end
if #p.player_cards[Player.Equip] > 0 then
io.write("装备区内的牌: ")
writeCardList(p.player_cards[Player.Equip])
io.write("\n")
end
if #p.player_cards[Player.Judge] > 0 then
io.write("判定区内的牌: ")
writeCardList(p.player_cards[Player.Judge])
io.write("\n")
end
io.write("技能:")
for _, s in ipairs(p.player_skills) do
if s.visible then io.write(tr(s.name) .. " ") end
end
io.write("\n")
end
end
local cmd_skill = function(args)
for _, s in ipairs(args) do
print(fk.BOLD .. tr(s) .. fk.RST)
print(colorConvert(Fk:getDescription(s)))
end
end
local cmd_card = function(args)
for _, s in ipairs(args) do
local cid = tonumber(s)
if not cid then return end
local c = Fk:getCardById(cid, true)
if not c then return end
print(fk.BOLD .. trcid(cid) .. fk.RST)
print(colorConvert(Fk:getDescription(c.name)))
end
end
local command_table = {
help = cmd_help, h = cmd_help,
dbg = function() dbg() end,
reply = cmd_reply, rp = cmd_reply,
room = cmd_room, r = cmd_room,
player = cmd_player, p = cmd_player,
skill = cmd_skill, s = cmd_skill,
card = cmd_card, c = cmd_card,
}
local last_cmd = "help"
function ServerPlayer:waitForReply()
-- dbg() 时的便利变量
local room = RoomInstance
local logic = room.logic
local player = room:getPlayerById(self.id)
if self.id == 1 then
while true do
io.write(string.char(27) .. "[95m(FkTest) " .. fk.RST)
io.flush()
local line = io.read()
if line == nil then break end -- Ctrl-D
local args = line:split(" ")
for i = #args, 1, -1 do
if args[i] == "" then table.remove(args, i) end
end
local command = table.remove(args, 1)
if command == nil then
command = last_cmd
else
last_cmd = command
end
local f = command_table[command]
if f then
local ret = f(args)
if ret then return ret end
elseif command then
print(fk.RED .. "unknown command '" .. command .. "'" .. fk.RST)
end
end
end
return ""
end
function ServerPlayer:doNotify(cmd, j)
if self.id ~= 1 then
return
end
if cmd == "GameLog" then
print(parseMsg(json.decode(j)))
elseif cmd == "MoveCards" then
local raw_moves = json.decode(j)
local separated = separateMoves(raw_moves)
local merged = mergeMoves(separated)
for _, move in ipairs(merged) do
sendMoveCardLog(move)
end
elseif cmd == "GameOver" then
print(cmd, j)
end
end
function ServerPlayer:busy() return self._busy end
function ServerPlayer:setBusy(b) self._busy = b end
function ServerPlayer:thinking() return self._thinking end
function ServerPlayer:setThinking(t) self._thinking = t end
function ServerPlayer:emitKick()
self.state = fk.Player_Offline
end
function ServerPlayer:getGameData()
return {[0]=0,0,0,0,at=function(t,k)return t[k]end}
end
return ServerPlayer

View File

@ -1,20 +1,6 @@
TestExppattern = { -- 针对 core/exppattern.lua 的一些测试用例
testUtil = function()
local table1 = {1, 3, 5, 8}
local table2 = {2, 3, 5, 7}
p(table1)
p(table2)
p(table.connect(table1, table2))
p(table1)
p(table2)
p(table.connectIfNeed(table1, table2))
p(table1)
p(table2)
p(table.slice(table1,3,4))
p(table.slice(table1,1,6))
p(table.slice(table1,-2,-1))
end,
TestExppattern = {
testMatchExp = function() testMatchExp = function()
local exp1 = Exppattern:Parse("slash,jink") local exp1 = Exppattern:Parse("slash,jink")
lu.assertTrue(exp1:matchExp("peack,jink")) lu.assertTrue(exp1:matchExp("peack,jink"))
@ -64,5 +50,4 @@ TestExppattern = {
lu.assertFalse(not_black:matchExp("slash|A~Q|spade")) lu.assertFalse(not_black:matchExp("slash|A~Q|spade"))
lu.assertTrue(not_black:matchExp("vine|10|^club")) lu.assertTrue(not_black:matchExp("vine|10|^club"))
end, end,
} }

41
test/lua/play.lua Normal file
View File

@ -0,0 +1,41 @@
-- 在命令行中玩单机版FK吧在游戏目录下 lua test/lua/play.lua
-- 只能在Linux或是Windows-MSYS2之类的环境运行
---@diagnostic disable: lowercase-global
package.path = package.path .. ";./test/lua/lib/?.lua"
lu = require('luaunit')
fk = require('fk')
fk.os = os
fk.io = io
local banner =
fk.CYAN .. [[ ______ __ __ _ ____]] .. fk.RST .. "\n" ..
fk.CYAN .. [[ / ____/_______ ___ / //_/(_) / /]] .. fk.RST .. "\n" ..
fk.CYAN .. [[ / /_ / ___/ _ \/ _ \/ ,< / / / / ]] .. fk.RST .. " 命令行版本新月杀,仅供测试用\n" ..
fk.BLUE .. [[ / __/ / / / __/ __/ /| |/ / / / ]] .. fk.RST .. "默认五人测试模式请手动修改相关Lua文件\n" ..
fk.BLUE .. [[/_/ /_/ \___/\___/_/ |_/_/_/_/ ]] .. fk.RST .. "\n"
print(banner)
-- load FreeKill core
dofile 'lua/freekill.lua'
fk.qlist = ipairs
dofile 'lua/client/i18n/init.lua'
-- load test cases
dofile 'test/lua/core/util.lua'
dofile 'test/lua/core/pattern.lua'
dofile 'test/lua/core/testmode.lua'
-- server tests
dofile 'lua/server/scheduler.lua'
Room = require 'server.room'
fk.Room = require 'test/lua/lib/room'
fk.ServerPlayer = require 'test/lua/lib/serverplayer'
dofile 'test/lua/server/scheduler.lua'
dofile 'test/lua/server/logic.lua'
_TestGameLogic.setup()
_TestGameLogic.testTrigger()
_TestGameLogic.tearDown()

View File

@ -1,14 +1,37 @@
-- Run tests with `lua5.4 test/lua/run.lua` -- Run tests with `lua5.4 test/lua/run.lua`
-- Can only run under Linux
---@diagnostic disable: lowercase-global ---@diagnostic disable: lowercase-global
package.path = package.path .. ";./test/lua/lib/?.lua" package.path = package.path .. ";./test/lua/lib/?.lua"
lu = require('luaunit') lu = require('luaunit')
local os = os
fk = require('fk') fk = require('fk')
function fk.GetDisabledPacks()
local pkgs = fk.QmlBackend_ls("packages")
table.removeOne(pkgs, "test")
return json.encode(pkgs)
end
fk.os = os
fk.io = io
-- load FreeKill core
dofile 'lua/freekill.lua' dofile 'lua/freekill.lua'
dofile 'test/lua/pattern.lua' fk.qlist = ipairs
dofile 'lua/client/i18n/init.lua'
os.exit( lu.LuaUnit.run() ) -- load test cases
dofile 'test/lua/core/util.lua'
dofile 'test/lua/core/pattern.lua'
dofile 'test/lua/core/testmode.lua'
-- server tests
dofile 'lua/server/scheduler.lua'
Room = require 'server.room'
fk.Room = require 'test/lua/lib/room'
fk.ServerPlayer = require 'test/lua/lib/serverplayer'
dofile 'test/lua/server/scheduler.lua'
dofile 'test/lua/server/logic.lua'
fk.os.exit( lu.LuaUnit.run() )

37
test/lua/server/logic.lua Normal file
View File

@ -0,0 +1,37 @@
-- 针对 lua/server/gamelogic.lua 以及 GameEvent 的测试
local croom
_TestGameLogic = {
setup = function()
croom = fk.Room:new()
croom.players = {
fk.ServerPlayer:new(1),
fk.ServerPlayer:new(2),
fk.ServerPlayer:new(3),
fk.ServerPlayer:new(4),
fk.ServerPlayer:new(5),
}
end,
testTrigger = function()
---@param room Room
fk.roomtest(croom, function(room)
local logic = room.logic
local p = room:getPlayerById(1)
room:handleAddLoseSkills(p, table.concat({
"luoyi", "wansha", "yaoyi", --"dili",
}, "|"))
logic:trigger(fk.GamePrepared)
GameEvent(GameEvent.DrawInitial):exec()
GameEvent(GameEvent.Round):exec()
-- DMG test
local victim = room.alive_players[2]
room:damage{ from = p, to = victim, damage = 20 }
room:damage{ to = p, from = victim, damage = 20 }
end)
end,
tearDown = function() croom = nil end
}

View File

@ -0,0 +1,7 @@
-- 针对 lua/server/scheduler.lua 的测试。
-- 这要怎么测试啊...
TestScheduler = {
testMain = function()
end,
}

28
test/lua/util.lua Normal file
View File

@ -0,0 +1,28 @@
-- 针对 core/util.lua 的一些测试用例
-- 总感觉没啥好测试的
TestUtil = {
testMisc = function()
lu.assertError(function()
Util.DummyTable.a = 4
end)
end,
testString = function()
lu.assertIs("He" + "is", "Heis")
local utf8string = "刘备,天下枭雄"
lu.assertEquals(utf8string:len(), 7)
lu.assertEquals(utf8string:rawlen(), 21)
lu.assertEquals(#utf8string, 21)
local s = "gfsdf%kj.\\ts4!!,34':"
lu.assertFalse(s:endsWith("%"))
end,
testTable = function()
local t = {1, 2, 5}
table.insertIfNeed(t, 2)
lu.assertEquals(t, {1, 2, 5})
end,
}