import argparse
import os
import shutil
import sys
here = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
if here not in sys.path:
sys.path.insert(0, here)
import tempfile
import coverage
import pytest
from buildtest.defaults import (
BUILDTEST_ROOT,
BUILDTEST_UNITTEST_ROOT,
BUILDTEST_USER_HOME,
DEFAULT_SETTINGS_FILE,
VAR_DIR,
console,
)
from buildtest.utils.file import is_dir, resolve_path
[docs]
def run_unit_tests(pytestopts=None, sourcefiles=None, enable_coverage=False):
"""Entry point for running buildtest unit tests. This method can be invoked via ``buildtest unittests`` or run
via command line as standalone program. The unit tests are run via `pytest <https://docs.pytest.org/>`_ and `coverage <https://coverage.readthedocs.io/en/6.2/>`_
for measuring coverage report. This method will report coverage results that can be viewable in html or json.
Args:
pytestopts (str): Specify options to pytest command.
sourcefiles (list): List of source files to run with pytest
enable_coverage (bool): Enable coverage when running regression test
"""
if not os.getenv("BUILDTEST_ROOT"):
sys.exit(
"Please check your buildtest installation by running 'source setup.sh'"
)
os.environ["BUILDTEST_CI_DIR"] = tempfile.mkdtemp()
settings_file = os.path.join(os.environ["BUILDTEST_CI_DIR"], "config.yml")
shutil.copyfile(DEFAULT_SETTINGS_FILE, settings_file)
console.rule("Running buildtest unit tests")
console.print("BUILDTEST_CI_DIR", os.getenv("BUILDTEST_CI_DIR"))
pytestopts = pytestopts.split() if pytestopts else []
sources = []
# if --sourcefiles specified we resolve path to each argument otherwise default to BUILDTEST_UNITTEST_ROOT which is root of test directory
sourcefiles = sourcefiles or [BUILDTEST_UNITTEST_ROOT]
for fpath in sourcefiles:
sources.append(resolve_path(fpath))
# need to remove any None types from list since resolve_path method can return None if path is invalid
sources = list(filter(None, sources))
pytest_cmd = pytestopts + sources
html_dir = os.path.join(BUILDTEST_ROOT, "htmlcov")
if is_dir(BUILDTEST_USER_HOME):
shutil.rmtree(BUILDTEST_USER_HOME)
if is_dir(VAR_DIR):
shutil.rmtree(VAR_DIR)
cov = coverage.Coverage(branch=True)
# run regression test with coverage if --coverage is specified
if enable_coverage:
cov.erase()
cov.start()
# run regression test
retcode = pytest.main(pytest_cmd)
# if there is a failure in pytest raise exit 1
if retcode == pytest.ExitCode.TESTS_FAILED:
sys.exit(1)
if enable_coverage:
cov.stop()
cov.combine()
cov.html_report(title="buildtest unittests coverage report", directory=html_dir)
cov.json_report(outfile=os.path.join(BUILDTEST_ROOT, "coverage.json"))
cov.report(ignore_errors=True, skip_empty=True, sort="-cover", precision=2)
print("\n\n")
console.print("Writing coverage results to: ", html_dir)
coverage_file = os.path.join(html_dir, "index.html")
assert os.path.exists(coverage_file)
console.print("You can view coverage report by viewing file: ", coverage_file)
if __name__ == "__main__":
parser = argparse.ArgumentParser(
prog="unittest", description="Run buildtest unit tests"
)
parser.add_argument(
"-c",
"--coverage",
action="store_true",
help="Enable coverage when running regression test",
)
parser.add_argument("-p", "--pytestopts", type=str, help="Specify option to pytest")
parser.add_argument(
"-s",
"--sourcefiles",
type=str,
help="Specify path to file or directory when running regression test",
action="append",
)
args = parser.parse_args()
run_unit_tests(
pytestopts=args.pytestopts,
sourcefiles=args.sourcefiles,
enable_coverage=args.coverage,
)