aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnton Yuzhaninov <citrin+git@citrin.ru>2018-10-27 13:18:04 -0400
committerAnton Yuzhaninov <citrin+git@citrin.ru>2018-10-27 13:36:52 -0400
commit3ebf458996b18c42f78ff35ed7fbd66179dc2eea (patch)
tree4323289a561a2ae30fd6b4dd88d598a5418e05f5
parentc974d1eaaaee82906415b3608089f24461daf86c (diff)
downloadrspamd-3ebf458996b18c42f78ff35ed7fbd66179dc2eea.tar.gz
rspamd-3ebf458996b18c42f78ff35ed7fbd66179dc2eea.zip
Speedup lua coverage collecting for functional test
luacov-coveralls merge mode (-j flag) was created to join reports containing coverage for different source files (e.g. C and Lua code). Coverage for the same file in two report is not merged, instead one source file is added several times to source_files array in JSON. As a result if we use luacov-coveralls -j on report for same source files it ends up spending a lot of time on parsing and dumping big JSON files. This change reduces functional test time from 7+ minutes to 4+ minutes.
-rw-r--r--.drone.yml6
-rw-r--r--test/functional/lib/rspamd.py93
2 files changed, 70 insertions, 29 deletions
diff --git a/.drone.yml b/.drone.yml
index ff1328300..834229210 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -121,10 +121,14 @@ pipeline:
- cd /rspamd/build
# extract coverage data for C code from .gcda files and save it in a format suitable for coveralls.io
- $CI_WORKSPACE/test/tools/gcov_coveralls.py --exclude test --prefix /rspamd/build --prefix $CI_WORKSPACE --out coverage.c.json
+ # luacov-coveralls reads luacov.stats.out generated by functional tests
+ # (see collect_lua_coverage() in test/functional/lib/rspamd.py)
+ # and writes json report for coveralls.io
+ - luacov-coveralls -o coverage.functional.lua.json --dryrun
# * merge coverage for C and Lua code
# * remove prefixes from absolute paths (in luacov-coveralls files), filter test, contrib, e. t.c
# * upload report to coveralls.io
- - $CI_WORKSPACE/test/tools/merge_coveralls.py --root $CI_WORKSPACE --input coverage.c.json unit_test_lua.json lua_coverage_report.json --token=$COVERALLS_REPO_TOKEN
+ - $CI_WORKSPACE/test/tools/merge_coveralls.py --root $CI_WORKSPACE --input coverage.c.json unit_test_lua.json coverage.functional.lua.json --token=$COVERALLS_REPO_TOKEN
when:
branch: master
# don't send coverage report for pull request
diff --git a/test/functional/lib/rspamd.py b/test/functional/lib/rspamd.py
index 6fc1c0e67..bd3e0c382 100644
--- a/test/functional/lib/rspamd.py
+++ b/test/functional/lib/rspamd.py
@@ -10,7 +10,6 @@ import signal
import socket
import sys
import tempfile
-import subprocess
from robot.libraries.BuiltIn import BuiltIn
from robot.api import logger
@@ -296,41 +295,79 @@ def python3_which(cmd, mode=os.F_OK | os.X_OK, path=None):
return None
+def _merge_luacov_stats(statsfile, coverage):
+ """
+ Reads a coverage stats file written by luacov and merges coverage data to
+ 'coverage' dict: { src_file: hits_list }
+
+ Format of the file defined in:
+ https://github.com/keplerproject/luacov/blob/master/src/luacov/stats.lua
+ """
+ with open(statsfile, 'rb') as fh:
+ while True:
+ # max_line:filename
+ line = fh.readline().rstrip()
+ if not line:
+ break
+
+ max_line, src_file = line.split(':')
+ counts = [int(x) for x in fh.readline().split()]
+ assert len(counts) == int(max_line)
+
+ if src_file in coverage:
+ # enlarge list if needed: lenght of list in different luacov.stats.out files may differ
+ old_len = len(coverage[src_file])
+ new_len = len(counts)
+ if new_len > old_len:
+ coverage[src_file].extend([0] * (new_len - old_len))
+ # sum execution counts for each line
+ for l, exe_cnt in enumerate(counts):
+ coverage[src_file][l] += exe_cnt
+ else:
+ coverage[src_file] = counts
+
+
+def _dump_luacov_stats(statsfile, coverage):
+ """
+ Saves data to the luacov stats file. Existing file is overwritted if exists.
+ """
+ src_files = sorted(coverage)
+
+ with open(statsfile, 'wb') as fh:
+ for src in src_files:
+ stats = " ".join(str(n) for n in coverage[src])
+ fh.write("%s:%s\n%s\n" % (len(coverage[src]), src, stats))
+
+
+# File used by luacov to collect coverage stats
+LUA_STATSFILE = "luacov.stats.out"
+
+
def collect_lua_coverage():
- if python3_which("luacov-coveralls") is None:
- logger.info("luacov-coveralls not found, will not collect Lua coverage")
- return
+ """
+ Merges ${TMPDIR}/*.luacov.stats.out into luacov.stats.out
+ Example:
+ | Collect Lua Coverage |
+ """
# decided not to do optional coverage so far
#if not 'ENABLE_LUA_COVERAGE' in os.environ['HOME']:
# logger.info("ENABLE_LUA_COVERAGE is not present in env, will not collect Lua coverage")
# return
- current_directory = os.getcwd()
- report_file = current_directory + "/lua_coverage_report.json"
- old_report = current_directory + "/lua_coverage_report.json.old"
-
tmp_dir = BuiltIn().get_variable_value("${TMPDIR}")
- coverage_files = glob.glob('%s/*.luacov.stats.out' % (tmp_dir))
-
- for stat_file in coverage_files:
- shutil.move(stat_file, "luacov.stats.out")
- # logger.console("statfile: " + stat_file)
- if (os.path.isfile(report_file)):
- shutil.move(report_file, old_report)
- p = subprocess.Popen(["luacov-coveralls", "-o", report_file, "-j", old_report, "--merge", "--dryrun"],
- stdout = subprocess.PIPE, stderr= subprocess.PIPE)
- output,error = p.communicate()
+ coverage = {}
+ input_files = []
- logger.info("luacov-coveralls stdout: " + output)
- logger.info("luacov-coveralls stderr: " + error)
- os.remove(old_report)
- else:
- p = subprocess.Popen(["luacov-coveralls", "-o", report_file, "--dryrun"], stdout = subprocess.PIPE, stderr= subprocess.PIPE)
- output,error = p.communicate()
-
- logger.info("luacov-coveralls stdout: " + output)
- logger.info("luacov-coveralls stderr: " + error)
- os.remove("luacov.stats.out")
+ for f in glob.iglob("%s/*.luacov.stats.out" % tmp_dir):
+ _merge_luacov_stats(f, coverage)
+ input_files.append(f)
+ if input_files:
+ if os.path.isfile(LUA_STATSFILE):
+ _merge_luacov_stats(LUA_STATSFILE, coverage)
+ _dump_luacov_stats(LUA_STATSFILE, coverage)
+ logger.info("%s merged into %s" % (", ".join(input_files), LUA_STATSFILE))
+ else:
+ logger.info("no *.luacov.stats.out files found in %s" % tmp_dir)