blob: 5890239fe51020eabb5553289f91ec4356c3d260 [file] [log] [blame]
#!/bin/bash
#
# Copyright 2015 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.
set -eu
# 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; }
# Writes the C++ source files and a corresponding BUILD file for which to
# collect code coverage. The sources are a.cc, a.h and t.cc.
function setup_a_cc_lib_and_t_cc_test() {
cat << EOF > BUILD
cc_library(
name = "a",
srcs = ["a.cc"],
hdrs = ["a.h"],
)
cc_test(
name = "t",
srcs = ["t.cc"],
deps = [":a"],
)
EOF
cat << EOF > a.h
int a(bool what);
EOF
cat << EOF > a.cc
#include "a.h"
int a(bool what) {
if (what) {
return 1;
} else {
return 2;
}
}
EOF
cat << EOF > t.cc
#include <stdio.h>
#include "a.h"
int main(void) {
a(true);
}
EOF
}
# Returns 0 if gcov is not installed or if a version before 7.0 was found.
# Returns 1 otherwise.
function is_gcov_missing_or_wrong_version() {
local -r gcov_location=$(which gcov)
if [[ ! -x ${gcov_location:-/usr/bin/gcov} ]]; then
echo "gcov not installed."
return 0
fi
"$gcov_location" -version | grep "LLVM" && \
echo "gcov LLVM version not supported." && return 0
# gcov -v | grep "gcov" outputs a line that looks like this:
# gcov (Debian 7.3.0-5) 7.3.0
local gcov_version="$(gcov -v | grep "gcov" | cut -d " " -f 4 | cut -d "." -f 1)"
[ "$gcov_version" -lt 7 ] \
&& echo "gcov versions before 7.0 is not supported." && return 0
return 1
}
# Asserts if the given expected coverage result is included in the given output
# file.
#
# - expected_coverage The expected result that must be included in the output.
# - output_file The location of the coverage output file.
function assert_coverage_result() {
local expected_coverage="${1}"; shift
local output_file="${1}"; shift
# Replace newlines with commas to facilitate the assertion.
local expected_coverage_no_newlines="$( echo "$expected_coverage" | tr '\n' ',' )"
local output_file_no_newlines="$( cat "$output_file" | tr '\n' ',' )"
(echo "$output_file_no_newlines" \
| grep -F "$expected_coverage_no_newlines") \
|| fail "Expected coverage result
<$expected_coverage>
was not found in actual coverage report:
<$( cat "$output_file" )>"
}
# Returns the path of the code coverage report that was generated by Bazel by
# looking at the current $TEST_log. The method fails if TEST_log does not
# contain any coverage report for a passed test.
function get_coverage_file_path_from_test_log() {
local ending_part="$(sed -n -e '/PASSED/,$p' "$TEST_log")"
local coverage_file_path=$(grep -Eo "/[/a-zA-Z0-9\.\_\-]+\.dat$" <<< "$ending_part")
[[ -e "$coverage_file_path" ]] || fail "Coverage output file does not exist!"
echo "$coverage_file_path"
}
function test_cc_test_coverage_gcov() {
if is_gcov_missing_or_wrong_version; then
echo "Skipping test." && return
fi
setup_a_cc_lib_and_t_cc_test
bazel coverage --test_output=all \
--build_event_text_file=bep.txt //:t &>"$TEST_log" \
|| fail "Coverage for //:t failed"
local coverage_file_path="$( get_coverage_file_path_from_test_log )"
# Check the expected coverage for a.cc in the coverage file.
local expected_result_a_cc="SF:a.cc
FN:3,_Z1ab
FNDA:1,_Z1ab
FNF:1
FNH:1
DA:3,1
DA:4,1
DA:5,1
DA:7,0
LH:3
LF:4
end_of_record"
assert_coverage_result "$expected_result_a_cc" "$coverage_file_path"
# t.cc is not included in the coverage report because test targets are not
# instrumented by default.
assert_not_contains "SF:t\.cc" "$coverage_file_path"
# Verify that this is also true for cached coverage actions.
bazel coverage --test_output=all \
--build_event_text_file=bep.txt //:t \
&>"$TEST_log" || fail "Coverage for //:t failed"
expect_log '//:t.*cached'
# Verify the files are reported correctly in the build event protocol.
assert_contains 'name: "test.lcov"' bep.txt
assert_contains 'name: "baseline.lcov"' bep.txt
}
# TODO(#6254): Enable this test when #6254 is fixed.
function test_cc_test_coverage_gcov_virtual_includes() {
if is_gcov_missing_or_wrong_version; then
echo "Skipping test." && return
fi
########### Setup source files and BUILD file ###########
mkdir -p examples/cpp
cat << EOF > examples/cpp/BUILD
cc_library(
name = "a_header",
hdrs = ["foo/bar/baz/a_header.h"],
strip_include_prefix = "foo",
include_prefix = "yin/yang",
)
cc_library(
name = "hello-lib",
srcs = ["hello-lib.cc"],
hdrs = ["hello-lib.h"],
deps = [":a_header"],
)
cc_test(
name = "hello-world_test",
srcs = ["hello-world.cc"],
deps = [":hello-lib"],
)
EOF
mkdir -p examples/cpp/foo/bar/baz
cat << EOF > examples/cpp/foo/bar/baz/a_header.h
#include<iostream>
using namespace std;
class A {
public:
void cout_whatever() const {
cout << "Whatever";
}
};
EOF
cat << EOF > examples/cpp/hello-lib.h
#ifndef EXAMPLES_CPP_HELLO_LIB_H_
#define EXAMPLES_CPP_HELLO_LIB_H_
#include <string>
#include <memory>
#include "yin/yang/bar/baz/a_header.h"
namespace hello {
class HelloLib {
public:
explicit HelloLib(const std::string &greeting);
void greet(const std::string &thing);
private:
std::auto_ptr<const std::string> greeting_;
};
} // namespace hello
#endif // EXAMPLES_CPP_HELLO_LIB_H_
EOF
cat << EOF > examples/cpp/hello-lib.cc
#include "examples/cpp/hello-lib.h"
#include <iostream>
using std::cout;
using std::endl;
using std::string;
namespace hello {
HelloLib::HelloLib(const string& greeting) : greeting_(new string(greeting)) {
}
void HelloLib::greet(const string& thing) {
cout << *greeting_ << " " << thing << endl;
A* a = new A();
a->cout_whatever();
}
} // namespace hello
EOF
cat << EOF > examples/cpp/hello-world.cc
#include "examples/cpp/hello-lib.h"
#include <string>
using hello::HelloLib;
using std::string;
int main(int argc, char** argv) {
HelloLib lib("Hello");
string thing = "world";
if (argc > 1) {
thing = argv[1];
}
lib.greet(thing);
return 0;
}
EOF
########### Run bazel coverage ###########
bazel coverage --test_output=all //examples/cpp:hello-world_test &>"$TEST_log" \
|| fail "Coverage for //examples/cpp:hello-world_test failed"
########### Assert coverage results. ###########
local coverage_file_path="$( get_coverage_file_path_from_test_log )"
local expected_result_hello_lib="SF:examples/cpp/hello-lib.cc
FN:19,_GLOBAL__sub_I_hello_lib.cc
FN:19,_Z41__static_initialization_and_destruction_0ii
FN:14,_ZN5hello8HelloLib5greetERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
FN:11,_ZN5hello8HelloLibC2ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
FNDA:1,_GLOBAL__sub_I_hello_lib.cc
FNDA:1,_Z41__static_initialization_and_destruction_0ii
FNDA:1,_ZN5hello8HelloLib5greetERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
FNDA:1,_ZN5hello8HelloLibC2ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
FNF:4
FNH:4
DA:11,1
DA:12,1
DA:14,1
DA:15,1
DA:16,1
DA:17,1
DA:18,1
DA:19,3
LH:8
LF:8
end_of_record"
assert_coverage_result "$expected_result_hello_lib" "$coverage_file_path"
local expected_result_a_header="SF:examples/cpp/foo/bar/baz/a_header.h
FN:6,_ZNK1A13cout_whateverEv
FNDA:1,_ZNK1A13cout_whateverEv
FNF:1
FNH:1
DA:6,1
DA:7,1
DA:8,1
LH:3
LF:3
end_of_record"
assert_coverage_result "$expected_result_a_header" "$coverage_file_path"
local coverage_result_hello_lib_header="SF:examples/cpp/hello-lib.h
FN:10,_ZN5hello8HelloLibD2Ev
FNDA:1,_ZN5hello8HelloLibD2Ev
FNF:1
FNH:1
DA:10,1
LH:1
LF:1
end_of_record"
assert_coverage_result "$coverage_result_hello_lib_header" "$coverage_file_path"
}
function test_cc_test_gcov_multiple_headers() {
if is_gcov_missing_or_wrong_version; then
echo "Skipping test." && return
fi
############## Setting up the test sources and BUILD file ##############
mkdir -p "coverage_srcs/"
cat << EOF > BUILD
cc_library(
name = "a",
srcs = ["coverage_srcs/a.cc"],
hdrs = ["coverage_srcs/a.h", "coverage_srcs/b.h"]
)
cc_test(
name = "t",
srcs = ["coverage_srcs/t.cc"],
deps = [":a"]
)
EOF
cat << EOF > "coverage_srcs/a.h"
int a(bool what);
EOF
cat << EOF > "coverage_srcs/a.cc"
#include "a.h"
#include "b.h"
#include <iostream>
int a(bool what) {
if (what) {
std::cout << "Calling b(1)";
return b(1);
} else {
std::cout << "Calling b(-1)";
return b(-1);
}
}
EOF
cat << EOF > "coverage_srcs/b.h"
int b(int what) {
if (what > 0) {
return 1;
} else {
return 2;
}
}
EOF
cat << EOF > "coverage_srcs/t.cc"
#include <stdio.h>
#include "a.h"
int main(void) {
a(true);
}
EOF
############## Running bazel coverage ##############
bazel coverage --test_output=all //:t \
&>"$TEST_log" || fail "Coverage for //:t failed"
##### Putting together the expected coverage results #####
local coverage_file_path="$( get_coverage_file_path_from_test_log )"
local expected_result_a_cc="SF:coverage_srcs/a.cc
FN:13,_GLOBAL__sub_I_a.cc
FN:5,_Z1ab
FN:13,_Z41__static_initialization_and_destruction_0ii
FNDA:1,_GLOBAL__sub_I_a.cc
FNDA:1,_Z1ab
FNDA:1,_Z41__static_initialization_and_destruction_0ii
FNF:3
FNH:3
DA:5,1
DA:6,1
DA:7,1
DA:8,1
DA:10,0
DA:11,0
DA:13,3
LH:5
LF:7
end_of_record"
local expected_result_b_h="SF:coverage_srcs/b.h
FN:1,_Z1bi
FNDA:1,_Z1bi
FNF:1
FNH:1
DA:1,1
DA:2,1
DA:3,1
DA:5,0
LH:3
LF:4
end_of_record"
local expected_result_t_cc="SF:coverage_srcs/t.cc
FN:4,main
FNDA:1,main
FNF:1
FNH:1
DA:4,1
DA:5,1
DA:6,1
LH:3
LF:3
end_of_record"
############## Asserting the coverage results ##############
assert_coverage_result "$expected_result_a_cc" "$coverage_file_path"
assert_coverage_result "$expected_result_b_h" "$coverage_file_path"
# coverage_srcs/t.cc is not included in the coverage report because the test
# targets are not instrumented by default.
assert_not_contains "SF:coverage_srcs/t\.cc" "$coverage_file_path"
# iostream should not be in the final coverage report because it is a syslib
assert_not_contains "iostream" "$coverage_file_path"
}
function test_cc_test_gcov_multiple_headers_instrument_test_target() {
if is_gcov_missing_or_wrong_version; then
echo "Skipping test." && return
fi
############## Setting up the test sources and BUILD file ##############
mkdir -p "coverage_srcs/"
cat << EOF > BUILD
cc_library(
name = "a",
srcs = ["coverage_srcs/a.cc"],
hdrs = ["coverage_srcs/a.h", "coverage_srcs/b.h"]
)
cc_test(
name = "t",
srcs = ["coverage_srcs/t.cc"],
deps = [":a"]
)
EOF
cat << EOF > "coverage_srcs/a.h"
int a(bool what);
EOF
cat << EOF > "coverage_srcs/a.cc"
#include "a.h"
#include "b.h"
#include <iostream>
int a(bool what) {
if (what) {
std::cout << "Calling b(1)";
return b(1);
} else {
std::cout << "Calling b(-1)";
return b(-1);
}
}
EOF
cat << EOF > "coverage_srcs/b.h"
int b(int what) {
if (what > 0) {
return 1;
} else {
return 2;
}
}
EOF
cat << EOF > "coverage_srcs/t.cc"
#include <stdio.h>
#include "a.h"
int main(void) {
a(true);
}
EOF
############## Running bazel coverage ##############
bazel coverage --instrument_test_targets \
--test_output=all //:t &>"$TEST_log" || fail "Coverage for //:t failed"
##### Putting together the expected coverage results #####
local coverage_file_path="$( get_coverage_file_path_from_test_log )"
local expected_result_a_cc="SF:coverage_srcs/a.cc
FN:13,_GLOBAL__sub_I_a.cc
FN:5,_Z1ab
FN:13,_Z41__static_initialization_and_destruction_0ii
FNDA:1,_GLOBAL__sub_I_a.cc
FNDA:1,_Z1ab
FNDA:1,_Z41__static_initialization_and_destruction_0ii
FNF:3
FNH:3
DA:5,1
DA:6,1
DA:7,1
DA:8,1
DA:10,0
DA:11,0
DA:13,3
LH:5
LF:7
end_of_record"
local expected_result_b_h="SF:coverage_srcs/b.h
FN:1,_Z1bi
FNDA:1,_Z1bi
FNF:1
FNH:1
DA:1,1
DA:2,1
DA:3,1
DA:5,0
LH:3
LF:4
end_of_record"
local expected_result_t_cc="SF:coverage_srcs/t.cc
FN:4,main
FNDA:1,main
FNF:1
FNH:1
DA:4,1
DA:5,1
DA:6,1
LH:3
LF:3
end_of_record"
############## Asserting the coverage results ##############
assert_coverage_result "$expected_result_a_cc" "$coverage_file_path"
assert_coverage_result "$expected_result_b_h" "$coverage_file_path"
# coverage_srcs/t.cc should be included in the coverage report
assert_coverage_result "$expected_result_t_cc" "$coverage_file_path"
# iostream should not be in the final coverage report because it is a syslib
assert_not_contains "iostream" "$coverage_file_path"
}
function test_cc_test_gcov_same_header_different_libs() {
if is_gcov_missing_or_wrong_version; then
echo "Skipping test." && return
fi
############## Setting up the test sources and BUILD file ##############
mkdir -p "coverage_srcs/"
cat << EOF > BUILD
cc_library(
name = "a",
srcs = ["coverage_srcs/a.cc"],
hdrs = ["coverage_srcs/a.h", "coverage_srcs/b.h"]
)
cc_library(
name = "c",
srcs = ["coverage_srcs/c.cc"],
hdrs = ["coverage_srcs/c.h", "coverage_srcs/b.h"]
)
cc_test(
name = "t",
srcs = ["coverage_srcs/t.cc"],
deps = [":a", ":c"]
)
EOF
cat << EOF > "coverage_srcs/a.h"
int a(bool what);
EOF
cat << EOF > "coverage_srcs/a.cc"
#include "a.h"
#include "b.h"
int a(bool what) {
if (what) {
return b_for_a(1);
} else {
return b_for_a(-1);
}
}
EOF
cat << EOF > "coverage_srcs/b.h"
// Lines 2-8 are covered by calling b_for_a from a.cc.
int b_for_a(int what) { // Line 2: executed once
if (what > 0) { // Line 3: executed once
return 1; // Line 4: executed once
} else {
return 2; // Line 6: not executed
}
}
// Lines 11-17 are covered by calling b_for_a from a.cc.
int b_for_c(int what) { // Line 11: executed once
if (what > 0) { // Line 12: executed once
return 1; // Line 13: not executed
} else {
return 2; // Line 15: executed once
}
}
EOF
cat << EOF > "coverage_srcs/c.h"
int c(bool what);
EOF
cat << EOF > "coverage_srcs/c.cc"
#include "c.h"
#include "b.h"
int c(bool what) {
if (what) {
return b_for_c(1);
} else {
return b_for_c(-1);
}
}
EOF
cat << EOF > "coverage_srcs/t.cc"
#include "a.h"
#include "c.h"
int main(void) {
a(true);
c(false);
}
EOF
############## Running bazel coverage ##############
bazel coverage --test_output=all //:t \
&>"$TEST_log" || fail "Coverage for //:t failed"
##### Putting together the expected coverage results #####
local coverage_file_path="$( get_coverage_file_path_from_test_log )"
local expected_result_a_cc="SF:coverage_srcs/a.cc
FN:4,_Z1ab
FNDA:1,_Z1ab
FNF:1
FNH:1
DA:4,1
DA:5,1
DA:6,1
DA:8,0
LH:3
LF:4
end_of_record"
local expected_result_b_h="SF:coverage_srcs/b.h
FN:2,_Z7b_for_ai
FN:11,_Z7b_for_ci
FNDA:1,_Z7b_for_ai
FNDA:1,_Z7b_for_ci
FNF:2
FNH:2
DA:2,1
DA:3,1
DA:4,1
DA:6,0
DA:11,1
DA:12,1
DA:13,0
DA:15,1
LH:6
LF:8
end_of_record"
local expected_result_c_cc="SF:coverage_srcs/c.cc
FN:4,_Z1cb
FNDA:1,_Z1cb
FNF:1
FNH:1
DA:4,1
DA:5,1
DA:6,0
DA:8,1
LH:3
LF:4
end_of_record"
############## Asserting the coverage results ##############
assert_coverage_result "$expected_result_a_cc" "$coverage_file_path"
assert_coverage_result "$expected_result_b_h" "$coverage_file_path"
assert_coverage_result "$expected_result_c_cc" "$coverage_file_path"
# coverage_srcs/t.cc is not included in the coverage report because the test
# targets are not instrumented by default.
assert_not_contains "SF:coverage_srcs/t\.cc" "$coverage_file_path"
}
function test_cc_test_gcov_same_header_different_libs_multiple_exec() {
if is_gcov_missing_or_wrong_version; then
echo "Skipping test." && return
fi
############## Setting up the test sources and BUILD file ##############
mkdir -p "coverage_srcs/"
cat << EOF > BUILD
cc_library(
name = "a",
srcs = ["coverage_srcs/a.cc"],
hdrs = ["coverage_srcs/a.h", "coverage_srcs/b.h"]
)
cc_library(
name = "c",
srcs = ["coverage_srcs/c.cc"],
hdrs = ["coverage_srcs/c.h", "coverage_srcs/b.h"]
)
cc_test(
name = "t",
srcs = ["coverage_srcs/t.cc"],
deps = [":a", ":c"]
)
EOF
cat << EOF > "coverage_srcs/a.h"
int a(bool what);
int a_redirect();
EOF
cat << EOF > "coverage_srcs/a.cc"
#include "a.h"
#include "b.h"
int a(bool what) {
if (what) {
return b_for_a(1);
} else {
return b_for_a(-1);
}
}
int a_redirect() {
return b_for_all();
}
EOF
cat << EOF > "coverage_srcs/b.h"
// Lines 2-8 are covered by calling b_for_a from a.cc.
int b_for_a(int what) { // Line 2: executed once
if (what > 0) { // Line 3: executed once
return 1; // Line 4: executed once
} else {
return 2; // Line 6: not executed
}
}
// Lines 11-17 are covered by calling b_for_a from a.cc.
int b_for_c(int what) { // Line 11: executed once
if (what > 0) { // Line 12: executed once
return 1; // Line 13: not executed
} else {
return 2; // Line 15: executed once
}
}
int b_for_all() { // Line 21: executed 3 times (2x from a.cc and 1x from c.cc)
return 10; // Line 21: executed 3 times (2x from a.cc and 1x from c.cc)
}
EOF
cat << EOF > "coverage_srcs/c.h"
int c(bool what);
int c_redirect();
EOF
cat << EOF > "coverage_srcs/c.cc"
#include "c.h"
#include "b.h"
int c(bool what) {
if (what) {
return b_for_c(1);
} else {
return b_for_c(-1);
}
}
int c_redirect() {
return b_for_all();
}
EOF
cat << EOF > "coverage_srcs/t.cc"
#include "a.h"
#include "c.h"
int main(void) {
a(true);
c(false);
a_redirect();
a_redirect();
c_redirect();
}
EOF
############## Running bazel coverage ##############
bazel coverage --test_output=all //:t \
&>"$TEST_log" || fail "Coverage for //:t failed"
##### Putting together the expected coverage results #####
local coverage_file_path="$( get_coverage_file_path_from_test_log )"
local expected_result_a_cc="SF:coverage_srcs/a.cc
FN:12,_Z10a_redirectv
FN:4,_Z1ab
FNDA:2,_Z10a_redirectv
FNDA:1,_Z1ab
FNF:2
FNH:2
DA:4,1
DA:5,1
DA:6,1
DA:8,0
DA:12,2
DA:13,2
LH:5
LF:6
end_of_record"
local expected_result_b_h="SF:coverage_srcs/b.h
FN:2,_Z7b_for_ai
FN:11,_Z7b_for_ci
FN:19,_Z9b_for_allv
FNDA:1,_Z7b_for_ai
FNDA:1,_Z7b_for_ci
FNDA:3,_Z9b_for_allv
FNF:3
FNH:3
DA:2,1
DA:3,1
DA:4,1
DA:6,0
DA:11,1
DA:12,1
DA:13,0
DA:15,1
DA:19,3
DA:20,3
LH:8
LF:10
end_of_record"
local expected_result_c_cc="SF:coverage_srcs/c.cc
FN:12,_Z10c_redirectv
FN:4,_Z1cb
FNDA:1,_Z10c_redirectv
FNDA:1,_Z1cb
FNF:2
FNH:2
DA:4,1
DA:5,1
DA:6,0
DA:8,1
DA:12,1
DA:13,1
LH:5
LF:6
end_of_record"
############## Asserting the coverage results ##############
assert_coverage_result "$expected_result_a_cc" "$coverage_file_path"
assert_coverage_result "$expected_result_b_h" "$coverage_file_path"
assert_coverage_result "$expected_result_c_cc" "$coverage_file_path"
# coverage_srcs/t.cc is not included in the coverage report because the test
# targets are not instrumented by default.
assert_not_contains "SF:coverage_srcs/t\.cc" "$coverage_file_path"
}
function test_failed_coverage() {
local -r LCOV=$(which lcov)
if [[ ! -x ${LCOV:-/usr/bin/lcov} ]]; then
echo "lcov not installed. Skipping test."
return
fi
cat << EOF > BUILD
cc_library(
name = "a",
srcs = ["a.cc"],
hdrs = ["a.h"],
)
cc_test(
name = "t",
srcs = ["t.cc"],
deps = [":a"],
)
EOF
cat << EOF > a.h
int a();
EOF
cat << EOF > a.cc
#include "a.h"
int a() {
return 1;
}
EOF
cat << EOF > t.cc
#include <stdio.h>
#include "a.h"
int main(void) {
return a();
}
EOF
bazel coverage --test_output=all --build_event_text_file=bep.txt //:t \
&>$TEST_log && fail "Expected test failure" || :
# Verify that coverage data is still reported.
assert_contains 'name: "test.lcov"' bep.txt
}
function test_coverage_doesnt_fail_on_empty_output() {
if is_gcov_missing_or_wrong_version; then
echo "Skipping test." && return
fi
mkdir empty_cov
cat << EOF > empty_cov/t.cc
#include <stdio.h>
int main(void) {
return 0;
}
EOF
cat << EOF > empty_cov/BUILD
cc_test(
name = "empty-cov-test",
srcs = ["t.cc"]
)
EOF
bazel coverage --test_output=all \
//empty_cov:empty-cov-test &>"$TEST_log" \
|| fail "Coverage for //empty_cov:empty-cov-test failed"
expect_log "WARNING: There was no coverage found."
}
run_suite "test tests"