Move C++ coverage collection logic out of collect_coverage.sh

This PR pulls the C++ code coverage collection logic out of `toos/test/of collect_coverage.sh` and moves it to a new script `tools/test/collect_cc_coverage.sh`. There are 2 reasons for this:
* to make the scripts easier to understand and maintain
* in preparation for having a more general code collection logic

This PR does not change the behavior of Bazel, is just a no-op refactoring.

Progress on #5882

Closes #5801.

PiperOrigin-RevId: 209923852
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/test/TestActionBuilder.java b/src/main/java/com/google/devtools/build/lib/analysis/test/TestActionBuilder.java
index da28ff2..5a20f52 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/test/TestActionBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/test/TestActionBuilder.java
@@ -53,6 +53,9 @@
  */
 public final class TestActionBuilder {
 
+  private static final String CC_CODE_COVERAGE_SCRIPT = "CC_CODE_COVERAGE_SCRIPT";
+  private static final String LCOV_MERGER = "LCOV_MERGER";
+
   private final RuleContext ruleContext;
   private RunfilesSupport runfilesSupport;
   private Artifact executable;
@@ -242,13 +245,21 @@
       inputsBuilder.addTransitive(metadataFiles);
       inputsBuilder.addTransitive(
           PrerequisiteArtifacts.nestedSet(ruleContext, ":coverage_support", Mode.DONT_CHECK));
+
+      if (ruleContext.isAttrDefined("$collect_cc_coverage", LABEL)) {
+        Artifact collectCcCoverage =
+            ruleContext.getHostPrerequisiteArtifact("$collect_cc_coverage");
+        inputsBuilder.add(collectCcCoverage);
+        extraTestEnv.put(CC_CODE_COVERAGE_SCRIPT, collectCcCoverage.getExecPathString());
+      }
+
       // We don't add this attribute to non-supported test target
       if (ruleContext.isAttrDefined("$lcov_merger", LABEL)) {
         TransitiveInfoCollection lcovMerger =
             ruleContext.getPrerequisite("$lcov_merger", Mode.TARGET);
         FilesToRunProvider lcovFilesToRun = lcovMerger.getProvider(FilesToRunProvider.class);
         if (lcovFilesToRun != null) {
-          extraTestEnv.put("LCOV_MERGER", lcovFilesToRun.getExecutable().getExecPathString());
+          extraTestEnv.put(LCOV_MERGER, lcovFilesToRun.getExecutable().getExecPathString());
           inputsBuilder.addTransitive(lcovFilesToRun.getFilesToRun());
         } else {
           NestedSet<Artifact> filesToBuild =
@@ -256,7 +267,7 @@
 
           if (Iterables.size(filesToBuild) == 1) {
             Artifact lcovMergerArtifact = Iterables.getOnlyElement(filesToBuild);
-            extraTestEnv.put("LCOV_MERGER", lcovMergerArtifact.getExecPathString());
+            extraTestEnv.put(LCOV_MERGER, lcovMergerArtifact.getExecPathString());
             inputsBuilder.add(lcovMergerArtifact);
           } else {
             ruleContext.attributeError("$lcov_merger",
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCcTestRule.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCcTestRule.java
index 1b277cb..e919db1 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCcTestRule.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCcTestRule.java
@@ -15,12 +15,14 @@
 package com.google.devtools.build.lib.bazel.rules.cpp;
 
 import static com.google.devtools.build.lib.packages.Attribute.attr;
+import static com.google.devtools.build.lib.packages.BuildType.LABEL;
 import static com.google.devtools.build.lib.packages.BuildType.TRISTATE;
 import static com.google.devtools.build.lib.syntax.Type.BOOLEAN;
 
 import com.google.devtools.build.lib.analysis.BaseRuleClasses;
 import com.google.devtools.build.lib.analysis.RuleDefinition;
 import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
+import com.google.devtools.build.lib.analysis.config.HostTransition;
 import com.google.devtools.build.lib.bazel.rules.cpp.BazelCppRuleClasses.CcBinaryBaseRule;
 import com.google.devtools.build.lib.packages.RuleClass;
 import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
@@ -42,6 +44,11 @@
         // to decorate data symbols imported from DLL.
         .override(attr("linkstatic", BOOLEAN).value(OS.getCurrent() == OS.WINDOWS))
         .override(attr("stamp", TRISTATE).value(TriState.NO))
+        .add(
+            attr("$collect_cc_coverage", LABEL)
+                .cfg(HostTransition.INSTANCE)
+                .singleArtifact()
+                .value(env.getToolsLabel("//tools/test:collect_cc_coverage")))
         .build();
   }
 
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java b/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java
index 614a355..5a72851 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java
@@ -131,6 +131,7 @@
         "filegroup(name = 'test_setup', srcs = ['test-setup.sh'])",
         "filegroup(name = 'test_xml_generator', srcs = ['test-xml-generator.sh'])",
         "filegroup(name = 'collect_coverage', srcs = ['collect_coverage.sh'])",
+        "filegroup(name = 'collect_cc_coverage', srcs = ['collect_cc_coverage.sh'])",
         "filegroup(name='coverage_support', srcs=['collect_coverage.sh'])",
         "filegroup(name = 'coverage_report_generator', srcs = ['coverage_report_generator.sh'])");
 
diff --git a/src/test/shell/bazel/BUILD b/src/test/shell/bazel/BUILD
index c2f1957..671a9c2 100644
--- a/src/test/shell/bazel/BUILD
+++ b/src/test/shell/bazel/BUILD
@@ -196,6 +196,15 @@
 )
 
 sh_test(
+    name = "bazel_cc_code_coverage_test",
+    srcs = ["bazel_cc_code_coverage_test.sh"],
+    data = [":test-deps"],
+    tags = [
+        "no_windows",
+    ],
+)
+
+sh_test(
     name = "bazel_localtest_test",
     srcs = ["bazel_localtest_test.sh"],
     data = [":test-deps"],
diff --git a/src/test/shell/bazel/bazel_cc_code_coverage_test.sh b/src/test/shell/bazel/bazel_cc_code_coverage_test.sh
new file mode 100755
index 0000000..10d8cc8
--- /dev/null
+++ b/src/test/shell/bazel/bazel_cc_code_coverage_test.sh
@@ -0,0 +1,188 @@
+#!/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"
diff --git a/tools/test/BUILD b/tools/test/BUILD
index 4d5a3ab..7f7e831 100644
--- a/tools/test/BUILD
+++ b/tools/test/BUILD
@@ -29,6 +29,11 @@
 )
 
 filegroup(
+    name = "collect_cc_coverage",
+    srcs = ["collect_cc_coverage.sh"],
+)
+
+filegroup(
     name = "coverage_report_generator",
     srcs = ["@bazel_tools//tools/test/LcovMerger/java/com/google/devtools/lcovmerger:Main"],
 )
@@ -54,6 +59,7 @@
         "test-setup.sh",
         "generate-xml.sh",
         "collect_coverage.sh",
+        "collect_cc_coverage.sh",
     ] + glob(["LcovMerger/**"]) + select({
         "@bazel_tools//src/conditions:windows": ["test_wrapper_bin"],
         "//conditions:default": [],
diff --git a/tools/test/BUILD.tools b/tools/test/BUILD.tools
index c41e293..003431b 100644
--- a/tools/test/BUILD.tools
+++ b/tools/test/BUILD.tools
@@ -24,6 +24,11 @@
 )
 
 filegroup(
+    name = "collect_cc_coverage",
+    srcs = ["collect_cc_coverage.sh"],
+)
+
+filegroup(
     name = "coverage_support",
     srcs = ["collect_coverage.sh"],
 )
diff --git a/tools/test/collect_cc_coverage.sh b/tools/test/collect_cc_coverage.sh
new file mode 100755
index 0000000..c0d6b36
--- /dev/null
+++ b/tools/test/collect_cc_coverage.sh
@@ -0,0 +1,100 @@
+#!/bin/bash -x
+ # 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.
+
+# This script collects code coverage data for C++ sources, after the tests
+# were executed.
+#
+# Bazel C++ code coverage collection support is poor and limited. There is
+# an ongoing effort to improve this (tracking issue #1118).
+#
+# Bazel uses the lcov tool for gathering coverage data. There is also
+# an experimental support for clang llvm coverage, which uses the .profraw
+# data files to compute the coverage report.
+#
+# This script assumes the following environment variables are set:
+# - COVERAGE_DIR            Directory containing metadata files needed for
+#                           coverage collection (e.g. gcda files, profraw).
+# - COVERAGE_MANIFEST       Location of the instrumented file manifest.
+# - COVERAGE_OUTPUT_FILE    Location of the final coverage report.
+# - COVERAGE_GCOV_PATH      Location of gcov. This is set by the TestRunner.
+# - ROOT                    Location from where the code coverage collection
+#                           was invoked.
+#
+# The script looks in $COVERAGE_DIR for the C++ metadata coverage files (either
+# gcda or profraw) and uses either lcov or gcov to get the coverage data.
+# The coverage data is placed in $COVERAGE_OUTPUT_FILE.
+
+# Checks if clang llvm coverage should be used instead of lcov.
+function uses_llvm() {
+  if stat "${COVERAGE_DIR}"/*.profraw >/dev/null 2>&1; then
+    return 0
+  fi
+  return 1
+}
+
+function init_gcov() {
+  # Symlink the gcov tool such with a link called gcov. Clang comes with a tool
+  # called llvm-cov, which behaves like gcov if symlinked in this way (otherwise
+  # we would need to invoke it with "llvm-cov gcov").
+  # For more details see https://llvm.org/docs/CommandGuide/llvm-cov.html.
+  GCOV="${COVERAGE_DIR}/gcov"
+  ln -s "${COVERAGE_GCOV_PATH}" "${GCOV}"
+}
+
+# Computes code coverage data using the clang generated metadata found under $COVERAGE_DIR.
+# Writes the collected coverage into ${COVERAGE_OUTPUT_FILE}.
+function llvm_coverage() {
+  export LLVM_PROFILE_FILE="${COVERAGE_DIR}/%h-%p-%m.profraw"
+  "${COVERAGE_GCOV_PATH}" merge -output "${COVERAGE_OUTPUT_FILE}" "${COVERAGE_DIR}"/*.profraw
+}
+
+# Computes code coverage data using gcda files found under $COVERAGE_DIR.
+# Writes the collected coverage into ${COVERAGE_OUTPUT_FILE} in lcov format.
+function lcov_coverage() {
+  cat "${COVERAGE_MANIFEST}" | grep ".gcno$" | while read gcno; do
+    mkdir -p "${COVERAGE_DIR}/$(dirname ${gcno})"
+    cp "${ROOT}/${gcno}" "${COVERAGE_DIR}/${gcno}"
+  done
+  # Run lcov over the .gcno and .gcda files to generate the lcov tracefile.
+  # -c                    - Collect coverage data
+  # --no-external         - Do not collect coverage data for system files
+  # --ignore-errors graph - Ignore missing .gcno files; Bazel only instruments some files
+  # -q                    - Quiet mode
+  # --gcov-tool "${GCOV}" - Pass the local symlink to be uses as gcov by lcov
+  # -b /proc/self/cwd     - Use this as a prefix for all source files instead of
+  #                         the current directory
+  # -d "${COVERAGE_DIR}"  - Directory to search for .gcda files
+  # -o "${COVERAGE_OUTPUT_FILE}" - Output file
+  LCOV=$(which lcov)
+  if [[ ! -x $LCOV ]]; then
+    LCOV=/usr/bin/lcov
+  fi
+  $LCOV -c --no-external --ignore-errors graph -q \
+      --gcov-tool "${GCOV}" -b /proc/self/cwd \
+      -d "${COVERAGE_DIR}" -o "${COVERAGE_OUTPUT_FILE}"
+   # Fix up the paths to be relative by removing the prefix we specified above.
+  sed -i -e "s*/proc/self/cwd/**g" "${COVERAGE_OUTPUT_FILE}"
+}
+
+function main() {
+  init_gcov
+  if uses_llvm; then
+    llvm_coverage
+  else
+    lcov_coverage
+  fi
+}
+
+main
\ No newline at end of file
diff --git a/tools/test/collect_coverage.sh b/tools/test/collect_coverage.sh
index 36e17bb..b40ec9a 100755
--- a/tools/test/collect_coverage.sh
+++ b/tools/test/collect_coverage.sh
@@ -44,7 +44,7 @@
 
 # When collect_coverage.sh is used, test runner must be instructed not to cd
 # to the test's runfiles directory.
-ROOT="$PWD"
+export ROOT="$PWD"
 
 if [[ "$COVERAGE_MANIFEST" != /* ]]; then
   # Canonicalize the path to coverage manifest so that tests can find it.
@@ -91,11 +91,13 @@
   export LLVM_PROFILE_FILE="${COVERAGE_DIR}/%h-%p-%m.profraw"
 fi
 
+# TODO(iirina): cd should be avoided.
 cd "$TEST_SRCDIR/$TEST_WORKSPACE"
+# Execute the test.
 "$@"
 TEST_STATUS=$?
 
-# always create output files
+# Always create the coverage report.
 touch $COVERAGE_OUTPUT_FILE
 
 if [[ $TEST_STATUS -ne 0 ]]; then
@@ -106,52 +108,12 @@
   exit $TEST_STATUS
 fi
 
+# TODO(iirina): cd should be avoided.
 cd $ROOT
 
-USES_LLVM_COV=
-if stat "${COVERAGE_DIR}"/*.profraw >/dev/null 2>&1; then
-  USES_LLVM_COV=1
-fi
-
-if [[ "$USES_LLVM_COV" ]]; then
-  "${COVERAGE_GCOV_PATH}" merge -output "${COVERAGE_OUTPUT_FILE}" "${COVERAGE_DIR}"/*.profraw
-  exit $TEST_STATUS
-
-# If LCOV_MERGER is not set, use the legacy C++-only method to convert coverage files.
-elif [[ "$COVERAGE_LEGACY_MODE" ]]; then
-  cat "${COVERAGE_MANIFEST}" | grep ".gcno$" | while read path; do
-    mkdir -p "${COVERAGE_DIR}/$(dirname ${path})"
-    cp "${ROOT}/${path}" "${COVERAGE_DIR}/${path}"
-  done
-
-  # Symlink the gcov tool such with a link called gcov. Clang comes with a tool
-  # called llvm-cov, which behaves like gcov if symlinked in this way (otherwise
-  # we would need to invoke it with "llvm-cov gcov").
-  GCOV="${COVERAGE_DIR}/gcov"
-  ln -s "${COVERAGE_GCOV_PATH}" "${GCOV}"
-
-  # Run lcov over the .gcno and .gcda files to generate the lcov tracefile.
-  # -c                    - Collect coverage data
-  # --no-external         - Do not collect coverage data for system files
-  # --ignore-errors graph - Ignore missing .gcno files; Bazel only instruments some files
-  # -q                    - Quiet mode
-  # --gcov-tool "${GCOV}" - Pass the local symlink to be uses as gcov by lcov
-  # -b /proc/self/cwd     - Use this as a prefix for all source files instead of
-  #                         the current directory
-  # -d "${COVERAGE_DIR}"  - Directory to search for .gcda files
-  # -o "${COVERAGE_OUTPUT_FILE}" - Output file
-  LCOV=$(which lcov)
-  if [[ ! -x $LCOV ]]; then
-    LCOV=/usr/bin/lcov
-  fi
-  $LCOV -c --no-external --ignore-errors graph -q \
-      --gcov-tool "${GCOV}" -b /proc/self/cwd \
-      -d "${COVERAGE_DIR}" -o "${COVERAGE_OUTPUT_FILE}"
-
-  # Fix up the paths to be relative by removing the prefix we specified above.
-  sed -i -e "s*/proc/self/cwd/**g" "${COVERAGE_OUTPUT_FILE}"
-
-  exit $TEST_STATUS
+if [[ "$CC_CODE_COVERAGE_SCRIPT" ]]; then
+    eval "${CC_CODE_COVERAGE_SCRIPT}"
+    exit $TEST_STATUS
 fi
 
 export LCOV_MERGER_CMD="${LCOV_MERGER} --coverage_dir=${COVERAGE_DIR} \