mirror of https://github.com/ARMmbed/mbed-os.git
add coverage filtering
parent
b5ee0c0e27
commit
04c98b0223
|
@ -23,12 +23,14 @@ UNIT TEST BUILD & RUN
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
from unit_test.options import get_options_parser, \
|
from unit_test.options import get_options_parser, \
|
||||||
pretty_print_test_options
|
pretty_print_test_options
|
||||||
from unit_test.settings import DEFAULT_CMAKE_GENERATORS
|
from unit_test.settings import DEFAULT_CMAKE_GENERATORS
|
||||||
from unit_test.test import UnitTestTool
|
from unit_test.test import UnitTestTool
|
||||||
from unit_test.new import UnitTestGeneratorTool
|
from unit_test.new import UnitTestGeneratorTool
|
||||||
|
from unit_test.coverage import CoverageAPI
|
||||||
|
|
||||||
def _mbed_unittest_test(options, cwd, pwd):
|
def _mbed_unittest_test(options, cwd, pwd):
|
||||||
if options is None:
|
if options is None:
|
||||||
|
@ -80,14 +82,28 @@ def _mbed_unittest_test(options, cwd, pwd):
|
||||||
tool.build_tests()
|
tool.build_tests()
|
||||||
|
|
||||||
if options.run_only:
|
if options.run_only:
|
||||||
|
tool.run_tests(filter_regex=options.test_regex)
|
||||||
|
|
||||||
# If code coverage generation:
|
# If code coverage generation:
|
||||||
if options.coverage:
|
if options.coverage:
|
||||||
# Run tests and generate reports
|
cov_api = CoverageAPI(
|
||||||
tool.generate_coverage_report(coverage_output_type=options.coverage,
|
mbed_os_root=os.path.normpath(os.path.join(pwd, "..")),
|
||||||
excludes=[pwd, options.build],
|
build_dir=options.build)
|
||||||
build_path=options.build)
|
|
||||||
else:
|
# Generate reports
|
||||||
tool.run_tests(filter_regex=options.test_regex) # Only run tests
|
outputs = [options.coverage]
|
||||||
|
if options.coverage == "both":
|
||||||
|
outputs = ["html", "xml"]
|
||||||
|
|
||||||
|
excludes = [pwd, options.build]
|
||||||
|
|
||||||
|
if not options.include_headers:
|
||||||
|
excludes.append(re.compile(".*\\.h"))
|
||||||
|
|
||||||
|
cov_api.generate_reports(outputs=outputs,
|
||||||
|
excludes=excludes,
|
||||||
|
filter_regex=options.test_regex,
|
||||||
|
build_path=options.build)
|
||||||
|
|
||||||
def _mbed_unittest_new(options, pwd):
|
def _mbed_unittest_new(options, pwd):
|
||||||
if options is None:
|
if options is None:
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
"""
|
||||||
|
Copyright (c) 2018, Arm Limited
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
GENERATE TEST CODE COVERAGE
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
import posixpath
|
||||||
|
import re
|
||||||
|
|
||||||
|
from .utils import execute_program
|
||||||
|
from .get_tools import get_gcov_program, \
|
||||||
|
get_gcovr_program
|
||||||
|
|
||||||
|
class CoverageAPI(object):
|
||||||
|
"""
|
||||||
|
Generate code coverage reports for unit tests.
|
||||||
|
"""
|
||||||
|
def __init__(self, mbed_os_root=None, build_dir=None):
|
||||||
|
self.root = mbed_os_root
|
||||||
|
|
||||||
|
if not self.root:
|
||||||
|
self.root = os.path.normpath(os.path.join(
|
||||||
|
os.path.dirname(os.path.realpath(__file__)),
|
||||||
|
"../.."))
|
||||||
|
|
||||||
|
self.build_dir = build_dir
|
||||||
|
|
||||||
|
if not self.build_dir:
|
||||||
|
logging.error("No build directory given for CoverageAPI.")
|
||||||
|
|
||||||
|
def _gen_cmd(self, coverage_type, excludes, filter_regex):
|
||||||
|
# Generate coverage generation command:
|
||||||
|
args = [get_gcovr_program(),
|
||||||
|
"--gcov-executable",
|
||||||
|
get_gcov_program(),
|
||||||
|
"-r",
|
||||||
|
os.path.relpath(self.root, self.build_dir),
|
||||||
|
"."]
|
||||||
|
|
||||||
|
if coverage_type == "html":
|
||||||
|
args.extend(["--html",
|
||||||
|
"--html-detail",
|
||||||
|
"-o",
|
||||||
|
"./coverage/index.html"])
|
||||||
|
elif coverage_type == "xml":
|
||||||
|
args.extend(["-x",
|
||||||
|
"-o",
|
||||||
|
"./coverage.xml"])
|
||||||
|
|
||||||
|
# Add exclude filters:
|
||||||
|
for path in excludes:
|
||||||
|
# Use posix separators if path is string
|
||||||
|
if isinstance(path, ("".__class__, u"".__class__)):
|
||||||
|
path = path.replace("\\", "/")
|
||||||
|
args.extend(["-e", path])
|
||||||
|
# Use regular expressions as is
|
||||||
|
elif isinstance(path, type(re.compile(""))):
|
||||||
|
args.extend(["-e", path.pattern])
|
||||||
|
|
||||||
|
# Add include filters:
|
||||||
|
if filter_regex:
|
||||||
|
filters = filter_regex.split(",")
|
||||||
|
|
||||||
|
for filt in filters:
|
||||||
|
regex = "(.+/)?%s" % filt.replace("-", "/")
|
||||||
|
args.extend(["-f", regex])
|
||||||
|
|
||||||
|
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
|
||||||
|
args.append("-v")
|
||||||
|
|
||||||
|
return args
|
||||||
|
|
||||||
|
def generate_reports(self,
|
||||||
|
outputs,
|
||||||
|
excludes=None,
|
||||||
|
filter_regex=None,
|
||||||
|
build_path=None):
|
||||||
|
"""
|
||||||
|
Run tests to generate coverage data, and generate coverage reports.
|
||||||
|
|
||||||
|
Positional arguments:
|
||||||
|
outputs - list of types to generate
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
excludes - list of paths to exclude from the report
|
||||||
|
filter_regex - comma-separated string to use for test filtering
|
||||||
|
build_path - build path
|
||||||
|
"""
|
||||||
|
|
||||||
|
if get_gcovr_program() is None:
|
||||||
|
logging.error("No gcovr tool found in path. \
|
||||||
|
Cannot generate coverage reports.")
|
||||||
|
return
|
||||||
|
|
||||||
|
if build_path is None:
|
||||||
|
build_path = os.getcwd()
|
||||||
|
|
||||||
|
if outputs is None:
|
||||||
|
outputs = []
|
||||||
|
|
||||||
|
if excludes is None:
|
||||||
|
excludes = []
|
||||||
|
|
||||||
|
for output in outputs:
|
||||||
|
if output == "html":
|
||||||
|
# Create build directory if not exist.
|
||||||
|
coverage_path = os.path.join(build_path, "coverage")
|
||||||
|
if not os.path.exists(coverage_path):
|
||||||
|
os.mkdir(coverage_path)
|
||||||
|
|
||||||
|
args = self._gen_cmd(output, excludes, filter_regex)
|
||||||
|
|
||||||
|
if output == "html":
|
||||||
|
execute_program(args,
|
||||||
|
"HTML code coverage report generation failed.",
|
||||||
|
"HTML code coverage report created.")
|
||||||
|
elif output == "xml":
|
||||||
|
execute_program(args,
|
||||||
|
"XML code coverage report generation failed.",
|
||||||
|
"XML code coverage report created.")
|
|
@ -75,6 +75,11 @@ def get_options_parser():
|
||||||
help="Generate code coverage report",
|
help="Generate code coverage report",
|
||||||
dest="coverage")
|
dest="coverage")
|
||||||
|
|
||||||
|
parser.add_argument("--include-headers",
|
||||||
|
action="store_true",
|
||||||
|
help="Include headers to coverage reports, defaults to false.",
|
||||||
|
dest="include_headers")
|
||||||
|
|
||||||
parser.add_argument("-m",
|
parser.add_argument("-m",
|
||||||
"--make-program",
|
"--make-program",
|
||||||
default=get_make_tool(),
|
default=get_make_tool(),
|
||||||
|
@ -140,3 +145,4 @@ Mbed OS unit testing:
|
||||||
if options.coverage:
|
if options.coverage:
|
||||||
logging.info(" [%s] \tGenerate coverage reports", "SET")
|
logging.info(" [%s] \tGenerate coverage reports", "SET")
|
||||||
logging.info(" \t\t - Output: %s", options.coverage)
|
logging.info(" \t\t - Output: %s", options.coverage)
|
||||||
|
logging.info(" \t\t - Include headers: %s", options.include_headers)
|
||||||
|
|
|
@ -27,9 +27,7 @@ from .utils import execute_program
|
||||||
from .get_tools import get_make_tool, \
|
from .get_tools import get_make_tool, \
|
||||||
get_cmake_tool, \
|
get_cmake_tool, \
|
||||||
get_cxx_tool, \
|
get_cxx_tool, \
|
||||||
get_c_tool, \
|
get_c_tool
|
||||||
get_gcov_program, \
|
|
||||||
get_gcovr_program
|
|
||||||
from .settings import DEFAULT_CMAKE_GENERATORS
|
from .settings import DEFAULT_CMAKE_GENERATORS
|
||||||
|
|
||||||
class UnitTestTool(object):
|
class UnitTestTool(object):
|
||||||
|
@ -115,80 +113,6 @@ class UnitTestTool(object):
|
||||||
"Building unit tests failed.",
|
"Building unit tests failed.",
|
||||||
"Unit tests built successfully.")
|
"Unit tests built successfully.")
|
||||||
|
|
||||||
def _get_coverage_script(self, coverage_type, excludes):
|
|
||||||
args = [get_gcovr_program(),
|
|
||||||
"--gcov-executable",
|
|
||||||
get_gcov_program(),
|
|
||||||
"-r",
|
|
||||||
"../..",
|
|
||||||
"."]
|
|
||||||
|
|
||||||
if coverage_type == "html":
|
|
||||||
args.extend(["--html",
|
|
||||||
"--html-detail",
|
|
||||||
"-o",
|
|
||||||
"./coverage/index.html"])
|
|
||||||
elif coverage_type == "xml":
|
|
||||||
args.extend(["-x",
|
|
||||||
"-o",
|
|
||||||
"./coverage.xml"])
|
|
||||||
|
|
||||||
for path in excludes:
|
|
||||||
args.extend(["-e", path.replace("\\", "/")])
|
|
||||||
|
|
||||||
#Exclude header files from report
|
|
||||||
args.extend(["-e", ".*\.h"])
|
|
||||||
|
|
||||||
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
|
|
||||||
args.append("-v")
|
|
||||||
|
|
||||||
return args
|
|
||||||
|
|
||||||
def generate_coverage_report(self,
|
|
||||||
coverage_output_type=None,
|
|
||||||
excludes=None,
|
|
||||||
build_path=None):
|
|
||||||
"""
|
|
||||||
Run tests to generate coverage data, and generate coverage reports.
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.run_tests()
|
|
||||||
|
|
||||||
if get_gcovr_program() is None:
|
|
||||||
logging.error("No gcovr tool found in path. \
|
|
||||||
Cannot generate coverage report.")
|
|
||||||
return
|
|
||||||
|
|
||||||
if build_path is None:
|
|
||||||
build_path = os.getcwd()
|
|
||||||
|
|
||||||
if coverage_output_type is None:
|
|
||||||
logging.warning("No coverage output type give. \
|
|
||||||
Cannot generate coverage reports.")
|
|
||||||
return
|
|
||||||
|
|
||||||
if excludes is None:
|
|
||||||
excludes = []
|
|
||||||
|
|
||||||
if coverage_output_type == "html" or coverage_output_type == "both":
|
|
||||||
# Create build directory if not exist.
|
|
||||||
coverage_path = os.path.join(build_path, "coverage")
|
|
||||||
if not os.path.exists(coverage_path):
|
|
||||||
os.mkdir(coverage_path)
|
|
||||||
|
|
||||||
args = self._get_coverage_script("html", excludes)
|
|
||||||
|
|
||||||
execute_program(args,
|
|
||||||
"HTML code coverage report generation failed.",
|
|
||||||
"HTML code coverage report created.")
|
|
||||||
|
|
||||||
if coverage_output_type == "xml" or coverage_output_type == "both":
|
|
||||||
args = self._get_coverage_script("xml", excludes)
|
|
||||||
|
|
||||||
execute_program(args,
|
|
||||||
"XML code coverage report generation failed.",
|
|
||||||
"XML code coverage report created.")
|
|
||||||
|
|
||||||
def run_tests(self, filter_regex=None):
|
def run_tests(self, filter_regex=None):
|
||||||
"""
|
"""
|
||||||
Run unit tests.
|
Run unit tests.
|
||||||
|
|
Loading…
Reference in New Issue