This is an automated email from the ASF dual-hosted git repository.

HyukjinKwon pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/spark-connect-rust.git


The following commit(s) were added to refs/heads/master by this push:
     new b1c2af9  [SPARK-59071][INFRA] Measure Rust coverage against a live 
Connect server
b1c2af9 is described below

commit b1c2af938e3fc09779d412593f5c157bb4f4e466
Author: Hyukjin Kwon <[email protected]>
AuthorDate: Fri Aug 28 15:07:24 2026 +0900

    [SPARK-59071][INFRA] Measure Rust coverage against a live Connect server
    
    ### What changes were proposed in this pull request?
    
    The Coverage workflow (`.github/workflows/coverage.yml`) ran `cargo 
llvm-cov -p apache-spark-connect` with **no Spark Connect server** and no 
`SPARK_REMOTE` set. The behavioral `e2e_*.rs` tests self-gate on `SPARK_REMOTE`:
    
    ```rust
    fn should_run() -> bool { std::env::var("SPARK_REMOTE").is_ok() }
    #[test] fn dataframe_transforms_and_setops() { if !should_run() { return; } 
... }
    ```
    
    So in the coverage job those tests **no-op**, and the runtime execution 
paths (collect/count/RPC-backed DataFrame, functions, session, catalog, ...) 
are never exercised. Only unit + golden (plan-serialization) tests run, so the 
Rust coverage badge reported **~73.3%**, well below the real coverage. Those 
same e2e tests already run in `build_python_connect.yml`, which downloads Spark 
4.2.0, starts a Connect server, and sets `SPARK_REMOTE`.
    
    This PR makes the coverage job start a Spark 4.2.0 Connect server 
(mirroring `build_python_connect.yml`) and measure coverage in two accumulated 
`cargo-llvm-cov` passes that are then combined:
    
    1. the default unit + golden tests, run in parallel;
    2. the behavioral e2e suite against the live server (`SPARK_REMOTE` set), 
run serially since the tests share one server session.
    
    `--no-report` accumulates raw coverage from each pass; `cargo llvm-cov 
report` merges both into one JSON summary consumed by the existing badge parser.
    
    Supporting changes: JDK 17 + the Python deps the server's workers need, a 
shared Spark-dist cache key (reused from the parity job), a 45-minute job 
timeout, and a server-log artifact for debugging. The two duplicate Python 
setup steps are consolidated into one.
    
    ### Why are the changes needed?
    
    So the Rust coverage badge reflects the coverage actually achieved when the 
runtime paths are exercised, instead of undercounting because the e2e tests 
silently no-op without a server.
    
    ### Does this PR introduce _any_ user-facing change?
    
    No. CI-only change.
    
    ### How was this patch tested?
    
    CI (the coverage workflow itself).
    
    ### Was this patch authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Code
    
    Closes #73 from HyukjinKwon/SPARK-59071.
    
    Authored-by: Hyukjin Kwon <[email protected]>
    Signed-off-by: Hyukjin Kwon <[email protected]>
---
 .github/workflows/coverage.yml | 106 ++++++++++++++++++++++++++++++++++++-----
 1 file changed, 93 insertions(+), 13 deletions(-)

diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml
index 83b327f..9140644 100644
--- a/.github/workflows/coverage.yml
+++ b/.github/workflows/coverage.yml
@@ -32,11 +32,15 @@ on:
 
 env:
   CARGO_TERM_COLOR: always
+  SPARK_VERSION: "4.2.0"
 
 jobs:
   coverage:
     name: Measure and publish coverage badges
     runs-on: ubuntu-latest
+    # Bound the job: coverage now runs the behavioral e2e suite against a live 
Spark
+    # Connect server, so a hung test fails the job instead of running to the 
6-hour default.
+    timeout-minutes: 45
     permissions:
       contents: write  # Needed to push to badges branch
     steps:
@@ -67,10 +71,89 @@ jobs:
       - name: Install cargo-llvm-cov
         uses: taiki-e/install-action@cargo-llvm-cov
 
