Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/firecracker
Path: blob/main/tests/integration_tests/build/test_coverage.py
1958 views
1
# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
# SPDX-License-Identifier: Apache-2.0
3
"""Tests pertaining to line/branch test coverage for the Firecracker code base.
4
5
# TODO
6
7
- Put the coverage in `s3://spec.firecracker` and update it automatically.
8
target should be put in `s3://spec.firecracker` and automatically updated.
9
"""
10
11
12
import os
13
import platform
14
import re
15
import shutil
16
import pytest
17
18
import framework.utils as utils
19
import host_tools.cargo_build as host # pylint: disable=import-error
20
import host_tools.proc as proc
21
22
# AMD has a slightly different coverage due to
23
# the appearance of the brand string. On Intel,
24
# this contains the frequency while on AMD it does not.
25
# Checkout the cpuid crate. In the future other
26
# differences may appear.
27
COVERAGE_DICT = {"Intel": 84.84, "AMD": 84.22, "ARM": 83.05}
28
29
PROC_MODEL = proc.proc_type()
30
31
COVERAGE_MAX_DELTA = 0.05
32
33
CARGO_KCOV_REL_PATH = os.path.join(host.CARGO_BUILD_REL_PATH, 'kcov')
34
35
KCOV_COVERAGE_FILE = 'index.js'
36
"""kcov will aggregate coverage data in this file."""
37
38
KCOV_COVERED_LINES_REGEX = r'"covered_lines":"(\d+)"'
39
"""Regex for extracting number of total covered lines found by kcov."""
40
41
KCOV_TOTAL_LINES_REGEX = r'"total_lines" : "(\d+)"'
42
"""Regex for extracting number of total executable lines found by kcov."""
43
44
SECCOMPILER_BUILD_DIR = '../build/seccompiler'
45
46
47
@pytest.mark.timeout(400)
48
def test_coverage(test_fc_session_root_path, test_session_tmp_path):
49
"""Test line coverage with kcov.
50
51
The result is extracted from the $KCOV_COVERAGE_FILE file created by kcov
52
after a coverage run.
53
"""
54
proc_model = [item for item in COVERAGE_DICT if item in PROC_MODEL]
55
assert len(proc_model) == 1, "Could not get processor model!"
56
coverage_target_pct = COVERAGE_DICT[proc_model[0]]
57
exclude_pattern = (
58
'${CARGO_HOME:-$HOME/.cargo/},'
59
'build/,'
60
'tests/,'
61
'usr/lib/gcc,'
62
'lib/x86_64-linux-gnu/,'
63
'test_utils.rs,'
64
# The following files/directories are auto-generated
65
'bootparam.rs,'
66
'elf.rs,'
67
'mpspec.rs,'
68
'msr_index.rs,'
69
'_gen'
70
)
71
exclude_region = '\'mod tests {\''
72
target = "{}-unknown-linux-musl".format(platform.machine())
73
74
cmd = (
75
'RUSTFLAGS="{}" CARGO_TARGET_DIR={} cargo kcov --all '
76
'--target {} --output {} -- '
77
'--exclude-pattern={} '
78
'--exclude-region={} --verify'
79
).format(
80
host.get_rustflags(),
81
os.path.join(test_fc_session_root_path, CARGO_KCOV_REL_PATH),
82
target,
83
test_session_tmp_path,
84
exclude_pattern,
85
exclude_region
86
)
87
# We remove the seccompiler custom build directory, created by the
88
# vmm-level `build.rs`.
89
# If we don't delete it before and after running the kcov command, we will
90
# run into linker errors.
91
shutil.rmtree(SECCOMPILER_BUILD_DIR, ignore_errors=True)
92
# By default, `cargo kcov` passes `--exclude-pattern=$CARGO_HOME --verify`
93
# to kcov. To pass others arguments, we need to include the defaults.
94
utils.run_cmd(cmd)
95
96
shutil.rmtree(SECCOMPILER_BUILD_DIR)
97
98
coverage_file = os.path.join(test_session_tmp_path, KCOV_COVERAGE_FILE)
99
with open(coverage_file) as cov_output:
100
contents = cov_output.read()
101
covered_lines = int(re.findall(KCOV_COVERED_LINES_REGEX, contents)[0])
102
total_lines = int(re.findall(KCOV_TOTAL_LINES_REGEX, contents)[0])
103
coverage = covered_lines / total_lines * 100
104
print("Number of executable lines: {}".format(total_lines))
105
print("Number of covered lines: {}".format(covered_lines))
106
print("Thus, coverage is: {:.2f}%".format(coverage))
107
108
coverage_low_msg = (
109
'Current code coverage ({:.2f}%) is below the target ({}%).'
110
.format(coverage, coverage_target_pct)
111
)
112
113
min_coverage = coverage_target_pct - COVERAGE_MAX_DELTA
114
assert coverage >= min_coverage, coverage_low_msg
115
116
# Get the name of the variable that needs updating.
117
namespace = globals()
118
cov_target_name = [name for name in namespace if namespace[name]
119
is COVERAGE_DICT][0]
120
121
coverage_high_msg = (
122
'Current code coverage ({:.2f}%) is above the target ({}%).\n'
123
'Please update the value of {}.'
124
.format(coverage, coverage_target_pct, cov_target_name)
125
)
126
127
assert coverage - coverage_target_pct <= COVERAGE_MAX_DELTA,\
128
coverage_high_msg
129
130