Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
8dc2cb6
test(core): define explicit database test lanes
nevzheng Sep 27, 2026
00d8f0d
fix(ci): address #13517 review nits from jerryshao
claude Sep 28, 2026
57e3f4f
improvement(test): replace per-backend exclude-tags with @DatabaseTes…
claude Sep 28, 2026
123ac1f
Revert "improvement(test): replace per-backend exclude-tags with @Dat…
claude Sep 28, 2026
e2c5af8
fix(test): update TestBackendTestSelector for getDisplayName() format…
claude Sep 28, 2026
409c0b7
improvement(ci): remove the unwired compare-legacy subcommand
claude Sep 28, 2026
737c831
fix(ci): use a JUnit tag expression for core database lane exclusion
claude Sep 28, 2026
59cb4e5
fix(ci): revert accidental test_core_test_identity.py inclusion
claude Sep 28, 2026
8a53aac
improvement(test): flatten core database lane tags, drop the gatekeep…
claude Sep 28, 2026
4650c02
improvement(test): namespace core database lane annotations under Cor…
claude Sep 28, 2026
adabf54
improvement(test): deprecate :core:test in place, add SPIP design doc
claude Sep 28, 2026
28afdd1
fix(test): fix unit-lane manifest break, tighten CoreBackend docs
claude Sep 28, 2026
9a027ef
improvement(test): fail when a core test class runs in no lane
nevzheng Sep 30, 2026
81ae97d
docs(build): note that includeDockerTaggedTests is read in doFirst
nevzheng Sep 30, 2026
64c0c48
docs(build): correct when CI realizes :core:test
nevzheng Sep 30, 2026
78b5cf7
improvement(test): report member classes in coreTestLaneOf
nevzheng Sep 30, 2026
05511e4
docs(design): note the orphan-lane guard in the core DB split doc
nevzheng Sep 30, 2026
f728749
ci: run core database test lanes in their own build.yml jobs
claude Oct 12, 2026
7cc881a
improvement(test): keep only the core database lane changes
nevzheng Oct 13, 2026
27ec2cb
docs(test): note that the core lane, not the class, decides Docker
nevzheng Oct 13, 2026
95ecb6c
docs(test): tighten core lane comments
nevzheng Oct 13, 2026
15b30e0
improvement(test): trim core lane test scaffolding
nevzheng Oct 13, 2026
85a0c7f
improvement(test): drop core lane code that restates defaults or pins…
nevzheng Oct 13, 2026
7df1dae
docs(test): describe the core test lanes in how-to-test
nevzheng Oct 13, 2026
5481579
improvement(test): give coreUnitTest main's policies.md setup
nevzheng Oct 13, 2026
4ef3fd5
test(core): reuse database fixtures by class
nevzheng Sep 27, 2026
56290e6
fix(test): log container close failures at the end of the test plan a…
nevzheng Oct 14, 2026
ca32141
test(core): add bounded adaptive database forks
nevzheng Sep 27, 2026
1eae08e
fix(test): keep the fork count out of the core lanes' cache key
nevzheng Oct 14, 2026
9912611
improvement(test): share one container per backend across core DB tests
claude Sep 28, 2026
e49f2cd
improvement(test): run core unit and H2 tests as :core:test
nevzheng Oct 13, 2026
81471da
improvement(test): run core database tests against an injected database
nevzheng Oct 14, 2026
62934a0
ci: run the core database tests against service containers
nevzheng Oct 14, 2026
b68229d
fix(test): give each test JVM's databases a random name token
nevzheng Oct 14, 2026
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
257 changes: 240 additions & 17 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,10 @@ jobs:
-x :catalogs-contrib:catalog-jdbc-clickhouse:test
-x :catalogs-contrib:catalog-jdbc-hologres:test
-x :catalogs-contrib:catalog-jdbc-oceanbase:test
# Core's tests run in the core-test job, one job per core test task.
-x :core:test
-x :core:coreMySQLTest
-x :core:corePostgreSQLTest
)

if [ "${{ needs.changes.outputs.mcp_server_changes }}" != "true" ]; then
Expand All @@ -223,13 +227,244 @@ jobs:

./gradlew "${gradle_args[@]}"

- name: Fetch base branch for coverage diff
- name: Upload coverage data
if: github.event_name == 'pull_request'
uses: actions/upload-artifact@v7
with:
name: jacoco-build
# build.gradle.kts anchors the artifact at the repository root, so the report paths keep
# their module prefix, which jacoco_report.py uses to name modules.
path: |
build.gradle.kts
**/build/reports/jacoco/test/jacocoTestReport.xml
if-no-files-found: ignore
retention-days: 1

