Path: blob/main/src/resources/pandoc/datadir/luacov/reporter/html.lua
12923 views
local luacov_reporter = require("luacov.reporter")12local reporter = {}34local HTML_HEADER, HTML_FOOTER, HTML_TOTAL, HTML_FILE_HEADER, HTML_FILE_FOOTER, HTML_LINE_HIT, HTML_LINE_MIS56local function parse_template(template, values)7local content = template:gsub("{{([a-z_]+)}}", function(key)8return values[key]9end)10return content11end1213----------------------------------------------------------------14--- parse template15do16local dir = string.gsub(debug.getinfo(1).source, "^@(.+/)[^/]+$", "%1")17local dir_sep = package.config:sub(1, 1)18if not dir_sep:find("[/\\]") then19dir_sep = "/"20end21local template = require("luacov.reporter.html.template")2223--- Removes a prefix from a string if it's present.24-- @param str a string.25-- @param prefix a prefix string.26-- @return original string if does not start with prefix27-- or string without prefix.28local function unprefix(str, prefix)29if str:sub(1, #prefix) == prefix then30return str:sub(#prefix + 1)31else32return str33end34end3536-- Returns contents of a file or nil + error message.37local function read_asset(name)38local f, open_err = io.open(dir .. "html" .. dir_sep .. name, "rb")3940if not f then41error(unprefix(open_err, name .. ": "))42end4344local contents, read_err = f:read("*a")45f:close()4647if contents then48return contents49else50error(read_err)51end52end5354local asset_types = {55script = template.SCRIPT,56style = template.STYLE,57}5859local assets_content = {}60for tag, assets in pairs(asset_types) do61for _, name in ipairs(assets) do62local content = read_asset(name)63if (not assets_content[tag]) then64assets_content[tag] = ""65end66if (tag == "script") then67assets_content[tag] = assets_content[tag] .. "\n <script type=\"text/javascript\">\n "68else69assets_content[tag] = assets_content[tag] .. "\n <" .. tag .. ">\n "70end71assets_content[tag] = assets_content[tag] .. content:gsub("\n", "\n ") .. "\n </" .. tag .. ">\n"72end73end7475HTML_HEADER = parse_template(template.HTML_HEADER, {76style = assets_content.style77})7879HTML_FOOTER = parse_template(template.HTML_FOOTER, {80script = assets_content.script,81timestamp = os.date("%Y-%m-%d %H:%M:%S", os.time())82})8384HTML_TOTAL = template.HTML_TOTAL85HTML_FILE_HEADER = template.HTML_FILE_HEADER86HTML_FILE_FOOTER = template.HTML_FILE_FOOTER87HTML_LINE_HIT = template.HTML_LINE_HIT88HTML_LINE_MIS = template.HTML_LINE_MIS89end90----------------------------------------------------------------9192--- Encodes the HTML entities in a string. Helpfull to avoid XSS.93-- @param s (String) String to escape.94local function escape_html(s)95return (string.gsub(s, "[}{\">/<'&]", {96["&"] = "&",97["<"] = "<",98[">"] = ">",99['"'] = """,100["'"] = "'",101["/"] = "/"102}))103end104105local HtmlReporter = setmetatable({}, luacov_reporter.ReporterBase)106do107HtmlReporter.__index = HtmlReporter108109function HtmlReporter:on_start()110self._summary = {}111self:write(HTML_HEADER)112end113114local HTML = ""115local FILE_HTML116117local function write_to_html(content)118HTML = HTML .. content119end120121local function write_to_file_html(template, values)122FILE_HTML = FILE_HTML .. parse_template(template, values)123end124125local function coverage_to_string(hits, missed)126local total = hits + missed127128if total == 0 then129total = 1130end131132return ("%.2f"):format(hits / total * 100.0)133end134135local function coverage_to_number(hits, missed)136return tonumber(coverage_to_string(hits, missed))137end138139local function filename_to_id(filename)140return filename:lower():gsub("(.lua)$", ""):gsub("([^a-z0-9_]+)", function(_key)141return "-"142end)143end144145local function coverage_to_css_class(hits, missed)146local coverageNum = coverage_to_number(hits, missed)147local cssClass148if (coverageNum < 40) then149cssClass = "danger"150elseif (coverageNum < 60) then151cssClass = "warning"152elseif (coverageNum < 75) then153cssClass = "success-low"154elseif (coverageNum < 90) then155cssClass = "success-medium"156else157cssClass = "success-high"158end159return cssClass160end161162--luacheck: no self163function HtmlReporter:on_new_file(_filename)164FILE_HTML = ""165end166167function HtmlReporter:on_file_error(filename, error_type, message)168io.stderr:write(("Couldn't %s %s: %s\n"):format(error_type, filename, message))169end170171function HtmlReporter:on_empty_line(_filename, _lineno, line)172if line == "" then173FILE_HTML = FILE_HTML .. "\n"174else175FILE_HTML = FILE_HTML .. escape_html(line) .. "\n"176end177end178179function HtmlReporter:on_mis_line(_filename, lineno, line)180write_to_file_html(HTML_LINE_MIS, {181line = escape_html(line),182lineno = lineno,183})184end185186function HtmlReporter:on_hit_line(_filename, lineno, line, hits)187write_to_file_html(HTML_LINE_HIT, {188hits = hits,189line = escape_html(line),190lineno = lineno,191})192end193194function HtmlReporter:on_end_file(filename, hits, miss)195196local coverage = coverage_to_string(hits, miss)197198write_to_html(parse_template(HTML_FILE_HEADER, {199id = filename_to_id(filename),200hits = hits,201miss = miss,202coverage = coverage,203css_class = coverage_to_css_class(hits, miss),204filename = filename205}))206207write_to_file_html(HTML_FILE_FOOTER, {208hits = hits,209miss = miss,210coverage = coverage,211filename = filename,212})213write_to_html(FILE_HTML)214self._summary[filename] = { hits = hits, miss = miss }215end216217function HtmlReporter:on_end()218local total_hits, total_missed = 0, 0219220for _, filename in ipairs(self:files()) do221local summary = self._summary[filename]222if summary then223local hits, missed = summary.hits, summary.miss224total_hits = total_hits + hits225total_missed = total_missed + missed226end227end228229self:write(parse_template(HTML_TOTAL, {230hits = total_hits,231miss = total_missed,232css_class = coverage_to_css_class(total_hits, total_missed),233coverage = tonumber(coverage_to_string(total_hits, total_missed)),234}))235self:write(HTML)236self:write(HTML_FOOTER)237end238end239----------------------------------------------------------------240241function reporter.report()242return luacov_reporter.report(HtmlReporter)243end244245return reporter246247248