Skip to content
Merged

Codecov #1369

Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 58 additions & 4 deletions .github/workflows/test-and-build-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ jobs:

test-and-build-linux:
needs: Get-CI-Image-Tag
timeout-minutes: 25
env:
TEST_FILTER: ${{ matrix.test_filter }}
strategy:
Expand Down Expand Up @@ -51,24 +52,40 @@ jobs:
# This is a hack, but this step creates a link to the X: mounted drive, which makes the path
# short enough to work on Windows
- name: Build with Gradle
timeout-minutes: 20
run: |
chown -R 1000:1000 `pwd`
su `id -un 1000` -c "./gradlew build ${{ env.TEST_FILTER }}"
- name: Generate Jacoco Test Report
if: always() && matrix.java == 21
run: |
su `id -un 1000` -c "./gradlew jacocoTestReport"
- name: Upload coverage XML
uses: actions/upload-artifact@v4
if: always() && matrix.java == 21
with:
name: coverage-xml-${{ matrix.java }}-${{ matrix.feature }}
path: build/reports/jacoco/**/jacocoTestReport.xml
if-no-files-found: warn
overwrite: 'true'
- name: Upload failed logs
uses: actions/upload-artifact@v4
if: ${{ failure() }}
with:
name: logs-${{ matrix.java }}-${{ matrix.feature }}
path: build/testclusters/integTest-*/logs/*
overwrite: 'true'
- name: Upload test reports
uses: actions/upload-artifact@v4
if: ${{ failure() }}
with:
name: test-reports-linux-${{ matrix.java }}-${{ matrix.feature }}
path: build/reports/
overwrite: 'true'
- name: Create Artifact Path
run: |
mkdir -p index-management-artifacts
cp ./build/distributions/*.zip index-management-artifacts
- name: Uploads coverage
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
# This step uses the upload-artifact Github action: https://github.com/actions/upload-artifact
- name: Upload Artifacts
# v4 requires node.js 20 which is not supported
Expand Down Expand Up @@ -134,3 +151,40 @@ jobs:
name: index-management-plugin-${{ matrix.os }}-${{ matrix.java }}-${{ matrix.feature }}
path: index-management-artifacts
overwrite: 'true'

report-coverage:
needs: ["test-and-build-linux"]
if: always()
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
path: downloaded-artifacts
pattern: coverage-xml-*

- name: Display structure of downloaded files
run: ls -R
working-directory: downloaded-artifacts

- name: Check if coverage files exist
id: check_coverage
run: |
if find downloaded-artifacts -name "*.xml" -type f | grep -q .; then
echo "Coverage XML files found"
echo "has_coverage=true" >> $GITHUB_OUTPUT
else
echo "No coverage XML files found"
echo "has_coverage=false" >> $GITHUB_OUTPUT
fi

- name: Upload Coverage with retry
if: steps.check_coverage.outputs.has_coverage == 'true'
uses: Wandalen/[email protected]
with:
attempt_limit: 5
attempt_delay: 2000
action: codecov/codecov-action@v4
with: |
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true
verbose: true
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ bin/
spi/bin/
src/test/resources/notifications*

.claude
CLAUDE.md
26 changes: 26 additions & 0 deletions DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- [Setup](#setup)
- [Build](#build)
- [Building from the command line](#building-from-the-command-line)
- [Code Coverage](#code-coverage)
- [Debugging](#debugging)
- [Using IntelliJ IDEA](#using-intellij-idea)
- [Submitting Changes](#submitting-changes)
Expand Down Expand Up @@ -63,6 +64,31 @@ However, to build the `index management` plugin project, we also use the OpenSea

When launching a cluster using one of the above commands, logs are placed in `build/testclusters/integTest-0/logs`. Though the logs are teed to the console, in practices it's best to check the actual log file.

### Code Coverage

The project supports generating code coverage reports using JaCoCo.

#### Generating Coverage Reports

To generate code coverage reports

```bash
./gradlew check -Dtests.coverage=true
```

Or run any tests, and generate test report

```bash
./gradlew test
./gradlew jacocoTestReport
```

#### Coverage Report Locations

After running with coverage enabled, reports are generated in:
- **HTML Report**: `build/reports/jacoco/test/html/index.html` (human readable)
- **XML Report**: `build/reports/jacoco/test/jacocoTestReport.xml` (for tools like Codecov)

### Debugging

Sometimes it is useful to attach a debugger to either the OpenSearch cluster or the integ tests to see what's going on. When running unit tests, hit **Debug** from the IDE's gutter to debug the tests. For the OpenSearch cluster or the integ tests, first, make sure start a debugger listening on port `5005`.
Expand Down
33 changes: 27 additions & 6 deletions build-tools/coverage.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

// Get gradle to generate the required jvm agent arg for us using a dummy tasks of type Test. Unfortunately Elastic's
// testing tasks don't derive from Test so the jacoco plugin can't do this automatically.
def jacocoDir = "${buildDir}/jacoco"
def jacocoDir = layout.buildDirectory.dir("jacoco").get().asFile.absolutePath

tasks.register("dummyTest", Test) {
enabled = false
Expand All @@ -42,12 +42,28 @@ tasks.register("dummyIntegTest", Test) {
}

integTest {
systemProperty 'jacoco.dir', "${jacocoDir}"
systemProperty 'jacoco.dir', jacocoDir
}

jacocoTestReport {
dependsOn integTest, test
executionData dummyTest.jacoco.destinationFile, dummyIntegTest.jacoco.destinationFile
// Use mustRunAfter instead of dependsOn to avoid re-running tests
// This ensures proper ordering without forcing test execution
mustRunAfter test, integTest

// Configure execution data to use any available .exec files
executionData.from fileTree(dir: jacocoDir, include: '*.exec')

// Make the task skip only if no execution data exists at all
// Using a closure to defer the check until execution time
onlyIf {
def execFiles = fileTree(dir: jacocoDir, include: '*.exec').files
def hasExecFiles = execFiles.any { it.exists() }
if (!hasExecFiles) {
println("Skipping jacocoTestReport: No execution data files found in ${jacocoDir}")
}
hasExecFiles
}

sourceDirectories.from = "src/main/kotlin"
classDirectories.from = sourceSets.main.output
reports {
Expand All @@ -58,8 +74,6 @@ jacocoTestReport {

allprojects{
afterEvaluate {
jacocoTestReport.dependsOn integTest

testClusters.integTest {
jvmArgs " ${dummyIntegTest.jacoco.getAsJvmArg()}".replace('javaagent:','javaagent:/')
systemProperty 'com.sun.management.jmxremote', "true"
Expand All @@ -69,4 +83,11 @@ allprojects{
systemProperty 'java.rmi.server.hostname', "127.0.0.1"
}
}
}

// Attach code coverage report task to Gradle check task when coverage is enabled
if (System.getProperty("tests.coverage")) {
project.getTasks().named(JavaBasePlugin.CHECK_TASK_NAME).configure {
dependsOn tasks.named('jacocoTestReport', JacocoReport)
}
}
1 change: 0 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@ def usingMultiNode = project.properties.containsKey('numNodes')
if (!usingRemoteCluster && !usingMultiNode) {
apply from: 'build-tools/coverage.gradle'
}
check.dependsOn jacocoTestReport

opensearchplugin {
name 'opensearch-index-management'
Expand Down
1 change: 0 additions & 1 deletion spi/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ jacocoTestReport {
html.destination file("${buildDir}/jacoco/")
}
}
check.dependsOn jacocoTestReport

repositories {
mavenLocal()
Expand Down
Loading