diff --git a/.github/workflows/dataconnect.yml b/.github/workflows/dataconnect.yml new file mode 100644 index 00000000000..1049d031209 --- /dev/null +++ b/.github/workflows/dataconnect.yml @@ -0,0 +1,215 @@ +name: Data Connect Integration Tests + +on: + workflow_dispatch: + inputs: + javaVersion: + androidEmulatorApiLevel: + nodeJsVersion: + firebaseToolsVersion: + gradleInfoLog: + type: boolean + pull_request: + paths: + - .github/workflows/dataconnect.yml + - 'firebase-dataconnect/**' + - '!firebase-dataconnect/demo/**' + - '!firebase-dataconnect/scripts/**' + - '!firebase-dataconnect/**/*.md' + - '!firebase-dataconnect/**/*.txt' + schedule: + - cron: '0 11 * * *' # Run nightly at 11am UTC (3am Pacific, 6am Eastern) + +env: + FDC_JAVA_VERSION: ${{ inputs.javaVersion || '17' }} + FDC_ANDROID_EMULATOR_API_LEVEL: ${{ inputs.androidEmulatorApiLevel || '34' }} + FDC_NODEJS_VERSION: ${{ inputs.nodeJsVersion || '20' }} + FDC_FIREBASE_TOOLS_VERSION: ${{ inputs.firebaseToolsVersion || '13.29.1' }} + FDC_FIREBASE_TOOLS_DIR: /tmp/firebase-tools + FDC_FIREBASE_COMMAND: /tmp/firebase-tools/node_modules/.bin/firebase + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + integration-test: + continue-on-error: false + runs-on: ubuntu-latest + + services: + postgres: + image: postgres + env: + POSTGRES_PASSWORD: password + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + steps: + - uses: actions/checkout@v4 + with: + show-progress: false + + - uses: actions/setup-java@v4 + with: + java-version: ${{ env.FDC_JAVA_VERSION }} + distribution: temurin + + - uses: actions/setup-node@v4 + with: + node-version: ${{ env.FDC_NODEJS_VERSION }} + + - name: install firebase-tools + run: | + set -v + mkdir -p ${{ env.FDC_FIREBASE_TOOLS_DIR }} + cd ${{ env.FDC_FIREBASE_TOOLS_DIR }} + echo '{}' > package.json + npm install --fund=false --audit=false --save --save-exact firebase-tools@${{ env.FDC_FIREBASE_TOOLS_VERSION }} + + - name: Restore Gradle cache + uses: actions/cache/restore@v4 + if: github.event_name != 'schedule' + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: gradle-cache-jqnvfzw6w7 + + - name: tool versions + continue-on-error: true + run: | + set +e -v + uname -a + which java + java -version + which javac + javac -version + which node + node --version + ${{ env.FDC_FIREBASE_COMMAND }} --version + ./gradlew --version + + - name: Gradle assembleDebugAndroidTest + run: | + set -v + + # Speed up build times and also avoid configuring firebase-crashlytics-ndk + # which is finicky integrating with the Android NDK. + echo >> gradle.properties + echo "org.gradle.configureondemand=true" >> gradle.properties + + ./gradlew \ + --profile \ + ${{ (inputs.gradleInfoLog && '--info') || '' }} \ + :firebase-dataconnect:assembleDebugAndroidTest + + - name: Save Gradle cache + uses: actions/cache/save@v4 + if: github.event_name == 'schedule' + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: gradle-cache-jqnvfzw6w7 + + - name: Enable KVM group permissions for Android Emulator + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' \ + | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + + - name: Restore AVD cache + uses: actions/cache/restore@v4 + if: github.event_name != 'schedule' + id: avd-cache + with: + path: | + ~/.android/avd/* + ~/.android/adb* + key: avd-cache-zhdsn586je-api${{ env.FDC_ANDROID_EMULATOR_API_LEVEL }} + + - run: echo "github.event_name == '${{ github.event_name }}' steps.avd-cache.outputs.cache-hit == '${{ steps.avd-cache.outputs.cache-hit }}'" + + - name: Create AVD + if: github.event_name == 'schedule' || steps.avd-cache.outputs.cache-hit != 'true' + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: ${{ env.FDC_ANDROID_EMULATOR_API_LEVEL }} + arch: x86_64 + force-avd-creation: false + emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + disable-animations: false + script: echo "Generated AVD snapshot for caching." + + - name: Save AVD cache + uses: actions/cache/save@v4 + if: github.event_name == 'schedule' + with: + path: | + ~/.android/avd/* + ~/.android/adb* + key: avd-cache-zhdsn586je-api${{ env.FDC_ANDROID_EMULATOR_API_LEVEL }} + + - name: Data Connect Emulator + run: | + set -x + + echo 'emulator.postgresConnectionUrl=postgresql://postgres:password@127.0.0.1:5432?sslmode=disable' > firebase-dataconnect/dataconnect.local.properties + + ./gradlew \ + ${{ (inputs.gradleInfoLog && '--info') || '' }} \ + :firebase-dataconnect:connectors:runDebugDataConnectEmulator \ + >firebase.emulator.dataconnect.log 2>&1 & + + - name: Firebase Auth Emulator + run: | + set -x + cd firebase-dataconnect/emulator + ${{ env.FDC_FIREBASE_COMMAND }} emulators:start --only=auth >firebase.emulator.auth.log 2>&1 & + + - name: Gradle connectedCheck + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: ${{ env.FDC_ANDROID_EMULATOR_API_LEVEL }} + arch: x86_64 + force-avd-creation: false + emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + disable-animations: true + script: | + set -eux && ./gradlew ${{ (inputs.gradleInfoLog && '--info') || '' }} :firebase-dataconnect:connectedCheck :firebase-dataconnect:connectors:connectedCheck + + - uses: actions/upload-artifact@v4 + if: true + with: + name: logs + path: "**/*.log" + if-no-files-found: warn + compression-level: 9 + + - uses: actions/upload-artifact@v4 + with: + name: gradle_build_reports + path: firebase-dataconnect/**/build/reports/ + if-no-files-found: warn + compression-level: 9 + + # Check this yml file with "actionlint": https://github.com/rhysd/actionlint + # To run actionlint yourself, run `brew install actionlint` followed by + # `actionlint .github/workflows/dataconnect.yml` + actionlint-dataconnect-yml: + continue-on-error: false + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + show-progress: false + - uses: docker://rhysd/actionlint:1.7.7 + with: + args: -color /github/workspace/.github/workflows/dataconnect.yml diff --git a/firebase-dataconnect/src/androidTest/kotlin/com/google/firebase/dataconnect/QueryRefIntegrationTest.kt b/firebase-dataconnect/src/androidTest/kotlin/com/google/firebase/dataconnect/QueryRefIntegrationTest.kt index efcd75d2aab..5e0527a6082 100644 --- a/firebase-dataconnect/src/androidTest/kotlin/com/google/firebase/dataconnect/QueryRefIntegrationTest.kt +++ b/firebase-dataconnect/src/androidTest/kotlin/com/google/firebase/dataconnect/QueryRefIntegrationTest.kt @@ -31,7 +31,7 @@ import io.kotest.matchers.shouldBe import io.kotest.matchers.types.shouldBeInstanceOf import io.kotest.property.Arb import io.kotest.property.arbitrary.next -import kotlin.time.Duration.Companion.seconds +import kotlin.time.Duration.Companion.minutes import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.test.runTest @@ -388,7 +388,7 @@ class QueryRefIntegrationTest : DataConnectIntegrationTestBase() { @Test fun executeShouldSupportMassiveConcurrency() = - runTest(timeout = 60.seconds) { + runTest(timeout = 5.minutes) { val latch = SuspendingCountDownLatch(25_000) val query = personSchema.getPerson(id = "foo") diff --git a/firebase-dataconnect/src/androidTest/kotlin/com/google/firebase/dataconnect/QuerySubscriptionIntegrationTest.kt b/firebase-dataconnect/src/androidTest/kotlin/com/google/firebase/dataconnect/QuerySubscriptionIntegrationTest.kt index 908382ccdcc..6b0f0c4ff47 100644 --- a/firebase-dataconnect/src/androidTest/kotlin/com/google/firebase/dataconnect/QuerySubscriptionIntegrationTest.kt +++ b/firebase-dataconnect/src/androidTest/kotlin/com/google/firebase/dataconnect/QuerySubscriptionIntegrationTest.kt @@ -303,7 +303,7 @@ class QuerySubscriptionIntegrationTest : DataConnectIntegrationTestBase() { .toList() assertSoftly { - withClue("results.size") { results.size shouldBeInRange 1..2000 } + withClue("results.size") { results.size shouldBeInRange 1..5000 } results.forEachIndexed { i, result -> withClue("results[$i]") { result.shouldHavePersonWithName("NewName") } }