Skip to content

Commit

Permalink
Coverage support.
Browse files Browse the repository at this point in the history
- open source CoverageCommand.java
- add a collect-coverage.sh script
- update test-setup.sh to be compatible with the coverage collector
- update StandaloneTestStrategy to provide the necessary env variables
- update StandaloneTestStrategy to set the right command line for coverage
- add support for C++ coverage

An HTML report can then be generated with genhtml like this:
genhtml -o report/ -p "$(readlink -f bazel-<project>)" path/to/coverage.dat

Progress on #1118.

--
MOS_MIGRATED_REVID=140125715
  • Loading branch information
ulfjack authored and dslomov committed Nov 24, 2016
1 parent 74ffaf7 commit 8829aba
Show file tree
Hide file tree
Showing 9 changed files with 451 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
@ExecutionStrategy(contextType = TestActionContext.class, name = { "standalone" })
public class StandaloneTestStrategy extends TestStrategy {
// TODO(bazel-team) - add tests for this strategy.
private static final String COLLECT_COVERAGE =
"external/bazel_tools/tools/coverage/collect-coverage.sh";

private final Path workspace;

Expand Down Expand Up @@ -100,9 +102,7 @@ public void exec(TestRunnerAction action, ActionExecutionContext actionExecution
Artifact testSetup = action.getRuntimeArtifact(TEST_SETUP_BASENAME);
Spawn spawn =
new BaseSpawn(
// Bazel lacks much of the tooling for coverage, so we don't attempt to pass a coverage
// script here.
getArgs(testSetup.getExecPathString(), "", action),
getArgs(testSetup.getExecPathString(), COLLECT_COVERAGE, action),
env,
info,
new RunfilesSupplierImpl(
Expand Down Expand Up @@ -172,7 +172,13 @@ private Map<String, String> getEnv(
if (!action.isEnableRunfiles()) {
vars.put("RUNFILES_MANIFEST_ONLY", "1");
}

if (isCoverageMode(action)) {
vars.put("COVERAGE_MANIFEST",
action.getExecutionSettings().getInstrumentedFileManifest().getExecPathString());
vars.put("COVERAGE_OUTPUT_FILE", action.getCoverageData().getExecPathString());
// Instruct test-setup.sh not to cd into the runfiles directory.
vars.put("RUNTEST_PRESERVE_CWD", "1");
}
return vars;
}

Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

Usage: blaze %{command} <options> <test-targets>

Builds and runs the specified test targets using the specified options while
collecting code coverage statistics. Optionally, it also generates combined
HTML report containing coverage results for all executed tests.

This command accepts all valid options to 'test' and 'build', and inherits
defaults for 'test' (and 'build') from your .blazerc. If you don't use
.blazerc, don't forget to pass all your 'build' options to '%{command}' too.

See 'blaze help target-syntax' for details and examples on how to
specify targets.

%{options}
2 changes: 2 additions & 0 deletions tools/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ filegroup(
"//tools/build_defs/pkg:srcs",
"//tools/build_defs/repo:srcs",
"//tools/build_rules:srcs",
"//tools/coverage:srcs",
"//tools/proto/toolchains:srcs",
"//tools/ide:srcs",
"//tools/jdk:srcs",
Expand All @@ -42,6 +43,7 @@ filegroup(
"//tools/build_defs/repo:srcs",
"//tools/build_rules:embedded_tools_srcs",
"//tools/buildstamp:srcs",
"//tools/coverage:srcs",
"//tools/proto/toolchains:srcs",
"//tools/cpp:srcs",
"//tools/genrule:srcs",
Expand Down
16 changes: 16 additions & 0 deletions tools/coverage/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package(default_visibility = ["//visibility:public"])

filegroup(
name = "coverage_support",
srcs = ["collect-coverage.sh"],
)

filegroup(
name = "coverage_report_generator",
srcs = ["dummy_coverage_report_generator"],
)

filegroup(
name = "srcs",
srcs = glob(["*"]),
)
72 changes: 72 additions & 0 deletions tools/coverage/collect-coverage.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/bin/bash

# Copyright 2016 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

ROOT="$PWD"
if [[ $COVERAGE_OUTPUT_FILE != /* ]]; then
COVERAGE_OUTPUT_FILE="${ROOT}/${COVERAGE_OUTPUT_FILE}"
fi
if [[ "$COVERAGE_MANIFEST" != /* ]]; then
export COVERAGE_MANIFEST="${ROOT}/${COVERAGE_MANIFEST}"
fi

export COVERAGE_DIR="$(mktemp -d ${TMPDIR:-/tmp}/tmp.XXXXXXXXXX)"
trap "{ rm -rf ${COVERAGE_DIR} }" EXIT

# C++ env variables
export GCOV_PREFIX_STRIP=3
export GCOV_PREFIX="${COVERAGE_DIR}"

touch "${COVERAGE_OUTPUT_FILE}"

DIR="$TEST_SRCDIR"
if [ ! -z "$TEST_WORKSPACE" ]; then
DIR="$DIR"/"$TEST_WORKSPACE"
fi
cd "$DIR" || { echo "Could not chdir $DIR"; exit 1; }
"$@"
TEST_STATUS=$?

if [[ ${TEST_STATUS} -ne 0 ]]; then
echo "--"
echo "Coverage runner: Not collecting coverage for failed test."
echo "The following commands failed with status ${TEST_STATUS}:"
echo "$@"
exit ${TEST_STATUS}
fi

echo "--"
echo "Post-processing coverage results:"

cat "${COVERAGE_MANIFEST}" | grep ".gcno$" | while read path; do
mkdir -p "${COVERAGE_DIR}/$(dirname ${path})"
cp "${ROOT}/${path}" "${COVERAGE_DIR}/${path}"
done

# Unfortunately, lcov messes up the source file names if it can't find the files
# at their relative paths. Workaround by creating empty source files according
# to the manifest (i.e., only for files that are supposed to be instrumented).
cat "${COVERAGE_MANIFEST}" | egrep ".(cc|h)$" | while read path; do
mkdir -p "${COVERAGE_DIR}/$(dirname ${path})"
touch "${COVERAGE_DIR}/${path}"
done

# Run lcov over the .gcno and .gcda files to generate the lcov tracefile.
/usr/bin/lcov -c --no-external -d "${COVERAGE_DIR}" -o "${COVERAGE_OUTPUT_FILE}"

# The paths are all wrong, because they point to /tmp. Fix up the paths to
# point to the exec root instead (${ROOT}).
sed -i -e "s*${COVERAGE_DIR}*${ROOT}*g" "${COVERAGE_OUTPUT_FILE}"

File renamed without changes.
4 changes: 2 additions & 2 deletions tools/test/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ filegroup(

filegroup(
name = "coverage_support",
srcs = [],
srcs = ["//tools/coverage:coverage_support"],
)

filegroup(
name = "coverage_report_generator",
srcs = ["dummy_coverage_report_generator"],
srcs = ["//tools/coverage:coverage_report_generator"],
)

filegroup(
Expand Down
14 changes: 7 additions & 7 deletions tools/test/test-setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,21 +46,20 @@ if [[ -n "${TEST_TOTAL_SHARDS+x}" ]] && ((TEST_TOTAL_SHARDS != 0)); then
fi
export GTEST_TMP_DIR="${TEST_TMPDIR}"

DIR="$TEST_SRCDIR"
RUNFILES_MANIFEST_FILE=$DIR/MANIFEST
RUNFILES_MANIFEST_FILE="${TEST_SRCDIR}/MANIFEST"

if [ -z "$RUNFILES_MANIFEST_ONLY" ]; then
function rlocation() {
if [[ "$1" = /* ]]; then
echo $1
echo "$1"
else
echo "$(dirname $RUNFILES_MANIFEST_FILE)/$1"
fi
}
else
function rlocation() {
if [[ "$1" = /* ]]; then
echo $1
echo "$1"
else
echo $(grep "^$1 " $RUNFILES_MANIFEST_FILE | awk '{ print $2 }')
fi
Expand All @@ -70,11 +69,11 @@ fi
export -f rlocation
export RUNFILES_MANIFEST_FILE

if [ ! -z "$TEST_WORKSPACE" ]
then
DIR="$TEST_SRCDIR"
if [ ! -z "$TEST_WORKSPACE" ]; then
DIR="$DIR"/"$TEST_WORKSPACE"
fi

[[ -n "$RUNTEST_PRESERVE_CWD" ]] && DIR="$PWD"


# normal commands are run in the exec-root where they have access to
Expand All @@ -100,6 +99,7 @@ if [[ "$TEST_NAME" = /* ]]; then
else
EXE="$(rlocation $TEST_WORKSPACE/$TEST_NAME)"
fi
[[ -n "$RUNTEST_PRESERVE_CWD" ]] && EXE="${TEST_NAME}"

exitCode=0
"${EXE}" "$@" || exitCode=$?
Expand Down

0 comments on commit 8829aba

Please sign in to comment.