Commit 5e390917 authored by Michael Büsch's avatar Michael Büsch

Add support for code coverage tracing

Signed-off-by: Michael Büsch's avatarMichael Buesch <m@bues.ch>
parent 130aac4f
......@@ -13,18 +13,19 @@ graft progs
graft submodules
graft tests
include awlsim/gui/icons/pic2py
include awlsimhw_linuxcnc/awlsim.hal
include awlsimhw_pyprofibus.conf
include COPYING.txt
include COMPATIBILITY.md
include COMPATIBILITY.html
include QUICK-START.md
include COMPATIBILITY.md
include COPYING.txt
include QUICK-START.html
include README.md
include QUICK-START.md
include README.html
include TODO.md
include README.md
include TODO.html
include TODO.md
include awlsim-covreport
include awlsim/gui/icons/pic2py
include awlsimhw_linuxcnc/awlsim.hal
include awlsimhw_pyprofibus.conf
prune ./build
prune build
......
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# AWL simulator - Code coverage trace report generator
#
# Copyright 2018 Michael Buesch <m@bues.ch>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
from __future__ import division, absolute_import, print_function, unicode_literals
import sys
import os
import coverage as coverage_mod
def coverageError(msg):
if sys.stderr:
sys.stderr.write(msg + "\n")
sys.stderr.flush()
try:
covReport = sys.argv[1]
covData = sys.argv[2:]
if not covData:
raise IndexError
except IndexError as e:
coverageError("Invalid arguments")
sys.exit(1)
dataPaths = []
for item in covData:
if os.path.isdir(item):
for subItem in os.listdir(item):
dataPaths.append(os.path.join(item, subItem))
else:
dataPaths.append(item)
try:
cov = coverage_mod.Coverage(config_file=False)
cov.combine(data_paths=dataPaths, strict=True)
cov.load()
cov.html_report(directory=covReport)
except coverage_mod.misc.CoverageException as e:
coverageError("Could not generate coverage report: " + str(e))
sys.exit(1)
sys.exit(0)
......@@ -100,6 +100,7 @@ def usage():
print("")
print(" AWLSIM_AFFINITY =0,2,... Comma separated list of host CPU cores")
print(" to run on. Default: all cores.")
print(" AWLSIM_COVERAGE =DATAFILE Enable code coverage tracing.")
def writeStdout(message):
if Logging.loglevel >= Logging.LOG_INFO:
......
from __future__ import division, absolute_import, print_function, unicode_literals
import awlsim_loader.coverage_helper
import awlsim_loader.cython_helper as __cython
__importmod = "awlsim.awlcompiler"
......
from __future__ import division, absolute_import, print_function, unicode_literals
import awlsim_loader.coverage_helper
import awlsim_loader.cython_helper as __cython
__importmod = "awlsim.awloptimizer"
......
from __future__ import division, absolute_import, print_function, unicode_literals
import awlsim_loader.coverage_helper
import awlsim_loader.cython_helper as __cython
__importmod = "awlsim.common"
......
from __future__ import division, absolute_import, print_function, unicode_literals
import awlsim_loader.coverage_helper
import awlsim_loader.cython_helper as __cython
__importmod = "awlsim.core"
......
from __future__ import division, absolute_import, print_function, unicode_literals
import awlsim_loader.coverage_helper
import awlsim_loader.cython_helper as __cython
__importmod = "awlsim.coreclient.client"
......
from __future__ import division, absolute_import, print_function, unicode_literals
import awlsim_loader.coverage_helper
import awlsim_loader.cython_helper as __cython
__importmod = "awlsim.coreserver.server"
......
# -*- coding: utf-8 -*-
#
# AWL simulator - Code coverage tracing support
#
# Copyright 2018 Michael Buesch <m@bues.ch>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
from __future__ import division, absolute_import, print_function, unicode_literals
try:
import coverage as coverage_mod
except ImportError as e:
coverage_mod = None
import os
import sys
import atexit
__all__ = [ ]
coverageInstance = None
def coverageError(msg):
if sys.stderr:
sys.stderr.write(msg + "\n")
sys.stderr.flush()
def coverageTryStart():
global coverageInstance
covPath = os.getenv("AWLSIM_COVERAGE", "")
if covPath:
if not coverage_mod:
coverageError("Code coverage tracing is requested "
"(AWLSIM_COVERAGE), but the Python 'coverage' "
"module was not found. "
"Please install Python 'coverage'.")
return
if not coverageInstance:
atexit.register(coverageStop)
try:
omit = [
"/usr/*",
"awlsim-test",
"awlsim/gui/*",
"awlsim_loader/*",
"libs/*",
"submodules/*",
"tests/*",
]
coverageInstance = coverage_mod.Coverage(
data_file=covPath,
auto_data=True,
branch=False,
#check_preimported=True,
config_file=False,
omit=omit)
coverageInstance.start()
except coverage_mod.misc.CoverageException as e:
coverageError("Coverage tracing exception: " + str(e))
sys.exit(1)
def coverageStop():
global coverageInstance
if coverageInstance:
try:
coverageInstance.stop()
except coverage_mod.misc.CoverageException as e:
coverageError("Coverage tracing exception: " + str(e))
sys.exit(1)
finally:
coverageInstance = None
coverageTryStart()
from __future__ import division, absolute_import, print_function, unicode_literals
import awlsim_loader.coverage_helper
import awlsim_loader.cython_helper as __cython
__importmod = "awlsim.fupcompiler"
......
......@@ -6,9 +6,11 @@ exec apt install \
devscripts \
git \
pypy \
pypy-coverage \
pypy-dev \
python3 \
python3-cffi \
python3-coverage \
python3-dev \
python3-nose \
python3-pyqt5 \
......
......@@ -8,7 +8,7 @@ __run_awlsim_linuxcnc_hal()
shift 3
export EXTRA_PYTHONPATH="$rootdir/libs/linuxcnc_fake_hal"
setup_test_environment "$interpreter"
setup_test_environment "$interpreter" "$awl_file" "${awl_file}.linuxcnc"
local interpreter="$RET"
FAKEHAL_HALFILE="${test_dir}/linuxcnc.hal" \
......
......@@ -41,12 +41,18 @@ die()
exit 1
}
# Create a temporary file. $1=name
# Create a temporary file. $1=name, $2=subdir
maketemp()
{
local prefix="$1"
local subdir="$2"
mktemp --tmpdir="$tmp_dir" awlsim-test-${prefix}.XXXXXX
if [ -z "$subdir" ]; then
local subdir="."
else
mkdir -p "$tmp_dir/$subdir"
fi
mktemp --tmpdir="$tmp_dir" "${subdir}/awlsim-test-${prefix}.XXXXXX"
}
# $1=message
......@@ -153,6 +159,22 @@ check_job_failure()
[ "0" != "$(du -s "$test_fail_file" | cut -f1)" ]
}
wait_for_all_background_jobs()
{
is_parallel_run || return
infomsg "Waiting for background jobs..."
wait
# Print the fail information.
if check_job_failure; then
errormsg
errormsg "===== FAILURES in parallel run: ====="
cat "$test_fail_file" >&2
errormsg "====================================="
global_retval=1
fi
}
# $1=interpreter
# Returns version on stdout as: MAJOR MINOR PATCHLEVEL
get_interpreter_version()
......@@ -218,16 +240,19 @@ check_dos_text_encoding()
fi
}
# $1=interpreter [$2=tested_file]
# $1=interpreter $2=tested_file [$3=test_name]
setup_test_environment()
{
local interpreter="$1"
local tested_file="$2"
local test_name="$3"
local use_cython=0
[ -z "$test_name" ] && local test_name="$tested_file"
local test_name="$(realpath -m --no-symlinks --relative-base="$rootdir" "$test_name" | tr '/\\' _)"
# Check if we want to run on Cython2/3 and set the environment
# for Cython2/3.
local use_cython=0
if [ "$interpreter" = "cython" -o "$interpreter" = "cython2" ] ||\
[ "$interpreter" = "python" -a "$AWLSIM_CYTHON" != "" ] ||\
[ "$interpreter" = "python2" -a "$AWLSIM_CYTHON" != "" ]; then
......@@ -313,6 +338,16 @@ setup_test_environment()
# Disable CPU affinity
unset AWLSIM_AFFINITY
# Setup coverage tracing
if [ $opt_coverage -eq 0 ]; then
unset AWLSIM_COVERAGE
else
local coverage_data_file="$(maketemp "coverage_${test_name}" "$coverage_data_subdir")"
rm "$coverage_data_file"
export AWLSIM_COVERAGE="$coverage_data_file"
fi
RET="$interpreter"
}
......@@ -690,6 +725,14 @@ do_tests()
cleanup_test_environment
# Create an interpreter name suitable as path component
local interpreter_name="$(printf '%s' "$interpreter" | tr '/\\' _)"
# Prepare code coverage directory
coverage_data_subdir="coverage-$interpreter_name"
mkdir -p "$tmp_dir/$coverage_data_subdir" || die "Failed to create coverage data dir"
# Basic interpreter setup. Build Cython modules.
if [ "$interpreter" = "cython" -o "$interpreter" = "cython2" ]; then
have_prog cython && have_prog python2 || {
warn_skipped "$interpreter"
......@@ -744,22 +787,29 @@ do_tests()
infomsg
check_job_failure && break
# Generate code coverage report
if [ $opt_coverage -ne 0 ]; then
# Wait for background jobs to finish
wait_for_all_background_jobs
if [ $global_retval -eq 0 ]; then
infomsg "\nGenerating code coverage report..."
local reportbase="$rootdir/code-coverage-report"
local reportdir="$reportbase/awlsim-coverage-$interpreter_name"
rm -rf "$reportdir"
"$rootdir/awlsim-covreport" \
"$reportdir" \
"$tmp_dir/$coverage_data_subdir/" ||\
die "Failed to generate code coverage report."
fi
fi
[ -n "$opt_interpreter" ] && break
done
if is_parallel_run; then
# This is a parallel run. Wait for all jobs.
infomsg "Waiting for background jobs..."
wait
# Print the fail information.
if check_job_failure; then
errormsg
errormsg "===== FAILURES in parallel run: ====="
cat "$test_fail_file" >&2
errormsg "====================================="
global_retval=1
fi
fi
# Wait for background jobs to finish
wait_for_all_background_jobs
# Print summary
if [ $global_retval -eq 0 ]; then
......@@ -806,6 +856,7 @@ show_help()
infomsg " -l|--loop COUNT Number of test loops to execute."
infomsg " Default: 1"
infomsg " Set to 0 for infinite looping."
infomsg " -c|--coverage Enable code coverage tracing."
}
tmp_dir="/tmp/awlsim-test-$$"
......@@ -830,6 +881,7 @@ opt_renice=
opt_jobs=1
opt_loglevel=2
opt_loop=1
opt_coverage=0
while [ $# -ge 1 ]; do
[ "$(printf '%s' "$1" | cut -c1)" != "-" ] && break
......@@ -880,6 +932,9 @@ while [ $# -ge 1 ]; do
shift
opt_loop="$1"
;;
-c|--coverage)
opt_coverage=1
;;
*)
errormsg "Unknown option: $1"
exit 1
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment