Skip to content

Commit

Permalink
HTML Reporter
Browse files Browse the repository at this point in the history
  • Loading branch information
nidorx authored Jul 26, 2022
1 parent 06666f7 commit 17374dc
Show file tree
Hide file tree
Showing 8 changed files with 776 additions and 0 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,17 @@ modules = {
For a full list of options, see
[`luacov.defaults` documentation](https://keplerproject.github.io/luacov/doc/modules/luacov.defaults.html).

## Html reporter

To generate report file as html document, adjust the `.luacov` parameters to

```lua
reporter = "html"
reportfile = "luacov.report.html"
```

![LuaCov Html Reporter](docs/luacov-html-reporter.png)

## Custom reporter engines

LuaCov supports custom reporter engines, which are distributed as separate
Expand Down
Binary file added docs/luacov-html-reporter.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
250 changes: 250 additions & 0 deletions src/luacov/reporter/html.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
local luacov_reporter = require("luacov.reporter")

local reporter = {}

local HTML_HEADER, HTML_FOOTER, HTML_TOTAL, HTML_FILE_HEADER, HTML_FILE_FOOTER, HTML_LINE_HIT, HTML_LINE_MIS

function parse_template(template, values)
local content = template:gsub("{{([a-z_]+)}}", function(key)
return values[key]
end)
return content
end

----------------------------------------------------------------
--- parse template
do
local dir = string.gsub(debug.getinfo(1).source, "^@(.+/)[^/]+$", "%1")
local dir_sep = package.config:sub(1, 1)
if not dir_sep:find("[/\\]") then
dir_sep = "/"
end
local template = require("luacov.reporter.html.template")

--- Removes a prefix from a string if it's present.
-- @param str a string.
-- @param prefix a prefix string.
-- @return original string if does not start with prefix
-- or string without prefix.
local function unprefix(str, prefix)
if str:sub(1, #prefix) == prefix then
return str:sub(#prefix + 1)
else
return str
end
end

-- Returns contents of a file or nil + error message.
local function read_asset(name)
local f, open_err = io.open(dir .. "html" .. dir_sep .. name, "rb")

if not f then
error(unprefix(open_err, name .. ": "))
end

local contents, read_err = f:read("*a")
f:close()

if contents then
return contents
else
error(read_err)
end
end

local assets = {
script = template.SCRIPT,
style = template.STYLE,
}

local assets_content = {}
for tag, assets in pairs(assets) do
for _, name in ipairs(assets) do
local content = read_asset(name)
if (not assets_content[tag]) then
assets_content[tag] = ""
end
if (tag == "script") then
assets_content[tag] = assets_content[tag] .. "\n <script type=\"text/javascript\">\n "
else
assets_content[tag] = assets_content[tag] .. "\n <" .. tag .. ">\n "
end
assets_content[tag] = assets_content[tag] .. content:gsub("\n", "\n ") .. "\n </" .. tag .. ">\n"
end
end

HTML_HEADER = parse_template(template.HTML_HEADER, {
style = assets_content.style
})

HTML_FOOTER = parse_template(template.HTML_FOOTER, {
script = assets_content.script,
timestamp = os.date("%Y-%m-%d %H:%M:%S", os.time())
})

HTML_TOTAL = template.HTML_TOTAL
HTML_FILE_HEADER = template.HTML_FILE_HEADER
HTML_FILE_FOOTER = template.HTML_FILE_FOOTER
HTML_LINE_HIT = template.HTML_LINE_HIT
HTML_LINE_MIS = template.HTML_LINE_MIS

assets = nil
template = nil
assets_content = nil
end
----------------------------------------------------------------

--- Encodes the HTML entities in a string. Helpfull to avoid XSS.
-- @param s (String) String to escape.
function escape_html(s)
return (string.gsub(s, "[}{\">/<'&]", {
["&"] = "&amp;",
["<"] = "&lt;",
[">"] = "&gt;",
['"'] = "&quot;",
["'"] = "&#39;",
["/"] = "&#47;"
}))
end

local HtmlReporter = setmetatable({}, luacov_reporter.ReporterBase)
do
HtmlReporter.__index = HtmlReporter

function HtmlReporter:on_start()
self._summary = {}
self:write(HTML_HEADER)
end

local HTML = ""
local FILE_HTML

local function write_to_html(content)
HTML = HTML .. content
end

local function write_to_file_html(template, values)
FILE_HTML = FILE_HTML .. parse_template(template, values)
end

local function coverage_to_string(hits, missed)
local total = hits + missed

if total == 0 then
total = 1
end

return ("%.2f"):format(hits / total * 100.0)
end

local function coverage_to_number(hits, missed)
return tonumber(coverage_to_string(hits, missed))
end

local function filename_to_id(filename)
return filename:lower():gsub("(.lua)$", ""):gsub("([^a-z0-9_]+)", function(key)
return "-"
end)
end

local function coverage_to_css_class(hits, missed)
local coverageNum = coverage_to_number(hits, missed)
local cssClass
if (coverageNum < 40) then
cssClass = "danger"
elseif (coverageNum < 60) then
cssClass = "warning"
elseif (coverageNum < 75) then
cssClass = "success-low"
elseif (coverageNum < 90) then
cssClass = "success-medium"
else
cssClass = "success-high"
end
return cssClass
end

function HtmlReporter:on_new_file(filename)
FILE_HTML = ""
end

function HtmlReporter:on_file_error(filename, error_type, message)
--luacheck: no self
io.stderr:write(("Couldn't %s %s: %s\n"):format(error_type, filename, message))
end

function HtmlReporter:on_empty_line(filename, lineno, line)
if line == "" then
FILE_HTML = FILE_HTML .. "\n"
else
FILE_HTML = FILE_HTML .. escape_html(line) .. "\n"
end
end

function HtmlReporter:on_mis_line(filename, lineno, line)
write_to_file_html(HTML_LINE_MIS, {
line = escape_html(line),
lineno = lineno,
})
end

function HtmlReporter:on_hit_line(filename, lineno, line, hits)
write_to_file_html(HTML_LINE_HIT, {
hits = hits,
line = escape_html(line),
lineno = lineno,
})
end

function HtmlReporter:on_end_file(filename, hits, miss)

local coverage = coverage_to_string(hits, miss)

write_to_html(parse_template(HTML_FILE_HEADER, {
id = filename_to_id(filename),
hits = hits,
miss = miss,
coverage = coverage,
css_class = coverage_to_css_class(hits, miss),
filename = filename
}))

write_to_file_html(HTML_FILE_FOOTER, {
hits = hits,
miss = miss,
coverage = coverage,
filename = filename,
})
write_to_html(FILE_HTML)
self._summary[filename] = { hits = hits, miss = miss }
end

function HtmlReporter:on_end()
local total_hits, total_missed = 0, 0

for _, filename in ipairs(self:files()) do
local summary = self._summary[filename]
if summary then
local hits, missed = summary.hits, summary.miss
total_hits = total_hits + hits
total_missed = total_missed + missed
end
end

self:write(parse_template(HTML_TOTAL, {
hits = total_hits,
miss = total_missed,
css_class = coverage_to_css_class(total_hits, total_missed),
coverage = tonumber(coverage_to_string(total_hits, total_missed)),
}))
self:write(HTML)
self:write(HTML_FOOTER)
end
end
----------------------------------------------------------------

function reporter.report()
return luacov_reporter.report(HtmlReporter)
end

return reporter
18 changes: 18 additions & 0 deletions src/luacov/reporter/html/lang-lua.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
Copyright (C) 2008 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^(?:\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)|\'(?:[^\'\\]|\\[\s\S])*(?:\'|$))/,null,"\"'"]],[["com",/^--(?:\[(=*)\[[\s\S]*?(?:\]\1\]|$)|[^\r\n]*)/],["str",/^\[(=*)\[[\s\S]*?(?:\]\1\]|$)/],["kwd",/^(?:and|break|do|else|elseif|end|false|for|function|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b/,null],["lit",/^[+-]?(?:0x[\da-f]+|(?:(?:\.\d+|\d+(?:\.\d*)?)(?:e[+\-]?\d+)?))/i],
["pln",/^[a-z_]\w*/i],["pun",/^[^\w\t\n\r \xA0][^\w\t\n\r \xA0\"\'\-\+=]*/]]),["lua"]);
Loading

0 comments on commit 17374dc

Please sign in to comment.