- name: Upload unit tests report
uses: actions/upload-artifact@v7
if: failure()
with:
name: unit test report
path: |
build/reports
catalogs-contrib/**/*.log
catalogs-contrib/**/*.tar
catalogs/**/*.log
catalogs/**/*.tar

core-test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
java-version: [ 17 ]
# One job per core test lane, so each lane's results and coverage inputs are inspectable on
# their own. Lanes are defined in core/build.gradle.kts.
lane: [ h2, mysql, postgresql ]
include:
- lane: h2
task: test
skip-docker-tests: true
- lane: mysql
task: coreMySQLTest
skip-docker-tests: false
- lane: postgresql
task: corePostgreSQLTest
skip-docker-tests: false
timeout-minutes: 120
needs: changes
if: >-
needs.changes.outputs.source_changes == 'true' &&
needs.changes.outputs.maintenance_module_only_changes != 'true'
steps:
- uses: actions/checkout@v4

- uses: ./.github/actions/setup-java-toolchains
with:
java-version: ${{ matrix.java-version }}

- name: Free up disk space
run: |
dev/ci/util_free_space.sh

- name: Test database concurrency build logic
id: buildsrc-tests
if: matrix.lane == 'h2'
run: ./gradlew -p buildSrc test

- name: Upload database concurrency build logic test report
if: ${{ failure() && steps.buildsrc-tests.outcome == 'failure' }}
uses: actions/upload-artifact@v7
with:
name: core-database-buildsrc-test-report
path: |
buildSrc/build/reports/tests/test
buildSrc/build/test-results/test
if-no-files-found: warn
retention-days: 1

- name: Run core ${{ matrix.lane }} lane
run: |
./gradlew :core:${{ matrix.task }} \
-PskipWeb=true \
-PskipITs \
-PskipDockerTests=${{ matrix.skip-docker-tests }}

- name: Validate core test evidence
run: |
junit_xml="$(find "core/build/test-results/${{ matrix.task }}" \
-type f -name 'TEST-*.xml' -print -quit)"
test -n "${junit_xml}"
test -s "${junit_xml}"
test -s "build/reports/tests/core/${{ matrix.task }}/index.html"
test -s "core/build/jacoco/${{ matrix.task }}.exec"
if [ "${{ matrix.lane }}" != "h2" ]; then
shard_record="$(find \
"core/build/test-results/${{ matrix.task }}/shard-telemetry" \
-type f -name 'worker-*.log' -size +0c -print -quit)"
test -n "${shard_record}"
test -s "${shard_record}"
fi

- name: Upload core test evidence
uses: actions/upload-artifact@v7
with:
name: core-${{ matrix.lane }}-test-evidence
path: |
core/build/test-results/${{ matrix.task }}
build/reports/tests/core/${{ matrix.task }}
core/build/jacoco/${{ matrix.task }}.exec
if-no-files-found: error
retention-days: 1

- name: Upload core test report
uses: actions/upload-artifact@v7
if: failure()
with:
name: core test report ${{ matrix.lane }}
path: |
build/reports/tests/core
core/build/test-results

# The same MySQL and PostgreSQL tasks against a database the job provides as a service
# container, the way a Kubernetes pod would use a sidecar. Gradle starts no container here.
core-test-injected:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- backend: mysql
task: coreMySQLTest
image: mysql:8.0
port: 3306
url: jdbc:mysql://127.0.0.1:3306/?useSSL=false&allowPublicKeyRetrieval=true
health: --health-cmd="mysqladmin ping -h 127.0.0.1 -proot" --health-interval=5s --health-timeout=5s --health-retries=30
- backend: postgresql
task: corePostgreSQLTest
image: postgres:13
port: 5432
url: jdbc:postgresql://127.0.0.1:5432/postgres
health: --health-cmd="pg_isready -U root" --health-interval=5s --health-timeout=5s --health-retries=30
timeout-minutes: 120
needs: changes
if: >-
needs.changes.outputs.source_changes == 'true' &&
needs.changes.outputs.maintenance_module_only_changes != 'true'
services:
db:
image: ${{ matrix.image }}
env:
MYSQL_ROOT_PASSWORD: root
POSTGRES_USER: root
POSTGRES_PASSWORD: root
ports:
- ${{ matrix.port }}:${{ matrix.port }}
options: ${{ matrix.health }}
steps:
- uses: actions/checkout@v4

- uses: ./.github/actions/setup-java-toolchains
with:
java-version: 17

