RsCov.lua:
--[[
Sample:
-- test/test2/t.lua :
function testt()
print("hello world")
print("bad world")
end
-- test.lua :
require("RsCov")
require("test/test2/t")
RsCov.start_cov( "test/test2/t.lua" )
testt()
local dump = RsCov.dump_file("t.lua")
print(dump)
-- output :
hello world
bad world
Cov:3 Uncov:6 Total:9 Pct:0.33333333333333
1 0
2 0
3 0 function testt()
4 1 print("hello world")
5 1 print("bad world")
6 1 end
7 0
8 0
9 0
]]
file_tbl = file_tbl or {}
rawcoroutinecreate = rawcoroutinecreate or coroutine.create
rawcoroutinewrap = rawcoroutinewrap or coroutine.wrap
rawexit = rawexit or os.exit
IsStarted = IsStarted or false
function trim_path( name )
local s1,s2,s3
s1= name:find("/", 1, true )
while s1 do
s2 = s1
s1 = name:find("/", s1+1, true )
end
if s2 then
s1,s3= name:find(".\\", s2+1, true )
else
s1,s3= name:find(".\\", 1, true )
end
while s1 do
s2 = s3
s1 = name:find(".\\", s2+1, true )
end
if s2 then
name = name:sub( s2+1 )
end
return name
end
local function on_line(_, line_nr)
-- get name of processed file; ignore Lua code loaded from raw strings
local name = debug.getinfo(2, "S").source
if not name:match("^@") then
return
end
name = name:sub(2)
name = trim_path( name )
local file = file_tbl[name]
if not file then
--print( "can't find :" .. name )
return
end
local cov = file.cov
cov[line_nr] = (cov[line_nr] or 0) + 1
end
function stop( )
debug.sethook()
coroutine.create = rawcoroutinecreate
coroutine.wrap = rawcoroutinewrap
os.exit = rawexit
IsStarted = false
file_tbl = {}
end
function start_cov( filename )
local ret = "Ok"
if filename then
ret = add_cov_file( filename )
end
if not IsStarted then
debug.sethook(on_line, "l")
IsStarted = true
coroutine.create = function(...)
local co = rawcoroutinecreate(...)
debug.sethook(co, on_line, "l")
return co
end
coroutine.wrap = function(...)
local co = rawcoroutinecreate(...)
debug.sethook(co, on_line, "l")
return function()
local r = { coroutine.resume(co) }
if not r[1] then
error(r[2])
end
return unpack(r, 2)
end
end
os.exit = function(...)
on_exit()
rawexit(...)
end
end
return ret
end
function clean( name )
if type(name) == "table" then
local ret = "fail with : "
local fail = false
for i,v in pairs( name ) do
if clean( v ) ~= "Ok" then
ret = ret .. v .. ","
fail = true
end
end
if fail then
return ret
else
return "Ok"
end
end
if type(name) ~= "string" then
return "clean file fail, need a string argument."
end
name = trim_path( name )
local file = file_tbl[ name ]
file.cov = {}
return "Ok"
end
function add_cov_file( name )
if type(name) == "table" then
local ret = "fail with : "
local fail = false
for i,v in pairs( name ) do
if add_cov_file( v ) ~= "Ok" then
ret = ret .. v .. ","
fail = true
end
end
if fail then
return ret
else
return "Ok"
end
end
if type(name) ~= "string" then
return "add coverage file fail, need a string argument."
end
local file = {}
for line in io.lines(name) do
table.insert( file, line )
end
local file_len = #file
if file_len == 0 then
return "read coverage file fail for " .. name
end
name = trim_path( name )
file_tbl[ name ] = file
file.cov = {}
--print(name)
--printTable( file )
return "Ok"
end
function dump_file( name )
name = trim_path( name )
local file = file_tbl[name]
if not file then
return "file is not in record"
end
local info = ""
local has_cov = 0
local un_cov = 0
local cov = file.cov
local show = {}
local has_run = false
for k,v in ipairs( file ) do
local run = cov[ k ] or 0
if run == 0 then
show[k] = true
show[k-1] = true
show[k+1] = true
--[[
show[k-2] = true
show[k-3] = true
show[k+2] = true
show[k+3] = true
]]
else
has_run = true
show[k] = false
end
end
if not has_run then
return "file has not been executed."
end
--printTable( show)
for k, v in ipairs( file ) do
local run = cov[ k ] or 0
if run == 0 then
if v ~= "" then
un_cov = un_cov+1
end
else
has_cov = has_cov +1
end
if show[k] then
info = info .. k .. " " .. run .. " " .. v .. "\n"
else
--info = info .. k .. " " .. run .. " " .. "[classified]..." .. "\n"
end
end
local total = un_cov + has_cov
local pct = has_cov/total
return "Cov:" .. tostring(has_cov) .. " Uncov:" .. tostring(un_cov) .. " Total:" .. tostring(total) .. " Pct:" .. tostring(pct) .. "\n" .. info
end
function CovCode(UserInfo, Cmd, ...)
local Useage = [[
启动代码检漏#r
$covcode start,module1,module2... #r
停止代码检漏#r
$covcode stop #r
打印某模块结果#r
$covcode dump,module1,module2,...#r
清除某模块统计结果#r
$covcode clean,module1,module2,...#r
]]
if not Cmd then
return Useage
end
if not RS_COV then
RS_COV = Import("cmd/RsCov.lua")
end
local files = {...}
if Cmd == "start" then
local Ret = RS_COV.start_cov(files)
return true, "#R已经启动代码检漏,执行结果:"..Ret.."。使用$dumpcodecov打印结果,$stop"
end
if Cmd == "stop" then
RS_COV.stop()
return true
end
if Cmd == "dump" then
for _, v in ipairs(files) do
local Ret = RS_COV.dump_file(v)
_DEBUG(Ret)
print(Ret)
end
return true, "结果已经打印到日志中"
end
if Cmd == "clean" then
for _, v in ipairs(files) do
local Ret = RS_COV.clean(v)
print(Ret)
end
return true, "已经清理"
end
return false, Useage
end