Import Mbed OS hard-float snapshot
This commit is contained in:
16
UNITTESTS/unit_test/__init__.py
Normal file
16
UNITTESTS/unit_test/__init__.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
151
UNITTESTS/unit_test/coverage.py
Normal file
151
UNITTESTS/unit_test/coverage.py
Normal file
@@ -0,0 +1,151 @@
|
||||
"""
|
||||
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
|
||||
from .settings import COVERAGE_OUTPUT_TYPES
|
||||
|
||||
|
||||
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"])
|
||||
else:
|
||||
logging.error("Invalid coverage output type: %s" % coverage_type)
|
||||
|
||||
# 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
|
||||
"""
|
||||
|
||||
# Check for the tool
|
||||
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:
|
||||
# Skip if invalid/unsupported output type
|
||||
if output not in COVERAGE_OUTPUT_TYPES:
|
||||
logging.warning(
|
||||
"Invalid output type. " +
|
||||
"Skip coverage report for type: %s." % output.upper())
|
||||
continue
|
||||
|
||||
if output == "html":
|
||||
# Create a build directory if not exist
|
||||
coverage_path = os.path.join(build_path, "coverage")
|
||||
if not os.path.exists(coverage_path):
|
||||
os.mkdir(coverage_path)
|
||||
coverage_output = os.path.join(coverage_path, "index.html")
|
||||
else:
|
||||
coverage_output = os.path.join(build_path, "coverage.xml")
|
||||
|
||||
# Generate the command
|
||||
args = self._gen_cmd(output, excludes, filter_regex)
|
||||
|
||||
# Run the coverage tool
|
||||
execute_program(
|
||||
args,
|
||||
"%s code coverage report generation failed." % output.upper(),
|
||||
"%s code coverage report created in %s" % (output.upper(), coverage_output))
|
||||
89
UNITTESTS/unit_test/get_tools.py
Normal file
89
UNITTESTS/unit_test/get_tools.py
Normal file
@@ -0,0 +1,89 @@
|
||||
"""
|
||||
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.
|
||||
|
||||
|
||||
GET UNIT TEST TOOLS
|
||||
"""
|
||||
|
||||
import platform
|
||||
|
||||
from .utils import is_tool
|
||||
from .settings import MAKE_PROGRAMS, \
|
||||
CXX_COMPILERS, \
|
||||
C_COMPILERS, \
|
||||
GCOV_PROGRAMS
|
||||
|
||||
def _get_program(programs, default=None):
|
||||
if programs is None:
|
||||
programs = []
|
||||
|
||||
for program in programs:
|
||||
if is_tool(program):
|
||||
return program
|
||||
|
||||
return default
|
||||
|
||||
def get_make_tool():
|
||||
"""
|
||||
Get make program
|
||||
"""
|
||||
|
||||
default_make = "mingw32-make" if (platform.system() == "Windows") \
|
||||
else "make"
|
||||
|
||||
return _get_program(MAKE_PROGRAMS, default=default_make)
|
||||
|
||||
def get_cmake_tool():
|
||||
"""
|
||||
Get cmake program
|
||||
"""
|
||||
|
||||
return _get_program(["cmake"])
|
||||
|
||||
def get_valgrind_tool():
|
||||
"""
|
||||
Get Valgrind program
|
||||
"""
|
||||
|
||||
return _get_program(["valgrind"])
|
||||
|
||||
def get_cxx_tool():
|
||||
"""
|
||||
Get C++ compiler
|
||||
"""
|
||||
|
||||
return _get_program(CXX_COMPILERS, "g++")
|
||||
|
||||
def get_c_tool():
|
||||
"""
|
||||
Get C compiler
|
||||
"""
|
||||
|
||||
return _get_program(C_COMPILERS, "gcc")
|
||||
|
||||
def get_gcov_program():
|
||||
"""
|
||||
Get gcov program
|
||||
"""
|
||||
|
||||
return _get_program(GCOV_PROGRAMS, "gcov")
|
||||
|
||||
def get_gcovr_program():
|
||||
"""
|
||||
Get covr program
|
||||
"""
|
||||
|
||||
return _get_program(["gcovr"])
|
||||
142
UNITTESTS/unit_test/new.py
Normal file
142
UNITTESTS/unit_test/new.py
Normal file
@@ -0,0 +1,142 @@
|
||||
"""
|
||||
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.
|
||||
|
||||
|
||||
UNIT TEST GENERATE NEW UNIT TESTS
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
import os
|
||||
import re
|
||||
import logging
|
||||
|
||||
class UnitTestGeneratorTool(object):
|
||||
"""
|
||||
Generator tool to create new unit tests from template
|
||||
"""
|
||||
|
||||
def _replace_content(self, template_content, dirname, classname, extension):
|
||||
if extension == "h":
|
||||
content = re.sub(r"cppfile",
|
||||
"",
|
||||
template_content)
|
||||
elif extension == "cpp":
|
||||
content = re.sub(r"cppfile",
|
||||
"../dirname/template.cpp",
|
||||
template_content)
|
||||
|
||||
content = re.sub(r"headerfile", "../dirname/template.h", content)
|
||||
content = re.sub(r"dirname", dirname, content)
|
||||
content = re.sub(r"template", classname, content)
|
||||
|
||||
return content
|
||||
|
||||
def create(self, mbed_os_root=None, filepath=None):
|
||||
"""
|
||||
Generate a unit test for a file to be tested from templates
|
||||
|
||||
Keyword arguments:
|
||||
mbed_os_root - Mbed OS root directory
|
||||
filepath - Path to the file to be tested
|
||||
"""
|
||||
|
||||
if filepath is None:
|
||||
logging.error("No filepath given. Cannot create a new unit test.")
|
||||
return
|
||||
|
||||
if mbed_os_root is None:
|
||||
mbed_os_root = \
|
||||
os.path.normpath(os.path.join(os.path.dirname(__file__),
|
||||
"../.."))
|
||||
if os.path.isabs(filepath):
|
||||
filepath = os.path.join(
|
||||
os.path.relpath(os.path.dirname(filepath), mbed_os_root),
|
||||
os.path.basename(filepath))
|
||||
|
||||
if not re.match(r"((\w+\/)+)(\w+)\.(c|cpp|h|hpp)$", filepath):
|
||||
logging.error(
|
||||
"No proper path to source file given.")
|
||||
return
|
||||
|
||||
if not os.path.isfile(os.path.join(mbed_os_root, filepath)):
|
||||
logging.warning("Cannot find source file: %s from Mbed OS.",
|
||||
filepath)
|
||||
|
||||
dir_name = re.sub(r"(\\|\/)(\w+)\.(cpp|h)$", "", filepath)
|
||||
|
||||
match = re.search(r"(\\|\/)(\w+)\.(cpp|h)$", filepath)
|
||||
|
||||
if match:
|
||||
class_name = match.group(2)
|
||||
file_extension = match.group(3)
|
||||
|
||||
# Create directory tree if not exist.
|
||||
test_dir = os.path.join(mbed_os_root, "UNITTESTS", dir_name, class_name)
|
||||
|
||||
if not os.path.exists(test_dir):
|
||||
os.makedirs(test_dir)
|
||||
else:
|
||||
logging.error("Unit tests exist already for this file.")
|
||||
return
|
||||
|
||||
try:
|
||||
suite_name = ("%s/%s" % (dir_name, class_name)).replace("/", "_")
|
||||
|
||||
# Test source file template
|
||||
template_source_file = \
|
||||
os.path.join(mbed_os_root,
|
||||
"UNITTESTS/template/test_template.cpp")
|
||||
|
||||
with open(template_source_file, "r") as input_file:
|
||||
template_content = input_file.read()
|
||||
|
||||
output_source_file = os.path.join(test_dir,
|
||||
"test_%s.cpp" % class_name)
|
||||
|
||||
with open(output_source_file, "w") as output_file:
|
||||
content = self._replace_content(template_content,
|
||||
dir_name,
|
||||
class_name,
|
||||
file_extension)
|
||||
|
||||
output_file.writelines(content)
|
||||
|
||||
# Definition file template
|
||||
template_source_file = \
|
||||
os.path.join(mbed_os_root,
|
||||
"UNITTESTS/template/unittest.cmake.template")
|
||||
|
||||
with open(template_source_file, "r") as input_file:
|
||||
template_content = input_file.read()
|
||||
|
||||
output_source_file = os.path.join(test_dir, "unittest.cmake")
|
||||
|
||||
with open(output_source_file, "w") as output_file:
|
||||
content = self._replace_content(template_content,
|
||||
dir_name,
|
||||
class_name,
|
||||
file_extension)
|
||||
|
||||
output_file.writelines(content)
|
||||
|
||||
logging.info("""
|
||||
Unit test %s created.
|
||||
|
||||
Generated files into %s directory.
|
||||
""", suite_name, test_dir)
|
||||
|
||||
except IOError:
|
||||
logging.error("Cannot create files for a unit test.")
|
||||
153
UNITTESTS/unit_test/options.py
Normal file
153
UNITTESTS/unit_test/options.py
Normal file
@@ -0,0 +1,153 @@
|
||||
"""
|
||||
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.
|
||||
|
||||
|
||||
UNIT TEST OPTIONS
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
|
||||
from .settings import CMAKE_GENERATORS, MAKE_PROGRAMS, COVERAGE_ARGS
|
||||
from .get_tools import get_make_tool
|
||||
|
||||
def get_options_parser():
|
||||
"""
|
||||
Create a unit test options parser.
|
||||
|
||||
Returns a parser object
|
||||
"""
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
log_group = parser.add_mutually_exclusive_group()
|
||||
|
||||
log_group.add_argument("-v",
|
||||
"--verbose",
|
||||
help="Verbose output",
|
||||
action="store_const",
|
||||
const=logging.getLevelName(logging.DEBUG),
|
||||
dest="log_level")
|
||||
|
||||
log_group.add_argument("--quiet",
|
||||
help="Quiet output",
|
||||
action="store_const",
|
||||
const=logging.getLevelName(logging.ERROR),
|
||||
dest="log_level")
|
||||
|
||||
parser.add_argument("--compile",
|
||||
action="store_true",
|
||||
help="Only compile unit tests",
|
||||
dest="compile_only")
|
||||
|
||||
parser.add_argument("--run",
|
||||
action="store_true",
|
||||
help="Only run unit tests",
|
||||
dest="run_only")
|
||||
|
||||
parser.add_argument("-c",
|
||||
"--clean",
|
||||
action="store_true",
|
||||
help="Clean the build directory",
|
||||
dest="clean")
|
||||
|
||||
parser.add_argument("-d",
|
||||
"--debug",
|
||||
action="store_true",
|
||||
help="Enable debug build",
|
||||
dest="debug_build")
|
||||
|
||||
parser.add_argument("--coverage",
|
||||
choices=COVERAGE_ARGS,
|
||||
help="Generate code coverage report",
|
||||
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",
|
||||
"--make-program",
|
||||
default=get_make_tool(),
|
||||
choices=MAKE_PROGRAMS,
|
||||
help="Which make program to use",
|
||||
dest="make_program")
|
||||
|
||||
parser.add_argument("-g",
|
||||
"--generator",
|
||||
choices=CMAKE_GENERATORS,
|
||||
help="Which CMake generator to use",
|
||||
dest="cmake_generator")
|
||||
|
||||
parser.add_argument("-r",
|
||||
"--regex",
|
||||
help="Run tests matching a regular expression",
|
||||
dest="test_regex")
|
||||
|
||||
parser.add_argument("--build",
|
||||
default="build",
|
||||
help="Build directory. Default: UNITTESTS/build/",
|
||||
dest="build")
|
||||
|
||||
parser.add_argument("--valgrind",
|
||||
help="Use Valgrind when running executables",
|
||||
action="store_true",
|
||||
dest="valgrind")
|
||||
|
||||
parser.add_argument("--new",
|
||||
action="append",
|
||||
help="Source file from which to generate test files. E.g. rtos/Semaphore.cpp",
|
||||
metavar="FILEPATH",
|
||||
dest="new_files")
|
||||
|
||||
return parser
|
||||
|
||||
def pretty_print_test_options(options=None):
|
||||
"""
|
||||
Pretty print test options
|
||||
|
||||
Keyword arguments:
|
||||
options - options
|
||||
"""
|
||||
|
||||
if options is None:
|
||||
return
|
||||
|
||||
# Mode
|
||||
logging.info("""
|
||||
#####################
|
||||
Mbed OS unit testing:
|
||||
#####################
|
||||
""")
|
||||
|
||||
logging.info("Steps:\n")
|
||||
|
||||
logging.info(" [%s] \tPrepare build directory", "AUTO")
|
||||
logging.info(" \t\t - Clean: %s", options.clean)
|
||||
logging.info(" \t\t - Directory: %s\n", options.build)
|
||||
status = "SET" if options.compile_only else "UNSET"
|
||||
logging.info(" [%s] \tBuild unit tests", status)
|
||||
logging.info(" \t\t - Debug: %s", options.debug_build)
|
||||
logging.info(" \t\t - CMake generator: %s", options.cmake_generator)
|
||||
logging.info(" \t\t - Make program: %s\n", options.make_program)
|
||||
status = "SET" if options.run_only else "UNSET"
|
||||
logging.info(" [%s] \tRun unit tests", status)
|
||||
logging.info(" \t\t - Filter: %s\n", options.test_regex)
|
||||
if options.coverage:
|
||||
logging.info(" [%s] \tGenerate coverage reports", "SET")
|
||||
logging.info(" \t\t - Output: %s", options.coverage)
|
||||
logging.info(" \t\t - Include headers: %s", options.include_headers)
|
||||
42
UNITTESTS/unit_test/settings.py
Normal file
42
UNITTESTS/unit_test/settings.py
Normal file
@@ -0,0 +1,42 @@
|
||||
"""
|
||||
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.
|
||||
|
||||
|
||||
UNIT TEST SETTINGS
|
||||
"""
|
||||
|
||||
CMAKE_GENERATORS = ["Unix Makefiles", "MinGW Makefiles", "Ninja"]
|
||||
|
||||
MAKE_PROGRAMS = ["gmake", "make", "mingw32-make", "ninja"]
|
||||
|
||||
DEFAULT_CMAKE_GENERATORS = {
|
||||
"gmake": "Unix Makefiles",
|
||||
"make": "Unix Makefiles",
|
||||
"mingw32-make": "MinGW Makefiles",
|
||||
"ninja": "Ninja"
|
||||
}
|
||||
|
||||
COVERAGE_ARGS = ["html",
|
||||
"xml",
|
||||
"both"]
|
||||
|
||||
COVERAGE_OUTPUT_TYPES = ["html", "xml"]
|
||||
|
||||
CXX_COMPILERS = ["g++-6", "g++-8", "g++-7", "g++-5", "g++-4.9", "g++"]
|
||||
|
||||
C_COMPILERS = ["gcc-6", "gcc-8", "gcc-7", "gcc-5", "gcc-4.9", "gcc"]
|
||||
|
||||
GCOV_PROGRAMS = ["gcov-6", "gcov-8", "gcov-7", "gcov-5", "gcov-4.9", "gcov"]
|
||||
220
UNITTESTS/unit_test/test.py
Normal file
220
UNITTESTS/unit_test/test.py
Normal file
@@ -0,0 +1,220 @@
|
||||
"""
|
||||
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.
|
||||
|
||||
|
||||
UNIT TEST BUILD & RUN
|
||||
"""
|
||||
|
||||
import os
|
||||
import logging
|
||||
import sys
|
||||
import psutil
|
||||
from shutil import copy
|
||||
|
||||
from .utils import execute_program
|
||||
from .get_tools import get_make_tool, \
|
||||
get_cmake_tool, \
|
||||
get_cxx_tool, \
|
||||
get_c_tool, \
|
||||
get_valgrind_tool
|
||||
from .settings import DEFAULT_CMAKE_GENERATORS
|
||||
|
||||
class UnitTestTool(object):
|
||||
"""
|
||||
Unit test tool to:
|
||||
- prepare build directory
|
||||
- create makefiles
|
||||
- build unit tests
|
||||
- run unit tests
|
||||
- generate code coverage reports
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
make_program=None):
|
||||
"""
|
||||
Constructor
|
||||
|
||||
Keyword arguments:
|
||||
make_program - Make tool to use
|
||||
"""
|
||||
|
||||
self.make_program = make_program
|
||||
|
||||
if self.make_program is None:
|
||||
self.make_program = get_make_tool()
|
||||
|
||||
def create_makefiles(self,
|
||||
path_to_src=None,
|
||||
generator=None,
|
||||
coverage_output_type=None,
|
||||
debug=False,
|
||||
valgrind=False):
|
||||
"""
|
||||
Create Makefiles and prepare targets with CMake.
|
||||
|
||||
Keyword arguments:
|
||||
path_to_src - Path to source directory
|
||||
generator - Type of Makefiles to generate
|
||||
coverage_output_type - Generate HTML, XML or both reports
|
||||
debug - Target debug or release build
|
||||
"""
|
||||
|
||||
if generator is None:
|
||||
generator = DEFAULT_CMAKE_GENERATORS.get(self.make_program,
|
||||
"Unix Makefiles")
|
||||
|
||||
cmake = get_cmake_tool()
|
||||
|
||||
if cmake is None:
|
||||
logging.error(
|
||||
"No CMake found in Path. Install all the required tools.")
|
||||
sys.exit(1)
|
||||
|
||||
args = [cmake,
|
||||
"-G",
|
||||
generator,
|
||||
"-DCMAKE_MAKE_PROGRAM=%s" % self.make_program,
|
||||
"-DCMAKE_CXX_COMPILER=%s" % get_cxx_tool(),
|
||||
"-DCMAKE_C_COMPILER=%s" % get_c_tool()]
|
||||
|
||||
if debug:
|
||||
args.append("-DCMAKE_BUILD_TYPE=Debug")
|
||||
|
||||
if coverage_output_type:
|
||||
args.append("-DCOVERAGE:STRING=%s" % coverage_output_type)
|
||||
|
||||
if valgrind:
|
||||
valgrind = get_valgrind_tool()
|
||||
if valgrind is None:
|
||||
logging.error(
|
||||
"No Valgrind found in Path. Install all the required tools.\n")
|
||||
sys.exit(1)
|
||||
args.append("-DVALGRIND=1")
|
||||
args.append("-DMEMORYCHECK_COMMAND_OPTIONS=\"--track-origins=yes\" \"--leak-check=full\" \"--show-reachable=yes\" \"--error-exitcode=1\"")
|
||||
else:
|
||||
args.append("-DVALGRIND=0")
|
||||
|
||||
if path_to_src is not None:
|
||||
args.append(path_to_src)
|
||||
|
||||
execute_program(args,
|
||||
"CMake failed to run successfully. See error message.")
|
||||
|
||||
def build_tests(self):
|
||||
"""
|
||||
Build unit tests and libraries to be tested.
|
||||
"""
|
||||
|
||||
args = [self.make_program]
|
||||
|
||||
# Speed up compilation by running on more than one core
|
||||
count = psutil.cpu_count()
|
||||
args.append("-j{}".format(count+1))
|
||||
|
||||
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
|
||||
args.append("VERBOSE=1")
|
||||
|
||||
execute_program(args,
|
||||
"Building unit tests failed.",
|
||||
"Unit tests built successfully.")
|
||||
|
||||
def run_tests(self, filter_regex=None, valgrind=False):
|
||||
"""
|
||||
Run unit tests.
|
||||
|
||||
Keyword arguments:
|
||||
filter_regex - Regular expression to select which tests to run
|
||||
"""
|
||||
|
||||
args = [self.make_program, "test"]
|
||||
if valgrind:
|
||||
if filter_regex:
|
||||
args.append("ARGS=-R %s -V -D ExperimentalMemCheck" % filter_regex)
|
||||
else:
|
||||
args.append("ARGS=-V -D ExperimentalMemCheck")
|
||||
else:
|
||||
if filter_regex:
|
||||
args.append("ARGS=-R %s -V -D ExperimentalTest" % filter_regex)
|
||||
else:
|
||||
args.append("ARGS=-V -D ExperimentalTest")
|
||||
|
||||
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
|
||||
args.append("VERBOSE=1")
|
||||
|
||||
execute_program(args, "Unit test run failed.")
|
||||
|
||||
def prepare_build_directory(self,
|
||||
path_to_src=None,
|
||||
build_path=None,
|
||||
clean=False):
|
||||
"""
|
||||
Create build directory if not exist and
|
||||
change current working directory to it
|
||||
|
||||
Keyword arguments:
|
||||
path_to_src - Path to source directory
|
||||
build_path - Path to build directory
|
||||
clean - Clean build directory
|
||||
"""
|
||||
|
||||
if build_path is None:
|
||||
build_path = os.getcwd()
|
||||
|
||||
# Clean CMake data if exists.
|
||||
if clean:
|
||||
self._clean_build(build_path)
|
||||
|
||||
# Create build directory if not exist.
|
||||
if not os.path.exists(build_path):
|
||||
os.makedirs(build_path)
|
||||
filename = ".mbedignore"
|
||||
inputfile = os.path.join(path_to_src, filename)
|
||||
outputfile = os.path.join(build_path, filename)
|
||||
copy(inputfile, outputfile)
|
||||
|
||||
# Change current working directory to build directory.
|
||||
os.chdir(build_path)
|
||||
|
||||
def _clean_build(self, build_path=None):
|
||||
"""
|
||||
Try clean build directory
|
||||
|
||||
Keyword arguments:
|
||||
build_path - Path to build directory
|
||||
"""
|
||||
|
||||
logging.info("Cleaning build directory...")
|
||||
|
||||
if os.path.exists(os.path.join(build_path, "CMakeCache.txt")):
|
||||
args = [self.make_program,
|
||||
"-C",
|
||||
build_path,
|
||||
"--no-print-directory",
|
||||
"clean"]
|
||||
|
||||
# Remove coverage files
|
||||
for root, _, files in os.walk(build_path):
|
||||
for current_file in files:
|
||||
if current_file.endswith((".gcno", ".gcda")):
|
||||
os.remove(os.path.join(root, current_file))
|
||||
|
||||
execute_program(args,
|
||||
"Clean step failed.",
|
||||
"Clean done.")
|
||||
|
||||
else:
|
||||
logging.warning("%s does not exist or \
|
||||
does not contain previous build data.", build_path)
|
||||
71
UNITTESTS/unit_test/utils.py
Normal file
71
UNITTESTS/unit_test/utils.py
Normal file
@@ -0,0 +1,71 @@
|
||||
"""
|
||||
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.
|
||||
|
||||
|
||||
UNIT TEST UTILITIES
|
||||
"""
|
||||
|
||||
import platform
|
||||
import subprocess
|
||||
import logging
|
||||
import sys
|
||||
|
||||
def is_tool(name):
|
||||
"""
|
||||
Test if tool is found in PATH
|
||||
|
||||
@param name: executable name
|
||||
@return: true if tool found, false otherwise
|
||||
"""
|
||||
cmd = "where" if platform.system() == "Windows" else "which"
|
||||
try:
|
||||
subprocess.check_output([cmd, name], stderr=subprocess.STDOUT)
|
||||
return True
|
||||
except subprocess.CalledProcessError:
|
||||
return False
|
||||
|
||||
def execute_program(args, error_msg="An error occurred!", success_msg=None):
|
||||
"""
|
||||
Execute program in a subprocess with given arguments
|
||||
|
||||
@param args: program and its arguments in a list
|
||||
@param success_msg: message to show in case of success
|
||||
@param error_msg: message to show in case of failure
|
||||
"""
|
||||
|
||||
try:
|
||||
process = subprocess.Popen(args,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT)
|
||||
|
||||
# Output is stripped to remove newline character. logging adds its own
|
||||
# so we avoid double newlines.
|
||||
# Because the process can terminate before the loop has read all lines,
|
||||
# we read the output remnant just in case. Otherwise we lose it.
|
||||
while process.poll() is None:
|
||||
logging.info(process.stdout.readline().decode('utf8').rstrip('\n'))
|
||||
logging.info(process.stdout.read().decode('utf8').rstrip('\n'))
|
||||
|
||||
retcode = process.wait()
|
||||
|
||||
if retcode:
|
||||
raise subprocess.CalledProcessError(retcode, args)
|
||||
elif success_msg:
|
||||
logging.info(success_msg)
|
||||
|
||||
except subprocess.CalledProcessError as error:
|
||||
logging.error(error_msg)
|
||||
sys.exit(error.returncode)
|
||||
Reference in New Issue
Block a user