- name: Run core ${{ matrix.backend }} tests against the injected database
env:
GRAVITINO_TEST_MYSQL_URL: ${{ matrix.backend == 'mysql' && matrix.url || '' }}
GRAVITINO_TEST_POSTGRESQL_URL: ${{ matrix.backend == 'postgresql' && matrix.url || '' }}
GRAVITINO_TEST_MYSQL_USER: root
GRAVITINO_TEST_MYSQL_PASSWORD: root
GRAVITINO_TEST_POSTGRESQL_USER: root
GRAVITINO_TEST_POSTGRESQL_PASSWORD: root
run: |
./gradlew :core:${{ matrix.task }} -PskipWeb=true -PskipITs -PskipDockerTests=true \
| tee core-injected.log

- name: Check the tests used the injected database
run: |
grep -q "\[CORE-DB-SOURCE\] task=:core:${{ matrix.task }} backend=${{ matrix.backend }} source=injected" core-injected.log
# Only the service container may exist: Gradle must not have started its own.
test "$(docker ps -aq | wc -l)" -eq 1

- name: Upload core injected test report
uses: actions/upload-artifact@v7
if: failure()
with:
name: core injected test report ${{ matrix.backend }}
path: |
build/reports/tests/core
core/build/test-results

coverage:
runs-on: ubuntu-latest
timeout-minutes: 30
needs: [ changes, build, core-test ]
if: >-
always() &&
github.event_name == 'pull_request' &&
needs.build.result == 'success' &&
(needs.changes.outputs.maintenance_module_only_changes == 'true' ||
needs['core-test'].result == 'success')
steps:
- uses: actions/checkout@v4

- uses: ./.github/actions/setup-java-toolchains
if: needs.changes.outputs.maintenance_module_only_changes != 'true'
with:
java-version: 17

- name: Fetch base branch for coverage diff
run: git fetch origin ${{ github.base_ref }} --depth=1

- name: Download coverage data
uses: actions/download-artifact@v4
with:
pattern: jacoco-*
merge-multiple: true

- name: Download core test evidence
if: needs.changes.outputs.maintenance_module_only_changes != 'true'
uses: actions/download-artifact@v4
with:
pattern: core-*-test-evidence
path: core-test-evidence

- name: Generate combined core coverage
if: needs.changes.outputs.maintenance_module_only_changes != 'true'
run: |
mkdir -p core/build/jacoco
for task in test coreMySQLTest corePostgreSQLTest; do
exec_file="$(find core-test-evidence -path "*/core/build/jacoco/${task}.exec" -print -quit)"
test -n "${exec_file}"
install -m 0644 "${exec_file}" "core/build/jacoco/${task}.exec"
done
./gradlew \
:core:jacocoTestReport \
-PskipWeb=true
test -s core/build/reports/jacoco/test/jacocoTestReport.xml

- name: Generate Coverage Report
id: coverage
if: github.event_name == 'pull_request'
run: |
python3 dev/ci/jacoco_report.py \
--base-ref "${{ github.base_ref }}" \
Expand All @@ -240,11 +475,11 @@ jobs:
--output coverage-report.md

- name: Save PR number
if: github.event_name == 'pull_request' && steps.coverage.outputs.has_reports == 'true'
if: steps.coverage.outputs.has_reports == 'true'
run: echo "${{ github.event.pull_request.number }}" > pr-number.txt

- name: Upload Coverage Report
if: github.event_name == 'pull_request' && steps.coverage.outputs.has_reports == 'true'
if: steps.coverage.outputs.has_reports == 'true'
uses: actions/upload-artifact@v7
with:
name: coverage-report
Expand All @@ -253,19 +488,7 @@ jobs:
pr-number.txt

- name: Output Coverage Info
if: github.event_name == 'pull_request' && steps.coverage.outputs.has_reports == 'true'
if: steps.coverage.outputs.has_reports == 'true'
run: |
echo "Total coverage ${{ steps.coverage.outputs.coverage-overall }}"
echo "Changed Files coverage ${{ steps.coverage.outputs.coverage-changed-files }}"

- name: Upload unit tests report
uses: actions/upload-artifact@v7
if: failure()
with:
name: unit test report
path: |
build/reports
catalogs-contrib/**/*.log
catalogs-contrib/**/*.tar
catalogs/**/*.log
catalogs/**/*.tar
49 changes: 40 additions & 9 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import com.github.jk1.license.render.InventoryHtmlReportRenderer
import com.github.jk1.license.render.ReportRenderer
import com.github.vlsi.gradle.dsl.configureEach
import net.ltgt.gradle.errorprone.errorprone
import org.apache.gravitino.testing.CoreDatabaseConcurrency
import org.apache.gravitino.testing.SharedDbContainerService
import org.apache.tools.zip.ZipEntry
import org.apache.tools.zip.ZipOutputStream
import org.gradle.api.attributes.java.TargetJvmVersion
Expand Down Expand Up @@ -81,6 +83,18 @@ val sharedTestEnvironmentLock = gradle.sharedServices.registerIfAbsent(
maxParallelUsages.set(1)
}

