|
1 | 1 | # #!/usr/bin/env bash
|
2 | 2 |
|
3 |
| -# (roughly) from https://github.com/mozilla/grcov , but don't use |
4 |
| -# codecov.io |
| 3 | +# From |
5 | 4 | #
|
6 |
| -# Writes a `target/coverage/report/index.html`, and outputs (at the end) |
7 |
| -# something like |
| 5 | +# https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/instrument-coverage.html |
8 | 6 | #
|
9 |
| -# ``` |
10 |
| -# Overall coverage rate: |
11 |
| -# lines......: 100.0% (57 of 57 lines) |
12 |
| -# functions..: 69.8% (30 of 43 functions) |
13 |
| -# ``` |
| 7 | +# and |
14 | 8 | #
|
15 |
| -# functions% is useless: grcov does not understand rust functions, but |
16 |
| -# lines% should get to 100% if you want complete coverage. The trick is |
17 |
| -# to put network code, long loops, and other non-unit-testable code in |
18 |
| -# `*system*.rs` files, which have trival `#[cfg(test)]` implementations. |
19 |
| -# Make sure you justify in comments why they should be excluded from |
20 |
| -# coverage. |
21 |
| -# |
22 |
| -# If you don't have `/target` added to your `.gitignore`, `.ignore`, |
23 |
| -# etc. files for some reason, you should at least include |
24 |
| -# `/target/coverage` |
25 |
| -# |
26 |
| -# first (MacOS): |
27 |
| -# ``` |
28 |
| -# rustup toolchain install nightly # for -Zprofile |
| 9 | +# https://marco-c.github.io/2020/11/24/rust-source-based-code-coverage.html |
| 10 | + |
| 11 | +## Do this first: |
| 12 | + |
29 | 13 | # rustup component add llvm-tools-preview
|
| 14 | +# cargo install cargo-binutils |
| 15 | +# cargo profdata -- --help |
30 | 16 | # cargo install grcov
|
31 |
| -# brew install lcov # for genhtml |
32 |
| -# ``` |
33 |
| - |
34 |
| -set -e |
35 | 17 |
|
36 |
| -if [[ ! -f Cargo.toml ]] ; then |
37 |
| - echo "ERROR: Must run in a root cargo project" |
38 |
| - exit -1 |
39 |
| -fi |
| 18 | +cmdname=`basename $0` |
40 | 19 |
|
41 | 20 | usage() {
|
42 | 21 | exitcode="$1"
|
43 | 22 | cat << USAGE >&2
|
44 | 23 | Usage:
|
45 | 24 | $cmdname [options]
|
46 |
| - -d Run dirty (don't clean): only use this if you |
47 |
| - haven't run cargo at all since the last rustcov. |
48 | 25 | -h Show this message
|
49 | 26 | USAGE
|
50 | 27 | exit "$exitcode"
|
51 | 28 | }
|
52 | 29 |
|
53 |
| -# Get our options |
54 |
| -dirty=0 |
55 |
| -while getopts "dh" opt ; do |
| 30 | +while getopts "h" opt ; do |
56 | 31 | case $opt in
|
57 |
| - d) dirty=1 ;; |
58 | 32 | h) usage 0 ;;
|
59 | 33 | esac
|
60 | 34 | done
|
61 | 35 |
|
62 |
| -# Assume that the name of the project is the same as `pwd` |
63 |
| -pname=$(basename `pwd`) |
64 |
| -uname=$(echo $pname | tr - _) |
65 |
| -tdir=target/coverage |
66 |
| - |
67 |
| -cargo_targets=$( |
68 |
| - IFS=$'\n' |
69 |
| - meta=`cargo +nightly metadata --format-version 1` |
70 |
| - for m in `jq -r '.workspace_members[]' <<< "$meta"` ; do |
71 |
| - jq -r ".packages[] | select(.id == \"$m\") | .targets[] | select(.kind[0] != \"custom-build\") | .name" <<< "$meta" | tr - _ |
72 |
| - done |
73 |
| -) |
74 |
| -cargo_targets=$(xargs echo <<< "$cargo_targets") |
75 |
| - |
76 |
| -workspace_members=$( |
77 |
| - cargo +nightly metadata --format-version 1 | jq -r '.workspace_members[]' | cut -d' ' -f1 |
78 |
| -) |
79 |
| -workspace_members=$(xargs echo <<< "$workspace_members") |
80 |
| - |
81 |
| -echo "running tests/coverage for ${cargo_targets}" |
82 |
| - |
83 |
| -export CARGO_INCREMENTAL=0 |
84 |
| -export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort" |
85 |
| -export RUSTDOCFLAGS="-Cpanic=abort" |
86 |
| - |
87 |
| -# Inelegent, but effective. We use --target on the cargo command to |
88 |
| -# prevent build scripts from seeing RUSTFLAGS (this is a |
89 |
| -# perhaps-temporarily intention of cargo). Some build scripts like |
90 |
| -# etcd-rs end up using panic/unwind, which panic=abort will cause to |
91 |
| -# fail. |
92 |
| -target=$(rustup show | grep default | sed -e "s/^[^-]*-\(.*\) .*/\1/") |
93 |
| - |
94 |
| -# Painful, but we need a complete build to coverage check. |
95 |
| -if [ $dirty == "0" ] ; then |
96 |
| - for w in "$workspace_members" ; do |
97 |
| - cargo +nightly clean -p "$w" --target ${target} |
98 |
| - done |
99 |
| - find target -name '*.gc*' -delete |
100 |
| -else |
101 |
| - find target -name '*.gcda' -delete |
102 |
| -fi |
103 |
| -rm -rf ${tdir} |
| 36 | +# echo cargo +nightly test "${@:$OPTIND}" |
104 | 37 |
|
105 |
| -# Actually (build and then) run tests |
106 |
| -cargo +nightly test --target ${target} "${@:$OPTIND}" |
| 38 | +cargo clean |
107 | 39 |
|
108 |
| -mkdir -p ${tdir} |
| 40 | +RUSTFLAGS="-Z instrument-coverage" \ |
| 41 | + LLVM_PROFILE_FILE="testing-%m.profraw" \ |
| 42 | + cargo +nightly test "${@:$OPTIND}" |
109 | 43 |
|
110 |
| -# zip -0 ${tdir}/ccov.zip `find . \( -name "${uname}*.gc*" \) -print` |
111 |
| -# grcov ${tdir}/ccov.zip -s . -t lcov --llvm --branch --ignore-not-existing \ |
112 |
| -# --ignore 'target/*' --ignore "/*" --ignore '*system*' \ |
113 |
| -# -o ${tdir}/lcov.info |
| 44 | +mkdir -p target/coverage |
114 | 45 |
|
115 |
| -grcov . -s . -t lcov --llvm --branch --ignore-not-existing \ |
116 |
| - --ignore 'target/*' --ignore "/*" --ignore '*system*' \ |
117 |
| - -o ${tdir}/lcov.info |
| 46 | +grcov . -b target/debug/deps \ |
| 47 | + -s . -t html --ignore 'target/*' --ignore '/*' \ |
| 48 | + --ignore-not-existing -o ./target/coverage |
118 | 49 |
|
119 |
| -genhtml -o ${tdir}/report/ --show-details --highlight --ignore-errors source \ |
120 |
| - --legend ${tdir}/lcov.info | tee ${tdir}/output |
| 50 | +rm -f testing-*.profraw |
121 | 51 |
|
122 |
| -# fail if there is no `100` in `lines...:` of genhtml output |
123 |
| -tail -5 ${tdir}/output | grep -i lines | grep -q 100 |
0 commit comments