Skip to content

Commit 32b7833

Browse files
authored
fix(ci): run coverage report from package directory to fix path matching (#17580)
Fixes coverage evaluation issues in the monorepo by isolating package coverage reports and ensuring correct path matching for `omit` rules in local `.coveragerc` files. ## Issue Details In the CI/CD pipeline (`unittest.yml`), coverage reports were being generated pointing to the aggregated `.coverage` file in the root. This caused two main problems: 1. **Pollution**: `coverage report` without `--include` reported on **ALL** packages present in the aggregated file. Smaller packages with lower coverage (like `google-cloud-spanner-dbapi-driver`) were artificially dragged up by larger packages with high coverage (like `google-cloud-logging`), hiding coverage deficits. 2. **Path Mismatch**: The aggregated file refers to absolute paths (e.g., `/home/runner/...`), while `omit` rules in package `.coveragerc` files are typically relative paths (e.g., `google/cloud/_testing/__init__.py`). This mismatch prevented files from being omitted correctly thus falsely inflating coverage deficits. ## Solution 1. **Isolate and Fix Paths**: Added `--include="$PWD/**"` to the `coverage report` command in `unittest.yml`. Combined with `pushd "${pkg}"`, this ensures that: - The report is isolated to files within the specific package directory. - Relative `omit` paths in `.coveragerc` match correctly against the files being evaluated. 2. **Expose and Lock Coverage**: This fix exposed a genuine coverage deficit in `google-cloud-spanner-dbapi-driver` (89%). It has never had full coverage (and updating that coverage is outside the scope of this PR). We updated its `.coveragerc` with `fail_under = 89` to prevent regressions while acknowledging known missing coverage. 3. **Limited system test capability in spanner-dbapi-driver lib**: the spanner-dbapi-driver package is in the planning phase so the full system test suite is not functional. We added a `pytest.skip` to prevent system test failures in CI/CD. > [!NOTE] > To ensure the correctness of the fix, we used dummy comments in version files to trigger unittests and cover, but we have removed them to avoid cluttering the PR. The passing results [can be seen here](https://github.com/googleapis/google-cloud-python/actions/runs/28232303292/job/83639068672)
1 parent 62b0752 commit 32b7833

3 files changed

Lines changed: 28 additions & 10 deletions

File tree

.github/workflows/unittest.yml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -117,21 +117,23 @@ jobs:
117117
echo "============================================================"
118118
119119
set +e
120-
if [ -f "${pkg}/.coveragerc" ]; then
120+
pushd "${pkg}" > /dev/null
121+
if [ -f ".coveragerc" ]; then
121122
echo "Using package-specific configuration: ${pkg}/.coveragerc"
122123
# If fail_under is specified in the package-specific .coveragerc, coverage report
123124
# will automatically enforce it. Otherwise, we enforce the default.
124-
if grep -q "fail_under" "${pkg}/.coveragerc"; then
125-
coverage report --rcfile="${pkg}/.coveragerc" --include="${pkg}/**"
125+
if grep -q "fail_under" ".coveragerc"; then
126+
COVERAGE_FILE=../../.coverage coverage report --include="$PWD/**"
126127
else
127128
echo "No fail_under specified in ${pkg}/.coveragerc, enforcing default"
128-
coverage report --rcfile="${pkg}/.coveragerc" --include="${pkg}/**" --fail-under="${DEFAULT_FAIL_UNDER}"
129+
COVERAGE_FILE=../../.coverage coverage report --include="$PWD/**" --fail-under="${DEFAULT_FAIL_UNDER}"
129130
fi
130131
else
131132
echo "No .coveragerc found for ${pkg}, enforcing default"
132-
coverage report --include="${pkg}/**" --fail-under="${DEFAULT_FAIL_UNDER}"
133+
COVERAGE_FILE=../../.coverage coverage report --include="$PWD/**" --fail-under="${DEFAULT_FAIL_UNDER}"
133134
fi
134135
status=$?
136+
popd > /dev/null
135137
set -e
136138
137139
if [ ${status} -ne 0 ]; then

packages/google-cloud-spanner-dbapi-driver/.coveragerc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ branch = True
33

44
[report]
55
show_missing = True
6+
# The spanner library has not had 100% coverage in the past and until such a time
7+
# as the unittest coverage is fixed, we need to set it at an appropriate level
8+
# to detect regressions in coverage but not fail due to known lack of coverage.
9+
fail_under = 89
610
omit =
711
google/cloud/spanner_driver/__init__.py
812
exclude_lines =

packages/google-cloud-spanner-dbapi-driver/tests/system/_helper.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,31 @@
1414
"""Helper functions for system tests."""
1515

1616
import os
17+
import pytest
1718

1819
SPANNER_EMULATOR_HOST = os.environ.get("SPANNER_EMULATOR_HOST")
1920
TEST_ON_PROD = not bool(SPANNER_EMULATOR_HOST)
2021

22+
# As of June 2026, this package is in 'Planning' status.
23+
# The CI/CD system does not yet have a SPANNER_EMULATOR_HOST active for this package,
24+
# so running tests on production is the default. This fallback and skip logic acts
25+
# as a safety measure until an emulator is available or production credentials are fully configured.
2126
if TEST_ON_PROD:
22-
PROJECT_ID = os.environ.get("SPANNER_PROJECT_ID")
23-
INSTANCE_ID = os.environ.get("SPANNER_INSTANCE_ID")
27+
PROJECT_ID = (
28+
os.environ.get("SPANNER_PROJECT_ID")
29+
or os.environ.get("GOOGLE_CLOUD_PROJECT")
30+
or os.environ.get("PROJECT_ID")
31+
)
32+
INSTANCE_ID = os.environ.get("SPANNER_INSTANCE_ID") or os.environ.get(
33+
"GOOGLE_CLOUD_TESTS_SPANNER_INSTANCE"
34+
)
2435
DATABASE_ID = os.environ.get("SPANNER_DATABASE_ID")
2536

2637
if not PROJECT_ID or not INSTANCE_ID or not DATABASE_ID:
27-
raise ValueError(
28-
"SPANNER_PROJECT_ID, SPANNER_INSTANCE_ID, and SPANNER_DATABASE_ID "
29-
"must be set when running tests on production."
38+
pytest.skip(
39+
"SPANNER_PROJECT_ID, SPANNER_INSTANCE_ID, and SPANNER_DATABASE_ID (or standard fallbacks) "
40+
"must be set when running tests on production.",
41+
allow_module_level=True,
3042
)
3143
else:
3244
PROJECT_ID = "test-project"

0 commit comments

Comments
 (0)