Skip to content

Commit 73faa50

Browse files
committed
create a script for automating code coverage
1 parent 7eb267d commit 73faa50

File tree

3 files changed

+123
-0
lines changed

3 files changed

+123
-0
lines changed

.config/nextest.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,10 @@ status-level = "all"
44
final-status-level = "skip"
55
failure-output = "immediate-final"
66
fail-fast = false
7+
8+
[profile.coverage]
9+
retries = 0
10+
status-level = "all"
11+
final-status-level = "skip"
12+
failure-output = "immediate-final"
13+
fail-fast = false

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# spell-checker:ignore (misc) direnv
22

33
target/
4+
coverage/
45
/src/*/gen_table
56
/build/
67
/tmp/
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#!/usr/bin/env bash
2+
3+
# spell-checker:ignore (env/flags) Ccodegen Cinstrument Coverflow Cpanic Zpanic
4+
# spell-checker:ignore PROFDATA PROFRAW coreutil librairies nextest profdata profraw rustlib
5+
6+
# This script will build, run and generate coverage reports for the whole
7+
# testsuite.
8+
# The biggest challenge of this process is managing the overwhelming generation
9+
# of trace files that are generated after EACH SINGLE invocation of a coreutil
10+
# in the testsuite. Moreover, because we run the testsuite against the multicall
11+
# binary, each trace file contains coverage information about the WHOLE
12+
# multicall binary, dependencies included, which results in a 5-6 MB file.
13+
# Running the testsuite easily creates +80 GB of trace files, which is
14+
# unmanageable in a CI environment.
15+
#
16+
# A workaround is to run the testsuite util per util, generate a report per
17+
# util, and remove the trace files. Therefore, we end up with several reports
18+
# that will get uploaded to codecov afterwards. The issue with this
19+
# approach is that the `grcov` call, which is responsible for transforming
20+
# `.profraw` trace files into a `lcov` file, takes a lot of time (~20s), mainly
21+
# because it has to browse all the sources. So calling it for each of the 100
22+
# utils (with --all-features) results in an absurdly long execution time
23+
# (almost an hour).
24+
25+
# TODO: Do not instrument 3rd party librairies to save space and performance
26+
27+
# Exit the script if an unexpected error arise
28+
set -e
29+
# Treat unset variables as errors
30+
set -u
31+
# Print expanded commands to stdout before running them
32+
set -x
33+
34+
ME="${0}"
35+
ME_dir="$(dirname -- "$(readlink -fm -- "${ME}")")"
36+
REPO_main_dir="$(dirname -- "${ME_dir}")"
37+
38+
# Features to enable for the `coreutils` package
39+
FEATURES_OPTION=${FEATURES_OPTION:-"--features=feat_os_unix"}
40+
COVERAGE_DIR=${COVERAGE_DIR:-"${REPO_main_dir}/coverage"}
41+
42+
LLVM_PROFDATA="$(find "$(rustc --print sysroot)" -name llvm-profdata)"
43+
44+
PROFRAW_DIR="${COVERAGE_DIR}/traces"
45+
PROFDATA_DIR="${COVERAGE_DIR}/data"
46+
REPORT_DIR="${COVERAGE_DIR}/report"
47+
REPORT_PATH="${REPORT_DIR}/total.lcov.info"
48+
49+
rm -rf "${PROFRAW_DIR}" && mkdir -p "${PROFRAW_DIR}"
50+
rm -rf "${PROFDATA_DIR}" && mkdir -p "${PROFDATA_DIR}"
51+
rm -rf "${REPORT_DIR}" && mkdir -p "${REPORT_DIR}"
52+
53+
#shellcheck disable=SC2086
54+
UTIL_LIST=$("${ME_dir}"/show-utils.sh ${FEATURES_OPTION})
55+
56+
export CARGO_INCREMENTAL=0
57+
export RUSTFLAGS="-Cinstrument-coverage -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort"
58+
export RUSTDOCFLAGS="-Cpanic=abort"
59+
export RUSTUP_TOOLCHAIN="nightly-gnu"
60+
export LLVM_PROFILE_FILE="${PROFRAW_DIR}/coverage-%m-%p.profraw"
61+
62+
# Disable expanded command printing for the rest of the program
63+
set +x
64+
65+
run_test_and_aggregate() {
66+
echo "# Running coverage tests for ${1}"
67+
68+
# Build and run tests for the UTIL
69+
cargo nextest run \
70+
--profile coverage \
71+
--no-fail-fast \
72+
--color=always \
73+
2>&1 \
74+
${2} \
75+
| grep -v 'SKIP'
76+
# Note: Do not print the skipped tests on the output as there will be many.
77+
78+
echo "## Tests for (${1}) generated $(du -h -d1 ${PROFRAW_DIR} | cut -f 1) of profraw files"
79+
80+
# Aggregate all the trace files into a profdata file
81+
PROFDATA_FILE="${PROFDATA_DIR}/${1}.profdata"
82+
echo "## Aggregating coverage files under ${PROFDATA_FILE}"
83+
"${LLVM_PROFDATA}" merge \
84+
-sparse \
85+
-o ${PROFDATA_FILE} \
86+
${PROFRAW_DIR}/*.profraw \
87+
|| true
88+
# We don't want an error in `llvm-profdata` to abort the whole program
89+
}
90+
91+
for UTIL in ${UTIL_LIST}; do
92+
93+
run_test_and_aggregate \
94+
"${UTIL}" \
95+
"-p coreutils -E test(/^test_${UTIL}::/) ${FEATURES_OPTION}"
96+
97+
echo "## Clear the trace directory to free up space"
98+
rm -rf "${PROFRAW_DIR}" && mkdir -p "${PROFRAW_DIR}"
99+
done;
100+
101+
echo "Running coverage tests over uucore"
102+
run_test_and_aggregate "uucore" "-p uucore --all-features"
103+
104+
echo "# Aggregating all the profraw files under ${REPORT_PATH}"
105+
grcov \
106+
"${PROFDATA_DIR}" \
107+
--binary-path "${REPO_main_dir}/target/debug/coreutils" \
108+
--output-types lcov \
109+
--output-path ${REPORT_PATH} \
110+
--llvm \
111+
--keep-only "${REPO_main_dir}"'/src/*'
112+
113+
114+
# Notify the report file to github
115+
echo "report=${REPORT_PATH}" >> $GITHUB_OUTPUT

0 commit comments

Comments
 (0)