blob: 10d8cc8b5d87dd8dc674c67681ee85ff1cb478f6 [file] [log] [blame]
#!/bin/bash -eu
#
# Copyright 2018 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.
# Unit tests for tools/test/collect_cc_code_coverage.sh
# Load the test setup defined in the parent directory
CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${CURRENT_DIR}/../integration_test_setup.sh" \
|| { echo "integration_test_setup.sh not found!" >&2; exit 1; }
# Check if all the tools required by CC coverage are installed.
[[ ! -x /usr/bin/lcov ]] && echo "lcov not installed. Skipping test" && exit 0
[[ -z $( which gcov ) ]] && fail "gcov not installed. Skipping test" && exit 0
[[ -z $( which g++ ) ]] && fail "g++ not installed. Skipping test" && exit 0
# These are the variables needed by tools/test/collect_cc_coverage.sh
# They will be properly sub-shelled when invoking the script.
# Directory containing gcda files.
readonly COVERAGE_DIR_VAR="${PWD}"
# Location of gcov.
readonly COVERAGE_GCOV_PATH_VAR="${PWD}/mygcov"
# Location from where the code coverage collection was invoked.
readonly ROOT_VAR="${PWD}"
# Location of the instrumented file manifest.
readonly COVERAGE_MANIFEST_VAR="${PWD}/COVERAGE_MANIFEST_VAR.txt"
# Location of the final coverage report.
readonly COVERAGE_OUTPUT_FILE_VAR="${PWD}/coverage_report.dat"
# Path to the canonical C++ coverage script.
readonly COLLECT_CC_COVERAGE_SCRIPT=tools/test/collect_cc_coverage.sh
# Setup to be run for every test.
function set_up() {
# The script expects gcov to be at $COVERAGE_GCOV_PATH.
cp $( which gcov ) "$COVERAGE_GCOV_PATH_VAR"
# The script expects the output file to already exist.
# TODO(iirina): In the future it would be better if the
# script creates the output file.
touch "$COVERAGE_OUTPUT_FILE_VAR"
echo "coverage_srcs/a.gcno" >> "$COVERAGE_MANIFEST_VAR"
# Create the CC sources.
mkdir -p coverage_srcs/
cat << EOF > coverage_srcs/a.h
int a(bool what);
EOF
cat << EOF > coverage_srcs/a.cc
#include "a.h"
int a(bool what) {
if (what) {
return 1;
} else {
return 2;
}
}
EOF
cat << EOF > coverage_srcs/t.cc
#include <stdio.h>
#include "a.h"
int main(void) {
a(true);
}
EOF
generate_gcno_files coverage_srcs/a.h coverage_srcs/a.cc coverage_srcs/t.cc
generate_instrumented_binary ./coverage_srcs/test coverage_srcs/a.h \
coverage_srcs/a.cc coverage_srcs/t.cc
generate_gcda_file ./coverage_srcs/test
}
# Reads the list of arguments provided by the caller (using $@) and uses them
# to produco .gcno files using g++.
function generate_gcno_files() {
# "-fprofile-arcs -ftest-coverage" tells the compiler to generate coverage
# information needed by gcov and include additional code in the object files
# for generating the profiling.
g++ -fprofile-arcs -ftest-coverage "$@" && return 0
fail "Couldn't produce .gcno files for $@"
return 1
}
# Reads the list of arguments provided by the caller (using $@) and uses them
# to produce an instrumented binary using g++.
# - path_to_binary destination of the binary produced by g++
function generate_instrumented_binary() {
local path_to_binary="${1}"; shift
# "-fprofile-arcs -ftest-coverage" tells the compiler to generate coverage
# information needed by gcov and include additional code in the object files
# for generating the profiling.
g++ -fprofile-arcs -ftest-coverage "$@" -o "$path_to_binary" && return 0
fail "Couldn't produce the instrumented binary for $@ \
with path_to_binary $path_to_binary"
return 1
}
# Execute an instrumented binary and generate the gcda file.
# - path_to_binary path of instrumented binary
function generate_gcda_file() {
local path_to_binary="${1}"
"$path_to_binary" && return 0
fail "Couldn't execute the instrumented binary $path_to_binary"
return 1
}
function tear_down() {
rm -rf coverage_srcs/
}
# Runs the script that computes the code coverage report for CC code.
# Sets up the sub-shell environment accordingly:
# - COVERAGE_DIR Directory containing gcda files.
# - COVERAGE_MANIFEST Location of the instrumented file manifest.
# - COVERAGE_OUTPUT_FILE Location of the final coverage report.
# - COVERAGE_GCOV_PATH Location of gcov.
# - ROOT Location from where the code coverage collection
# was invoked.
function run_coverage() {
(COVERAGE_DIR="$COVERAGE_DIR_VAR" \
COVERAGE_GCOV_PATH="$COVERAGE_GCOV_PATH_VAR" \
ROOT="$ROOT_VAR" COVERAGE_MANIFEST="$COVERAGE_MANIFEST_VAR" \
COVERAGE_OUTPUT_FILE="$COVERAGE_OUTPUT_FILE_VAR" \
"$COLLECT_CC_COVERAGE_SCRIPT")
}
function test_cc_test_coverage() {
run_coverage > "$TEST_log"
# After running the test in coverage_srcs/t.cc, the sources covered are the
# test itself and the source file a.cc.
# For more details about the lcov format see
# http://ltp.sourceforge.net/coverage/lcov/geninfo.1.php
# The expected result can be constructed manually by following the lcov
# documentation and manually checking what lines of code are covered when
# running the test.
cat <<EOF > expected_result.dat
TN:
SF:coverage_srcs/a.cc
FN:3,_Z1ab
FNDA:1,_Z1ab
FNF:1
FNH:1
DA:3,1
DA:4,1
DA:5,1
DA:7,0
LF:4
LH:3
end_of_record
TN:
SF:coverage_srcs/t.cc
FN:4,main
FNDA:1,main
FNF:1
FNH:1
DA:4,1
DA:5,1
DA:6,1
LF:3
LH:3
end_of_record
EOF
# tools/test/collect_cc_coverage.sh places the coverage result in
# $COVERAGE_OUTPUT_FILE
diff -u expected_result.dat "$COVERAGE_OUTPUT_FILE_VAR" >> "$TEST_log" \
|| fail "Coverage output file is different than the expected file"
}
run_suite "Testing tools/test/collect_cc_coverage.sh"