// Owns one shared MySQL container and one shared PostgreSQL container for the whole build (see
// org.apache.gravitino.testing.SharedDbContainerService in buildSrc), so the Core database test
// tasks no longer have to start a fresh container per test-worker fork. Registered here, at the
// root, so a single instance is shared by every project that opts in via `usesService`. Stored on
// rootProject.extra so subproject build scripts (for example core/build.gradle.kts) can look up
// the same registration by name.
val sharedDbContainerService = gradle.sharedServices.registerIfAbsent(
"sharedDbContainerService",
SharedDbContainerService::class
) {}
rootProject.extra["sharedDbContainerService"] = sharedDbContainerService

/** Packages the legal documents for one Maven artifact, retaining dependency provenance. */
@CacheableTask
abstract class GenerateJarLegalFiles : DefaultTask() {
Expand Down Expand Up @@ -458,12 +472,11 @@ allprojects {
// Ryuk need privileged mode, if we want to rootless or run non-privileged mode, we need to disable it.
param.environment("TESTCONTAINERS_RYUK_DISABLED", "true")

val dockerRunning = project.rootProject.extra["dockerRunning"] as? Boolean ?: false
val macDockerConnector = project.rootProject.extra["macDockerConnector"] as? Boolean ?: false
if (OperatingSystem.current().isMacOsX() &&
dockerRunning &&
macDockerConnector
) {
val macDockerConnectorFixedNetwork =
project.rootProject.extra[
CoreDatabaseConcurrency.MAC_DOCKER_CONNECTOR_FIXED_NETWORK_EXTRA
] as? Boolean ?: false
if (macDockerConnectorFixedNetwork) {
param.environment("NEED_CREATE_DOCKER_NETWORK", "true")
}

Expand Down Expand Up @@ -514,9 +527,14 @@ allprojects {

val dockerTest = project.rootProject.extra["dockerTest"] as? Boolean ?: false
param.environment("dockerTest", dockerTest.toString())
// Must stay inside doFirst: tasks such as :core's database lanes set this flag in their own
// configuration action, which may run after this configureEach action registers.
val includeDockerTaggedTests =
param.extensions.extraProperties.properties["includeDockerTaggedTests"] as? Boolean
?: dockerTest
val dorisMultiVersion = project.hasProperty("dorisMultiVersionTest")
param.useJUnitPlatform {
if (!dockerTest) {
if (!includeDockerTaggedTests) {
excludeTags("gravitino-docker-test")
}
if (!dorisMultiVersion) {
Expand Down Expand Up @@ -1017,9 +1035,14 @@ subprojects {
val skipTests = project.hasProperty("skipTests")
if (!skipTests) {
val extraArgs = project.property("extraJvmArgs") as List<String>
jvmArgs = listOf("-Xmx4G") + extraArgs
jvmArgs = listOf(CoreDatabaseConcurrency.TEST_WORKER_MAX_HEAP_ARGUMENT) + extraArgs
useJUnitPlatform()
finalizedBy(tasks.getByName("jacocoTestReport"))
val isCoreSuiteTask =
project.path == ":core" &&
name in setOf("coreMySQLTest", "corePostgreSQLTest")
if (!isCoreSuiteTask) {
finalizedBy(tasks.getByName("jacocoTestReport"))
}
}
}

Expand Down Expand Up @@ -1630,13 +1653,21 @@ project.extra["dockerTest"] = false
project.extra["dockerRunning"] = false
project.extra["macDockerConnector"] = false
project.extra["isOrbStack"] = false
project.extra[CoreDatabaseConcurrency.MAC_DOCKER_CONNECTOR_FIXED_NETWORK_EXTRA] = false

// The following is to check the docker status and print the tip message
fun printDockerCheckInfo() {
checkMacDockerConnector()
checkDockerStatus()
checkOrbStackStatus()

val macDockerConnectorFixedNetwork =
OperatingSystem.current().isMacOsX() &&
(project.extra["dockerRunning"] as? Boolean ?: false) &&
(project.extra["macDockerConnector"] as? Boolean ?: false)
project.extra[CoreDatabaseConcurrency.MAC_DOCKER_CONNECTOR_FIXED_NETWORK_EXTRA] =
macDockerConnectorFixedNetwork

val testMode = project.properties["testMode"] as? String ?: "embedded"
if (testMode != "deploy" && testMode != "embedded") {
return
Expand Down
Loading
Loading