Files
rust-vmm-ci/integration_tests/test_coverage.py
mrxinwang cd7096e7a6 Enable rust-vmm coverage test in CI
1. Enabled the aarch64 coverage test in `test_coverage` script.

2. Seperated the coverage json sample file to two sample files for
different machine achitectures.

3. Updated Buildkite pipeline container version.

4. Updated README for the description of coverage files and container
version.

Signed-off-by: Henry Wang <henry.wang@arm.com>
2020-02-21 09:56:34 +01:00

123 lines
4.5 KiB
Python

# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
"""Test the coverage and update the threshold when coverage is increased."""
import json, os, re, shutil, subprocess, platform
import pytest
from utils import get_repo_root_path
REPO_ROOT_PATH = get_repo_root_path()
if platform.machine() == "x86_64":
COVERAGE_CONFIG_PATH = os.path.join(REPO_ROOT_PATH, "coverage_config_x86_64.json")
elif platform.machine() == "aarch64":
COVERAGE_CONFIG_PATH = os.path.join(REPO_ROOT_PATH, "coverage_config_aarch64.json")
def _read_test_config():
"""
Reads the config of the coverage for the repository being tested.
Returns a JSON object with the configuration.
"""
coverage_config = {}
with open(COVERAGE_CONFIG_PATH) as config_file:
coverage_config = json.load(config_file)
assert "coverage_score" in coverage_config
assert "exclude_path" in coverage_config
assert "crate_features" in coverage_config
return coverage_config
def _write_coverage_config(coverage_config):
"""Updates the coverage config file as per `coverage_config`"""
with open(COVERAGE_CONFIG_PATH, 'w') as outfile:
json.dump(coverage_config, outfile)
def _get_current_coverage(coverage_config):
"""Helper function that returns the coverage computed with kcov."""
kcov_ouput_dir = os.path.join(REPO_ROOT_PATH, "kcov_output")
# By default the build output for kcov and unit tests are both in the debug
# directory. This causes some linker errors that I haven't investigated.
# Error: error: linking with `cc` failed: exit code: 1
# An easy fix is to have separate build directories for kcov & unit tests.
kcov_build_dir = os.path.join(REPO_ROOT_PATH, "kcov_build")
# Remove kcov output and build directory to be sure we are always working
# on a clean environment.
shutil.rmtree(kcov_ouput_dir, ignore_errors=True)
shutil.rmtree(kcov_build_dir, ignore_errors=True)
exclude_pattern = (
'${CARGO_HOME:-$HOME/.cargo/},'
'usr/lib/,'
'lib/'
)
exclude_region = "'mod tests {'"
additional_exclude_path = coverage_config["exclude_path"]
if additional_exclude_path:
exclude_pattern += ',' + additional_exclude_path
additional_kcov_param = ''
crate_features = coverage_config["crate_features"]
if crate_features:
additional_kcov_param += '--features=' + crate_features
kcov_cmd = "CARGO_TARGET_DIR={} cargo kcov {} --all " \
"--output {} -- " \
"--exclude-region={} " \
"--exclude-pattern={} " \
"--verify".format(
kcov_build_dir,
additional_kcov_param,
kcov_ouput_dir,
exclude_region,
exclude_pattern
)
subprocess.run(kcov_cmd, shell=True, check=True)
# Read the coverage reported by kcov.
coverage_file = os.path.join(kcov_ouput_dir, 'index.js')
with open(coverage_file) as cov_output:
coverage = float(re.findall(
r'"covered":"(\d+\.\d)"',
cov_output.read()
)[0])
# Remove coverage related directories.
shutil.rmtree(kcov_ouput_dir, ignore_errors=True)
shutil.rmtree(kcov_build_dir, ignore_errors=True)
return coverage
def test_coverage(profile):
coverage_config = _read_test_config()
current_coverage = _get_current_coverage(coverage_config)
previous_coverage = coverage_config["coverage_score"]
if previous_coverage < current_coverage:
if profile == pytest.profile_ci:
# In the CI Profile we expect the coverage to be manually updated.
assert False, "Coverage is increased from {} to {}. " \
"Please update the coverage in " \
"tests/coverage.".format(
previous_coverage,
current_coverage
)
elif profile == pytest.profile_devel:
coverage_config["coverage_score"] = current_coverage
_write_coverage_config(coverage_config)
else:
# This should never happen because pytest should only accept
# the valid test profiles specified with `choices` in
# `pytest_addoption`.
assert False, "Invalid test profile."
elif previous_coverage > current_coverage:
diff = float(previous_coverage - current_coverage)
assert False, "Coverage drops by {:.2f}%. Please add unit tests for" \
"the uncovered lines.".format(diff)