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

maciej 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 2e74a3e10 ci(Java): add JaCoCo code coverage with Codecov integration 
(#2623)
2e74a3e10 is described below

commit 2e74a3e100637dc17c2a28f9dc3c928730e06c14
Author: Qichao Chu <[email protected]>
AuthorDate: Mon Feb 9 12:35:47 2026 -0800

    ci(Java): add JaCoCo code coverage with Codecov integration (#2623)
---
 .github/actions/java-gradle/pre-merge/action.yml   | 21 ++++++++++++
 .github/workflows/_test.yml                        | 12 +++++++
 codecov.yml                                        |  9 ++++-
 foreign/java/build.gradle.kts                      | 39 ++++++++++++++++++++++
 .../kotlin/iggy.java-common-conventions.gradle.kts | 31 +++++++++++++++++
 foreign/java/gradle/libs.versions.toml             |  1 +
 .../org/apache/iggy/serde/BytesSerializer.java     |  3 +-
 7 files changed, 114 insertions(+), 2 deletions(-)

diff --git a/.github/actions/java-gradle/pre-merge/action.yml 
b/.github/actions/java-gradle/pre-merge/action.yml
index df02a52fc..7c76044f4 100644
--- a/.github/actions/java-gradle/pre-merge/action.yml
+++ b/.github/actions/java-gradle/pre-merge/action.yml
@@ -93,6 +93,27 @@ runs:
         USE_EXTERNAL_SERVER: true
       run: ./gradlew test
 
+    - name: Generate coverage report
+      if: inputs.task == 'test'
+      shell: bash
+      working-directory: foreign/java
+      env:
+        USE_EXTERNAL_SERVER: true
+      run: ./gradlew jacocoAggregatedReport
+
+    - name: Copy coverage reports
+      if: ${{ !cancelled() && inputs.task == 'test' }}
+      shell: bash
+      run: |
+        if [ -f 
"foreign/java/build/reports/jacoco/aggregate/jacocoAggregated.xml" ]; then
+          echo "Found aggregated coverage report"
+          mkdir -p reports/java-coverage
+          cp foreign/java/build/reports/jacoco/aggregate/jacocoAggregated.xml 
reports/java-coverage/
+          if [ -d "foreign/java/build/reports/jacoco/aggregate/html" ]; then
+            cp -r foreign/java/build/reports/jacoco/aggregate/html 
reports/java-coverage/
+          fi
+        fi
+
     - name: Copy test reports
       if: ${{ !cancelled() && inputs.task == 'test' }}
       shell: bash
diff --git a/.github/workflows/_test.yml b/.github/workflows/_test.yml
index d83ee8add..b05626676 100644
--- a/.github/workflows/_test.yml
+++ b/.github/workflows/_test.yml
@@ -104,6 +104,18 @@ jobs:
         with:
           task: ${{ inputs.task }}
 
+      - name: Upload Java coverage to Codecov
+        if: inputs.component == 'sdk-java' && inputs.task == 'test'
+        uses: codecov/codecov-action@v5
+        with:
+          token: ${{ secrets.CODECOV_TOKEN }}
+          files: reports/java-coverage/jacocoAggregated.xml
+          disable_search: true
+          flags: java
+          fail_ci_if_error: false
+          verbose: true
+          override_pr: ${{ github.event.pull_request.number }}
+
       # C# SDK
       - name: Run C# SDK task
         if: inputs.component == 'sdk-csharp'
diff --git a/codecov.yml b/codecov.yml
index 0cdb369b5..1926f1853 100644
--- a/codecov.yml
+++ b/codecov.yml
@@ -48,6 +48,13 @@ ignore:
   - "core/harness_derive/**"
   - "bdd/**"
   - "examples/**"
-  - "foreign/**"
+  - "foreign/csharp/**"
+  - "foreign/go/**"
+  - "foreign/node/**"
+  - "foreign/python/**"
   - "web/**"
   - "**/tests/**"
+  - "**/*Test.java"
+  - "**/test/**"
+  - "**/build/**"
+  - "**/target/**"
diff --git a/foreign/java/build.gradle.kts b/foreign/java/build.gradle.kts
index d1d66fb3b..a0693eb03 100644
--- a/foreign/java/build.gradle.kts
+++ b/foreign/java/build.gradle.kts
@@ -21,6 +21,11 @@ import com.diffplug.gradle.spotless.SpotlessExtension
 
 plugins {
     alias(libs.plugins.spotless) apply false
+    jacoco
+}
+
+repositories {
+    mavenCentral()
 }
 
 subprojects {
@@ -103,3 +108,37 @@ subprojects {
         }
     }
 }
+
+tasks.register<JacocoReport>("jacocoAggregatedReport") {
+    description = "Generates aggregated code coverage report for all modules"
+    group = "verification"
+
+    dependsOn(subprojects.map { it.tasks.named("test") })
+
+    // Aggregate execution data from all subprojects
+    executionData.setFrom(files(subprojects.mapNotNull {
+        val testTask = it.tasks.withType<Test>().findByName("test")
+        if (testTask != null && it.plugins.hasPlugin("java")) {
+            it.layout.buildDirectory.file("jacoco/test.exec").get().asFile
+        } else {
+            null
+        }
+    }.filter { it.exists() }))
+
+    // Aggregate source and class files
+    subprojects.forEach { subproject ->
+        if (subproject.plugins.hasPlugin("java")) {
+            val sourceSets = 
subproject.extensions.getByType<SourceSetContainer>()
+            
sourceDirectories.from(sourceSets.getByName("main").allSource.srcDirs)
+            classDirectories.from(files(sourceSets.getByName("main").output))
+        }
+    }
+
+    reports {
+        xml.required.set(true)
+        
xml.outputLocation.set(layout.buildDirectory.file("reports/jacoco/aggregate/jacocoAggregated.xml"))
+        html.required.set(true)
+        
html.outputLocation.set(layout.buildDirectory.dir("reports/jacoco/aggregate/html"))
+        csv.required.set(false)
+    }
+}
diff --git 
a/foreign/java/buildSrc/src/main/kotlin/iggy.java-common-conventions.gradle.kts 
b/foreign/java/buildSrc/src/main/kotlin/iggy.java-common-conventions.gradle.kts
index bb86082b9..ba057c6b6 100644
--- 
a/foreign/java/buildSrc/src/main/kotlin/iggy.java-common-conventions.gradle.kts
+++ 
b/foreign/java/buildSrc/src/main/kotlin/iggy.java-common-conventions.gradle.kts
@@ -17,8 +17,11 @@
  * under the License.
  */
 
+import org.gradle.api.artifacts.VersionCatalogsExtension
+
 plugins {
     java
+    jacoco
 }
 
 repositories {
@@ -39,6 +42,34 @@ tasks.withType<Javadoc> {
     options.encoding = "UTF-8"
 }
 
+jacoco {
+    toolVersion = extensions.getByType<VersionCatalogsExtension>()
+        .named("libs")
+        .findVersion("jacoco")
+        .get()
+        .requiredVersion
+}
+
 tasks.withType<Test> {
     useJUnitPlatform()
+    finalizedBy(tasks.jacocoTestReport)
+}
+
+tasks.jacocoTestReport {
+    dependsOn(tasks.test)
+    reports {
+        xml.required.set(true)
+        html.required.set(true)
+        csv.required.set(false)
+    }
+}
+
+tasks.jacocoTestCoverageVerification {
+    violationRules {
+        rule {
+            limit {
+                minimum = "0.0".toBigDecimal()
+            }
+        }
+    }
 }
diff --git a/foreign/java/gradle/libs.versions.toml 
b/foreign/java/gradle/libs.versions.toml
index dd3e8408c..108b5909f 100644
--- a/foreign/java/gradle/libs.versions.toml
+++ b/foreign/java/gradle/libs.versions.toml
@@ -58,6 +58,7 @@ typesafe-config = "1.4.5"
 spotless = "8.1.0"
 shadow = "9.2.2"
 checkstyle = "12.1.2"
+jacoco = "0.8.14"
 
 [libraries]
 # Jackson
diff --git 
a/foreign/java/java-sdk/src/main/java/org/apache/iggy/serde/BytesSerializer.java
 
b/foreign/java/java-sdk/src/main/java/org/apache/iggy/serde/BytesSerializer.java
index c9c317189..c4354719c 100644
--- 
a/foreign/java/java-sdk/src/main/java/org/apache/iggy/serde/BytesSerializer.java
+++ 
b/foreign/java/java-sdk/src/main/java/org/apache/iggy/serde/BytesSerializer.java
@@ -207,7 +207,8 @@ public final class BytesSerializer {
     }
 
     public static ByteBuf toBytes(String value) {
-        ByteBuf buffer = Unpooled.buffer(1 + value.length());
+        int bufferLength = 1 + value.length();
+        ByteBuf buffer = Unpooled.buffer(bufferLength);
         byte[] stringBytes = value.getBytes(StandardCharsets.UTF_8);
         buffer.writeByte(stringBytes.length);
         buffer.writeBytes(stringBytes);

Reply via email to