You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iggy.apache.org by hg...@apache.org on 2026/10/05 13:45:43 UTC
(iggy) branch master updated: feat(cpp): add code coverage (#4118)
This is an automated email from the ASF dual-hosted git repository.
hubcio pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iggy.git
The following commit(s) were added to refs/heads/master by this push:
new a2895fdc6 feat(cpp): add code coverage (#4118)
a2895fdc6 is described below
commit a2895fdc6ec01a1570a7aecaf82b0c950fe843d5
Author: Rimuksh Kansal <ni...@gmail.com>
AuthorDate: Mon Oct 5 22:45:38 2026 +0900
feat(cpp): add code coverage (#4118)
---
.github/actions/cpp-bazel/pre-merge/action.yml | 16 ++++-
.github/workflows/_test.yml | 12 ++++
.github/workflows/coverage-baseline.yml | 28 ++++++++-
.github/workflows/pre-merge.yml | 2 +
codecov.yml | 7 +++
foreign/cpp/BUILD.bazel | 50 ++++++++++++++-
foreign/cpp/MODULE.bazel | 17 ++++++
foreign/cpp/MODULE.bazel.lock | 2 +-
scripts/ci/coverage-baseline-affected.sh | 3 +-
scripts/ci/cpp-coverage.sh | 84 ++++++++++++++++++++++++++
scripts/ci/sync-rustc-version.sh | 21 +++++++
11 files changed, 233 insertions(+), 9 deletions(-)
diff --git a/.github/actions/cpp-bazel/pre-merge/action.yml b/.github/actions/cpp-bazel/pre-merge/action.yml
index 0cd50e62f..b21f3eb2e 100644
--- a/.github/actions/cpp-bazel/pre-merge/action.yml
+++ b/.github/actions/cpp-bazel/pre-merge/action.yml
@@ -63,7 +63,7 @@ runs:
shell: bash
run: |
cd foreign/cpp
- cargo clippy --manifest-path Cargo.toml --all-targets --all-features -- -D warnings
+ cargo clippy --all-targets --all-features -- -D warnings
- name: Build
if: inputs.task == 'build'
@@ -72,22 +72,32 @@ runs:
cd foreign/cpp
bazel build --config=ci //:iggy-cpp
+ - name: Install cargo-llvm-cov
+ if: inputs.task == 'test'
+ uses: taiki-e/install-action@v2.87.18
+ with:
+ tool: cargo-llvm-cov
+
- name: Setup server for tests
+ id: iggy
if: inputs.task == 'test'
uses: ./.github/actions/utils/server-start
with:
cargo-bin: iggy-server
- - name: Test
+ - name: Test with coverage
if: inputs.task == 'test'
shell: bash
run: |
cd foreign/cpp
- bazel test --config=ci //...
+ ../../scripts/ci/cpp-coverage.sh
- name: Stop server after tests
if: always() && inputs.task == 'test'
uses: ./.github/actions/utils/server-stop
+ with:
+ pid-file: ${{ steps.iggy.outputs.pid_file }}
+ log-file: ${{ steps.iggy.outputs.log_file }}
- name: Validate task
if: inputs.task != 'lint' && inputs.task != 'build' && inputs.task != 'test'
diff --git a/.github/workflows/_test.yml b/.github/workflows/_test.yml
index 1c7d0c4bb..0b2ecf77e 100644
--- a/.github/workflows/_test.yml
+++ b/.github/workflows/_test.yml
@@ -241,6 +241,18 @@ jobs:
with:
task: ${{ inputs.task }}
+ - name: Upload C++ coverage to Codecov
+ if: inputs.component == 'sdk-cpp' && inputs.task == 'test'
+ uses: codecov/codecov-action@v7.0.0
+ with:
+ token: ${{ secrets.CODECOV_TOKEN }}
+ files: reports/cpp-coverage.lcov
+ disable_search: true
+ flags: cpp
+ fail_ci_if_error: false
+ verbose: true
+ override_pr: ${{ github.event.pull_request.number }}
+
- name: Upload Java coverage to Codecov
if: inputs.component == 'sdk-java' && inputs.task == 'test'
uses: codecov/codecov-action@v7.1.1
diff --git a/.github/workflows/coverage-baseline.yml b/.github/workflows/coverage-baseline.yml
index d57606337..a07b71acb 100644
--- a/.github/workflows/coverage-baseline.yml
+++ b/.github/workflows/coverage-baseline.yml
@@ -15,8 +15,8 @@
# specific language governing permissions and limitations
# under the License.
-# Coverage baseline for all 7 languages (Rust, Java, C#, Python, PHP,
-# Node, Go). Runs on push to master so Codecov has data on master SHAs for
+# Coverage baseline for all 8 languages (Rust, Java, C#, Python, PHP,
+# Node, Go, C++). Runs on push to master so Codecov has data on master SHAs for
# carryforward on PR builds where only a subset of SDKs is tested.
#
# Jobs are path-gated: a language job runs only when the paths feeding its
@@ -61,6 +61,7 @@ jobs:
php: ${{ steps.affected.outputs.php }}
node: ${{ steps.affected.outputs.node }}
go: ${{ steps.affected.outputs.go }}
+ cpp: ${{ steps.affected.outputs.cpp }}
steps:
- uses: actions/checkout@v7.0.1
with:
@@ -581,6 +582,29 @@ jobs:
flags: go
fail_ci_if_error: false
+ cpp-coverage:
+ name: C++ coverage baseline
+ needs: detect
+ if: needs.detect.outputs.cpp == 'true'
+ runs-on: ubuntu-latest
+ timeout-minutes: 45
+ steps:
+ - uses: actions/checkout@v7.0.1
+
+ - name: Run C++ SDK tests with coverage
+ uses: ./.github/actions/cpp-bazel/pre-merge
+ with:
+ task: test
+
+ - name: Upload to Codecov
+ uses: codecov/codecov-action@v7.0.0
+ with:
+ token: ${{ secrets.CODECOV_TOKEN }}
+ files: reports/cpp-coverage.lcov
+ disable_search: true
+ flags: cpp
+ fail_ci_if_error: false
+
warm-cache-macos:
name: Warm macOS cache
needs: detect
diff --git a/.github/workflows/pre-merge.yml b/.github/workflows/pre-merge.yml
index 4fa8c7f98..cf35325a0 100644
--- a/.github/workflows/pre-merge.yml
+++ b/.github/workflows/pre-merge.yml
@@ -167,6 +167,8 @@ jobs:
with:
component: ${{ matrix.component }}
task: ${{ matrix.task }}
+ secrets:
+ CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
# Swift SDK
test-swift:
diff --git a/codecov.yml b/codecov.yml
index cc1533be8..2d927b241 100644
--- a/codecov.yml
+++ b/codecov.yml
@@ -76,6 +76,9 @@ flag_management:
- name: go
paths:
- foreign/go/
+ - name: cpp
+ paths:
+ - foreign/cpp/
component_management:
individual_components:
@@ -107,6 +110,10 @@ component_management:
name: "Go SDK"
paths:
- foreign/go/
+ - component_id: cpp
+ name: "C++ SDK"
+ paths:
+ - foreign/cpp/
comment:
layout: "header, diff, components, files"
diff --git a/foreign/cpp/BUILD.bazel b/foreign/cpp/BUILD.bazel
index 1d3d2e8d5..c5d655ba1 100644
--- a/foreign/cpp/BUILD.bazel
+++ b/foreign/cpp/BUILD.bazel
@@ -17,11 +17,17 @@
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_import", "cc_library", "cc_test")
+config_setting(
+ name = "coverage_enabled",
+ values = {"define": "iggy_cpp_coverage=1"},
+)
+
genrule(
name = "cargo_build",
srcs = glob(["src/**/*.rs"]) + [
"Cargo.toml",
"build.rs",
+ "@iggy_source//:cpp_rust_dependencies",
],
outs = [
"libiggy_cpp.a",
@@ -55,7 +61,7 @@ genrule(
env -u PWD \
CARGO_TARGET_DIR="$$CARGO_TARGET_DIR" \
RUSTC="$$RUSTC" \
- "$$CARGO" build $$FLAGS
+ "$$CARGO" build --quiet $$FLAGS
cp "$$CARGO_TARGET_DIR/$$PROFILE/libiggy_cpp.a" "$$OUT_LIB"
@@ -88,11 +94,48 @@ genrule(
],
)
+genrule(
+ name = "extract_profiler_runtime",
+ outs = ["libprofiler_builtins.a"],
+ cmd = """
+ set -euo pipefail
+
+ EXECROOT="$$(pwd)"
+ RUSTC="$$EXECROOT/$(execpath @rs_host_tools//:rustc)"
+ PROFILER_RT_OUT="$$EXECROOT/$(location libprofiler_builtins.a)"
+
+ RT_SYSROOT="$$("$$RUSTC" --print sysroot)"
+ RT_HOST="$$("$$RUSTC" -vV | sed -n 's/^host: //p')"
+ RT_RLIB="$$(find "$$RT_SYSROOT/lib/rustlib/$$RT_HOST/lib" -maxdepth 1 -name 'libprofiler_builtins-*.rlib' -print -quit)"
+ if [ -z "$$RT_RLIB" ]; then
+ echo "ERROR: Failed to locate libprofiler_builtins rlib under $$RT_SYSROOT" >&2
+ exit 1
+ fi
+ cp "$$RT_RLIB" "$$PROFILER_RT_OUT"
+ for member in $$(ar t "$$PROFILER_RT_OUT"); do
+ case "$$member" in
+ *.o) ;;
+ *) ar d "$$PROFILER_RT_OUT" "$$member" ;;
+ esac
+ done
+ """,
+ tools = [
+ "@rs_host_tools//:rust_toolchain",
+ "@rs_host_tools//:rustc",
+ ],
+)
+
cc_import(
name = "iggy_cpp_static",
static_library = ":libiggy_cpp.a",
)
+cc_import(
+ name = "profiler_rt",
+ static_library = ":libprofiler_builtins.a",
+ alwayslink = True,
+)
+
cc_library(
name = "iggy-cpp",
srcs = glob(
@@ -140,7 +183,10 @@ cc_library(
deps = [
":iggy_cpp_static",
"@abseil-cpp//absl/numeric:int128",
- ],
+ ] + select({
+ ":coverage_enabled": [":profiler_rt"],
+ "//conditions:default": [],
+ }),
)
cc_binary(
diff --git a/foreign/cpp/MODULE.bazel b/foreign/cpp/MODULE.bazel
index 0badaf0c6..5b05e43f4 100644
--- a/foreign/cpp/MODULE.bazel
+++ b/foreign/cpp/MODULE.bazel
@@ -27,6 +27,23 @@ bazel_dep(name = "cucumber-cpp", version = "0.8.0.bcr.1")
bazel_dep(name = "rules_rust", version = "0.74.0")
bazel_dep(name = "abseil-cpp", version = "20260526.0")
+iggy_source = use_repo_rule("@bazel_tools//tools/build_defs/repo:local.bzl", "new_local_repository")
+iggy_source(
+ name = "iggy_source",
+ build_file_content = """
+filegroup(
+ name = "cpp_rust_dependencies",
+ srcs = glob([
+ "core/binary_protocol/**",
+ "core/common/**",
+ "core/sdk/**",
+ ]) + ["Cargo.toml"],
+ visibility = ["//visibility:public"],
+)
+""",
+ path = "../..",
+)
+
rust_host_tools = use_extension("@rules_rust//rust:extensions.bzl", "rust_host_tools")
rust_host_tools.host_tools(
name = "rs_host_tools",
diff --git a/foreign/cpp/MODULE.bazel.lock b/foreign/cpp/MODULE.bazel.lock
index 840faa16d..7743e6ffc 100644
--- a/foreign/cpp/MODULE.bazel.lock
+++ b/foreign/cpp/MODULE.bazel.lock
@@ -489,7 +489,7 @@
},
"@@rules_rust+//crate_universe/private:internal_extensions.bzl%cu_nr": {
"general": {
- "bzlTransitiveDigest": "qQd7ktlamEwHDmeHLMSsEyM7+9Y2dOsIhs3Y/RXmCDY=",
+ "bzlTransitiveDigest": "0Uuil/+kAYdA1LXnr4MYuGZOREPtv/5CjQwREuGjTm4=",
"usagesDigest": "McimNXAztVHDzspa1Kng94T+YeyY/44zx9FkNqIMn2s=",
"recordedInputs": [
"REPO_MAPPING:apple_support+,bazel_skylib bazel_skylib+",
diff --git a/scripts/ci/coverage-baseline-affected.sh b/scripts/ci/coverage-baseline-affected.sh
index ef73d043d..aa1187d8f 100755
--- a/scripts/ci/coverage-baseline-affected.sh
+++ b/scripts/ci/coverage-baseline-affected.sh
@@ -33,7 +33,7 @@ set -euo pipefail
BASE="${1:-}"
HEAD="${2:-}"
ZERO="0000000000000000000000000000000000000000"
-GATES=(rust java csharp python php node go)
+GATES=(rust java csharp python php node go cpp)
emit_all() {
local gate
@@ -83,6 +83,7 @@ for gate in "${GATES[@]}"; do
node) paths=(foreign/node) ;;
# The go job also runs the bdd/go suite with foreign/go in -coverpkg.
go) paths=(foreign/go bdd/go) ;;
+ cpp) paths=(foreign/cpp) ;;
*)
echo "coverage-gate: no pathspecs defined for gate '$gate'" >&2
exit 1
diff --git a/scripts/ci/cpp-coverage.sh b/scripts/ci/cpp-coverage.sh
new file mode 100755
index 000000000..bb5525c12
--- /dev/null
+++ b/scripts/ci/cpp-coverage.sh
@@ -0,0 +1,84 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
+CPP_ROOT="$REPO_ROOT/foreign/cpp"
+
+cd "$CPP_ROOT"
+
+OUTPUT="$REPO_ROOT/reports/cpp-coverage.lcov"
+COVERAGE_DIR="$CPP_ROOT/target/cpp-coverage"
+# Cargo build scripts also inherit coverage instrumentation. Keep their profiles
+# separate because llvm-cov exports only the unit and e2e test objects below.
+BUILD_PROFRAW_DIR="$COVERAGE_DIR/build"
+PROFDATA="$COVERAGE_DIR/shim.profdata"
+CPP_RAW="$CPP_ROOT/bazel-out/_coverage/_coverage_report.dat"
+
+mkdir -p "$COVERAGE_DIR" "$BUILD_PROFRAW_DIR" "$(dirname "$OUTPUT")"
+rm -f "$COVERAGE_DIR"/*.profraw "$BUILD_PROFRAW_DIR"/*.profraw "$PROFDATA" "$OUTPUT"
+
+if ! LLVM_COV_ENV="$(cargo llvm-cov show-env --no-rustc-wrapper --sh)"; then
+ echo "cpp-coverage: 'cargo llvm-cov show-env' failed" >&2
+ exit 1
+fi
+eval "$LLVM_COV_ENV"
+export LLVM_PROFILE_FILE="$COVERAGE_DIR/%p-%m.profraw"
+
+LLVM_BIN="$(rustc --print target-libdir)/../bin"
+LLVM_COV="$LLVM_BIN/llvm-cov"
+LLVM_PROFDATA="$LLVM_BIN/llvm-profdata"
+
+# --run_under overrides Bazel's profile path so Rust profiles do not replace GCC LCOV data.
+bazel coverage \
+ --config=debug \
+ --lockfile_mode=error \
+ --combined_report=lcov \
+ '--instrumentation_filter=//:iggy-cpp' \
+ --strategy=TestRunner=standalone \
+ --define=iggy_cpp_coverage=1 \
+ --action_env=RUSTFLAGS \
+ "--action_env=LLVM_PROFILE_FILE=$BUILD_PROFRAW_DIR/%p-%m.profraw" \
+ "--run_under=/usr/bin/env LLVM_PROFILE_FILE=$LLVM_PROFILE_FILE" \
+ //:unit //:e2e
+
+"$LLVM_PROFDATA" merge -sparse \
+ "$COVERAGE_DIR"/*.profraw \
+ -o "$PROFDATA"
+
+sed 's|^SF:|SF:foreign/cpp/|' "$CPP_RAW" >"$OUTPUT"
+
+"$LLVM_COV" export \
+ --object bazel-bin/unit \
+ --object bazel-bin/e2e \
+ -instr-profile="$PROFDATA" \
+ -format=lcov \
+ -ignore-filename-regex='(\.cargo/|/rustc/|/core/|cxxbridge|/registry/)' |
+ awk '
+ /^SF:/ {
+ include = /foreign\/cpp\/src\/[^/]+\.rs$/
+ if (include) {
+ sub(/^SF:.*foreign\/cpp\//, "SF:foreign/cpp/")
+ }
+ }
+ include
+ ' >>"$OUTPUT"
+
+"$SCRIPT_DIR/validate-lcov.sh" "$OUTPUT"
diff --git a/scripts/ci/sync-rustc-version.sh b/scripts/ci/sync-rustc-version.sh
index 8cf0709a4..fc7bca5f3 100755
--- a/scripts/ci/sync-rustc-version.sh
+++ b/scripts/ci/sync-rustc-version.sh
@@ -235,6 +235,27 @@ check_devcontainer_json() {
check_devcontainer_json
+# Bazel's Rust shim profiles must match the LLVM tools from rust-toolchain.toml.
+CPP_BAZEL_MODULE="foreign/cpp/MODULE.bazel"
+BAZEL_RUST_VERSION=$(sed -n '/rust_host_tools\.host_tools(/,/^)/ s/^[[:space:]]*version = "\([^"]*\)",/\1/p' "$CPP_BAZEL_MODULE")
+if [ -z "$BAZEL_RUST_VERSION" ]; then
+ echo -e "${RED}Error: Could not extract host_tools version from $CPP_BAZEL_MODULE${NC}"
+ exit 1
+fi
+
+TOTAL_FILES=$((TOTAL_FILES + 1))
+if [ "$BAZEL_RUST_VERSION" = "$RUST_VERSION" ]; then
+ echo -e "${GREEN}✓${NC} $CPP_BAZEL_MODULE: host_tools version ${GREEN}$RUST_VERSION${NC}"
+elif [ "$MODE" = "check" ]; then
+ MISALIGNED_FILES+=("$CPP_BAZEL_MODULE")
+ echo -e "${RED}✗${NC} $CPP_BAZEL_MODULE: host_tools version ${RED}$BAZEL_RUST_VERSION${NC} (expected: ${GREEN}$RUST_VERSION${NC})"
+else
+ sed -i.bak -E "/rust_host_tools\.host_tools\(/,/^\)/ s/(version = \")[^\"]+(\",)/\1$RUST_VERSION\2/" "$CPP_BAZEL_MODULE"
+ rm -f "$CPP_BAZEL_MODULE.bak"
+ FIXED_FILES=$((FIXED_FILES + 1))
+ echo -e "${GREEN}Fixed${NC} $CPP_BAZEL_MODULE: host_tools version ${RED}$BAZEL_RUST_VERSION${NC} -> ${GREEN}$RUST_VERSION${NC}"
+fi
+
echo ""
echo "────────────────────────────────────────────────"