-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcontrol.lua
More file actions
185 lines (172 loc) · 5.98 KB
/
control.lua
File metadata and controls
185 lines (172 loc) · 5.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
--[[
each mod that requires `coverage.lua` will have a
remote "__coverage_modname" defined for it
with start(testname), stop(), and dump()
data is saved in a table by testname, short_src and line number:
coveragedata = {
levelpath = { modname = "", basepath = "" }
tests = {
[testname] = {
[short_src] = {
lines = {
[linenumber] = count
},
funcs = {
[linedefined] = {
names = {name=true,...},
linedefined = linedefined,
count = count,
},
},
}
}
}
}
--]]
local function callAll(mods,funcname,...)
local results = {}
for name,version in pairs(mods) do
local remotename = "__coverage_" .. name
if remote.interfaces[remotename] then
results[name] = remote.call(remotename,funcname,...)
end
end
if remote.interfaces["__coverage_level"] then
results["level"] = remote.call("__coverage_level",funcname,...)
end
return results
end
local active_mods = nil
local function getActiveMods()
if active_mods then return active_mods end
local json = game.json_to_table(settings.global["coverage-include-modstates"].value)
if json then
active_mods = {}
for _,modname in pairs(json) do
active_mods[modname] = game.active_mods[modname]
end
else
active_mods = game.active_mods
end
return active_mods
end
local function getExcludeMods(excludetype)
local json = game.json_to_table(settings.global[excludetype].value)
local nopathmods = {}
if json then
for _,modname in pairs(json) do
nopathmods[modname] = true
end
end
return nopathmods
end
local runningtestname = nil
local function start(testname)
runningtestname = testname
callAll(getActiveMods(),"start",testname)
end
local function stop()
runningtestname = nil
callAll(getActiveMods(),"stop")
end
local function report()
if runningtestname then stop() end
-- These mod names will remain in __modname__ format, rather than being translated
local nopathmods = getExcludeMods("coverage-nopath-mods")
nopathmods.level = true
nopathmods.base = true
nopathmods.core = true
-- These mods will be omitted from output entirely
local ignoremods = getExcludeMods("coverage-exclude-modfiles")
--dump everything anyone collected, will just be empty for disabled mods unless startup collection
local moddumps = callAll(game.active_mods,"dump")
local outlines = {}
for dumpname,dump in pairs(moddumps) do
for testname,files in pairs(dump.tests) do
for file,fileinfo in pairs(files) do
local modname,filename = file:match("__(.+)__/(.+)")
if not ignoremods[modname] then
outlines[#outlines+1] = string.format("TN:%s [%s]\n",testname,dumpname)
if not modname then
--startup tracing sometimes gives absolute path of the scenario script, turn it back into the usual form...
filename = file:match("currently%-playing/(.+)")
if filename then
modname = "level"
end
end
-- scenario scripts may provide hints to where they came from...
if modname == "level" then
local level = moddumps.level
local levelpath = level and level.levelpath
if levelpath then
modname = levelpath.modname
filename = levelpath.basepath .. filename
end
end
if nopathmods[modname] then
-- we *still* can't identify level properly, so just give up...
-- also, we can't create proper paths for core/base anyway
outlines[#outlines+1] = string.format("SF:__%s__/%s\n",modname,filename)
elseif modname == nil then
--something totally unrecognized?
outlines[#outlines+1] = string.format("SF:%s\n",file)
else
-- we found it! This will be a path relative to the `mods` directory.
local modver = game.active_mods[modname]
outlines[#outlines+1] = string.format("SF:./%s_%s/%s\n",modname,modver,filename)
end
if fileinfo.funcs then
for _,func in pairs(fileinfo.funcs) do
local name,_ = next(func.names)
local nextname
if not name then
name = "[unknown]"
else
nextname,_ = next(func.names,name)
end
if not nextname then
local linedefined = func.linedefined
outlines[#outlines+1] = string.format("FN:%d,%s@%d\n",linedefined,name,linedefined)
outlines[#outlines+1] = string.format("FNDA:%d,%s@%d\n",func.count,name,linedefined)
else
log("too many names")
log(serpent.block(func))
end
end
end
for line,count in pairs(fileinfo.lines) do
if settings.global["coverage-include-nohit-lines"].value or count ~= 0 then
outlines[#outlines+1] = string.format("DA:%d,%d\n",line,count)
end
end
outlines[#outlines+1] = "end_of_record\n"
end
end
end
end
game.write_file("lcov.info",table.concat(outlines))
end
remote.add_interface("coverage",{
start = start,
isrunning = function() return runningtestname end,
stop = stop,
report = report,
})
script.on_init(function()
if runningtestname == "startup" then
runningtestname = nil
callAll(game.active_mods,"stop")
end
end)
script.on_load(function()
if runningtestname == "startup" then
runningtestname = nil
callAll(game.active_mods,"stop")
end
end)
commands.add_command("startCoverage", "Starts coverage counting",
function(command)
start(command.parameter)
end)
commands.add_command("stopCoverage", "Stops coverage counting",stop)
commands.add_command("reportCoverage", "Generate coverage report",report)