From: Marek VavruĊĦa Date: Sun, 26 Nov 2017 00:54:11 +0000 (-0800) Subject: build: added `make coverage` to compute C and Lua code coverage X-Git-Tag: v1.5.1~12^2~6 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1985c6786b43bfc0416ebe801634ebacb2e2cca4;p=thirdparty%2Fknot-resolver.git build: added `make coverage` to compute C and Lua code coverage Currently it gathers gcov and luacov code coverage, and merges it in a single lcov info file. It returns summary at the end which the CI can parse and interpret. It can build a html report later using the data. --- diff --git a/Makefile b/Makefile index 8d9644cae..86064e0b0 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,19 @@ clean: contrib-clean lib-clean daemon-clean client-clean modules-clean \ tests-clean doc-clean bench-clean doc: doc-html lint: - luacheck --codes . + luacheck --codes --formatter TAP . +coverage: $(wildcard */*/luacov.stats.out) + @echo "# C coverage in gcov.c.info" + @lcov --no-external --capture --directory . --output-file gcov.c.info > /dev/null + @if [ ! -z "$^" ]; then \ + echo "# Lua coverage in luacov.stats.out and gcov.lua.info"; \ + cat $^ > luacov.stats.out; \ + ./scripts/luacov_to_info.lua $^ > gcov.lua.info; \ + lcov --add-tracefile gcov.c.info --add-tracefile gcov.lua.info --output-file gcov.total.info; \ + else \ + lcov --add-tracefile gcov.c.info --output-file gcov.total.info; \ + fi + .PHONY: all install check clean doc info # Options diff --git a/doc/build.rst b/doc/build.rst index d67766c4d..0c9a9d033 100644 --- a/doc/build.rst +++ b/doc/build.rst @@ -63,6 +63,7 @@ There are also *optional* packages that enable specific functionality in Knot DN "`libprotobuf-c`_ 1.0+", "``modules/dnstap``", "C bindings for Protobuf." "libfstrm_ 0.2+", "``modules/dnstap``", "Frame Streams data transport protocol." "luacheck_", "``lint``", "Syntax and static analysis checker for Lua." + "luacov_", "``check-config``", "Code coverage analysis for Lua modules." .. [#] Requires C99, ``__attribute__((cleanup))`` and ``-MMD -MP`` for dependency file generation. GCC, Clang and ICC are supported. .. [#] You can use variables ``_CFLAGS`` and ``_LIBS`` to configure dependencies manually (i.e. ``libknot_CFLAGS`` and ``libknot_LIBS``). @@ -250,6 +251,12 @@ Building extras The project can be built with code coverage tracking using the ``COVERAGE=1`` variable. +The `make coverage` target gathers both gcov code coverage for C files, and luacov_ code coverage for Lua files and merges it for analysis. It requires lcov_ to be installed. + +.. code-block:: bash + + $ make coverage + Running unit and integration tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -322,5 +329,7 @@ You can hack on the container by changing the container entrypoint to shell like .. _libprotobuf-c: https://github.com/protobuf-c/protobuf-c/wiki .. _libfstrm: https://github.com/farsightsec/fstrm .. _luacheck: http://luacheck.readthedocs.io +.. _luacov: https://keplerproject.github.io/luacov/ +.. _lcov: http://ltp.sourceforge.net/coverage/lcov.php .. _DESTDIR: https://www.gnu.org/prep/standards/html_node/DESTDIR.html diff --git a/scripts/luacov_to_info.lua b/scripts/luacov_to_info.lua new file mode 100755 index 000000000..13bf583d0 --- /dev/null +++ b/scripts/luacov_to_info.lua @@ -0,0 +1,56 @@ +#!/usr/bin/env luajit + +local luacov = require('luacov') +local ReporterBase = require('luacov.reporter').ReporterBase +local LcovReporter = setmetatable({}, ReporterBase) +LcovReporter.__index = LcovReporter + +function LcovReporter:on_new_file(filename) + self.finfo = self.current_files[filename] or {name=filename, coverage={}} +end + +function LcovReporter:on_mis_line(_, lineno, _) + self.finfo.coverage[lineno] = self.finfo.coverage[lineno] or 0 +end + +function LcovReporter:on_hit_line(_, lineno, _, hits) + self.finfo.coverage[lineno] = (self.finfo.coverage[lineno] or 0) + hits +end + +function LcovReporter:on_end_file() + self.current_files[self.finfo.name] = self.finfo + self.finfo = nil +end + +-- Write out results in lcov format +local function write_lcov_info(files) + for fname, finfo in pairs(files) do + local instrumented, nonzero = 0, 0 + print('TN:') + print(string.format('SF:%s', fname)) + for i, hits in pairs(finfo.coverage) do + print(string.format('DA:%d,%d', i - 1, hits)) + instrumented = instrumented + 1 + if hits > 0 then + nonzero = nonzero + 1 + end + end + print(string.format('LH:%d', nonzero)) + print(string.format('LF:%d', instrumented)) + print('end_of_record') + end +end + +-- Accumulate total coverage +local all_files = {} +for _, fname in ipairs(arg) do + local conf = luacov.load_config() + conf.statsfile = fname + local reporter = assert(LcovReporter:new(conf)) + reporter.current_files = all_files + reporter:run() + reporter:close() +end + +-- Write results +write_lcov_info(all_files) \ No newline at end of file