]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
build: added `make coverage` to compute C and Lua code coverage
authorMarek Vavruša <mvavrusa@cloudflare.com>
Sun, 26 Nov 2017 00:54:11 +0000 (16:54 -0800)
committerPetr Špaček <petr.spacek@nic.cz>
Tue, 28 Nov 2017 10:21:16 +0000 (11:21 +0100)
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.

Makefile
doc/build.rst
scripts/luacov_to_info.lua [new file with mode: 0755]

index 8d9644cae64b82e719ef6571c3d70700608cf353..86064e0b0228ef6a8fbd9eb63f30b2fb077c80a5 100644 (file)
--- 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
index d67766c4d38033e9edf7e1612a2b1526aeeda162..0c9a9d033def05dc7fb8aa55dad9aa49dace5add 100644 (file)
@@ -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 ``<dependency>_CFLAGS`` and ``<dependency>_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 (executable)
index 0000000..13bf583
--- /dev/null
@@ -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