+      - name: Set up JDK 17
+        uses: actions/setup-java@v4
+        with:
+          distribution: temurin
+          java-version: "17"
+
+      - name: Set up Python 3.11
+        uses: actions/setup-python@v5
+        with:
+          python-version: "3.11"
+          cache: pip
+
+      - name: Install Python dependencies
+        # Two sets: (1) build deps for the Python-coverage half 
(maturin/pytest/coverage);
+        # (2) the runtime deps the Connect server's Python workers need for 
arrow/pandas
+        # execution (mirrors build_python_connect.yml).
+        run: |
+          python -m pip install --upgrade pip
+          python -m pip install maturin pytest coverage
+          python -m pip install pandas pyarrow numpy grpcio grpcio-status 
googleapis-common-protos cloudpickle zstandard py4j
+
+      - name: Cache Spark ${{ env.SPARK_VERSION }} distribution
+        uses: actions/cache@v4
+        with:
+          path: ~/spark-dist
+          key: spark-dist-${{ env.SPARK_VERSION }}-hadoop3
+
+      - name: Download Apache Spark ${{ env.SPARK_VERSION }} (Connect server)
+        id: spark-download
+        run: |
+          PKG="spark-${SPARK_VERSION}-bin-hadoop3"
+          if [ ! -d "${HOME}/spark-dist/${PKG}" ]; then
+            
URL="https://archive.apache.org/dist/spark/spark-${SPARK_VERSION}/${PKG}.tgz";
+            mkdir -p "${HOME}/spark-dist"
+            echo "Downloading ${URL}"
+            wget -q -O "${HOME}/spark-dist/${PKG}.tgz" "${URL}"
+            tar -xzf "${HOME}/spark-dist/${PKG}.tgz" -C "${HOME}/spark-dist"
+            rm -f "${HOME}/spark-dist/${PKG}.tgz"
+          else
+            echo "Using cached Spark distribution."
+          fi
+          echo "SPARK_HOME=${HOME}/spark-dist/${PKG}" >> "$GITHUB_OUTPUT"
+
+      - name: Start Spark Connect server
+        env:
+          # The server spawns Python workers for arrow/pandas execution; they 
need
+          # pandas + pyarrow (installed above) plus pyspark (from 
SPARK_HOME/python).
+          PYSPARK_PYTHON: python
+          PYSPARK_DRIVER_PYTHON: python
+        run: |
+          SPARK_HOME="${{ steps.spark-download.outputs.SPARK_HOME }}"
+          echo "Starting Connect server on sc://localhost:15002"
+          "${SPARK_HOME}/sbin/start-connect-server.sh" \
+            --packages "org.apache.spark:spark-connect_2.13:${SPARK_VERSION}" \
+            --driver-memory 3g \
+            --conf spark.sql.session.localRelationSizeLimit=1073741824 \
+            > /tmp/connect-server.log 2>&1 &
+          for i in $(seq 1 90); do
+            if (echo > /dev/tcp/localhost/15002) 2>/dev/null; then
+              echo "Connect server is up (after ${i}s)."; break
+            fi
+            if [ "$i" -eq 90 ]; then echo "Server failed to start:"; cat 
/tmp/connect-server.log; exit 1; fi
+            sleep 1
+          done
+
       - name: Generate Rust coverage report
+        # The behavioral e2e_* tests self-gate on SPARK_REMOTE and no-op 
without a live
+        # server, so a server-less coverage run undercounts the runtime 
execution paths
+        # (collect/count/RPC-backed DataFrame, functions, session, catalog, 
...) and the
+        # badge showed only ~73%. Run coverage in two accumulated passes and 
combine:
+        #   1. the default unit + golden (plan-serialization) tests, run in 
parallel;
+        #   2. the behavioral e2e suite against the live server (SPARK_REMOTE 
set), run
+        #      serially since the tests share one server session.
+        # `--no-report` accumulates raw coverage; `report` merges both passes 
into one JSON.
         run: |
-          cargo llvm-cov --no-cfg-coverage -p apache-spark-connect \
-            --json --output-path coverage-rust-llvm.json
+          cargo llvm-cov clean --workspace
+          cargo llvm-cov --no-cfg-coverage --no-report -p apache-spark-connect
+          SPARK_REMOTE=sc://localhost:15002 \
+            cargo llvm-cov --no-cfg-coverage --no-report -p 
apache-spark-connect \
+              --test e2e_functions --test e2e_columns --test e2e_dataframe \
+              --test e2e_coverage --test e2e_integration \
+              -- --test-threads=1
+          cargo llvm-cov report --json --output-path coverage-rust-llvm.json
 
       - name: Parse Rust coverage and create badge JSON
         # Extract line coverage % from llvm-cov JSON output and convert to 
shields.io
@@ -127,17 +210,6 @@ jobs:
           print(f"Rust coverage: {coverage_pct:.1f}% ({color})")
           PYTHON_EOF
 
-      - name: Install Python and dependencies
-        uses: actions/setup-python@v5
-        with:
-          python-version: "3.11"
-          cache: pip
-
-      - name: Install Python build dependencies
-        run: |
-          python -m pip install --upgrade pip
-          python -m pip install maturin pytest coverage
-
       - name: Build Python extension
         run: |
           cargo build -p pyspark-rs --release
@@ -228,3 +300,11 @@ jobs:
             coverage-rust-badge.json
             coverage-python-badge.json
           retention-days: 5
+
+      - name: Upload Connect server log
+        if: always()
+        uses: actions/upload-artifact@v4
+        with:
+          name: coverage-connect-server-log
+          path: /tmp/connect-server.log
+          if-no-files-found: warn


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to