diff --git a/.github/actions/setup_test_action/action.yml b/.github/actions/setup_test_action/action.yml index ba5441480..bb6155dbb 100644 --- a/.github/actions/setup_test_action/action.yml +++ b/.github/actions/setup_test_action/action.yml @@ -5,7 +5,7 @@ runs: using: "composite" steps: - name: Set up JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: '17' diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index c2417991c..3c8d7f8ab 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -15,11 +15,11 @@ env: jobs: build: - runs-on: macos-13 + runs-on: macos-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: '17' @@ -59,9 +59,9 @@ jobs: documentation: runs-on: macos-13 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: '17' @@ -71,7 +71,7 @@ jobs: - name: Generate documentation run: ./gradlew dokkaHtmlMultiModule - name: Uploading build folder - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: artefact path: build/dokka/htmlMultiModule diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 8fa7d0956..e6ab58abe 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: '17' @@ -31,7 +31,7 @@ jobs: fail-fast: false matrix: ${{ fromJson(needs.jobEmulatorMatrixSetup.outputs.emulator_jobs_matrix) }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Enable KVM group perms run: | echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules @@ -39,33 +39,36 @@ jobs: sudo udevadm trigger --name-match=kvm - name: Setup test environment uses: ./.github/actions/setup_test_action + - name: Set Artifact Name + run: | + echo "ARCHIVE_KEY=$(echo ${{ matrix.gradle_tasks }} | cut -d: -f2)" >> $GITHUB_ENV - name: Apply Android licenses run: ./gradlew ciSdkManagerLicenses - name: Run Android Instrumented Tests run: ./gradlew ${{ matrix.gradle_tasks }} - name: Upload Android test artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() with: - name: "Android Test Report HTML" + name: Android ${{ env.ARCHIVE_KEY }} Test Report HTML path: "**/build/reports/androidTests/" - name: Upload Firebase Debug Log - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() with: - name: "Firebase Debug Log" + name: Android ${{ env.ARCHIVE_KEY }} Firebase Debug Log path: "**/firebase-debug.log" build-js: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup test environment uses: ./.github/actions/setup_test_action timeout-minutes: 10 - name: Run JS Tests run: ./gradlew cleanTest jsTest - name: Upload JS test artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() with: name: "JS Test Report HTML" @@ -74,17 +77,17 @@ jobs: **/build/reports/tests/jsBrowserTest/ **/build/reports/tests/jsNodeTest/ - name: Upload Firebase Debug Log - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() with: - name: "Firebase Debug Log" + name: "JS Firebase Debug Log" path: "**/firebase-debug.log" build-ios: runs-on: macos-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Cocoapods cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ~/.cocoapods @@ -95,16 +98,38 @@ jobs: - name: Setup test environment uses: ./.github/actions/setup_test_action - name: Run iOS Tests - run: ./gradlew cleanTest iosX64Test + run: ./gradlew cleanTest iosSimulatorArm64Test - name: Upload iOS test artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() with: name: "iOS Test Report HTML" - path: "**/build/reports/tests/iosX64Test/" + path: "**/build/reports/tests/iosSimulatorArm64Test/" - name: Upload Firebase Debug Log - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() with: - name: "Firebase Debug Log" + name: "iOS Firebase Debug Log" path: "**/firebase-debug.log" + build-jvm: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup test environment + uses: ./.github/actions/setup_test_action + timeout-minutes: 10 + - name: Run JVM Tests + run: ./gradlew cleanTest jvmTest + - name: Upload JVM test artifact + uses: actions/upload-artifact@v4 + if: failure() + with: + name: "JVM Test Report HTML" + path: | + **/build/reports/tests/jvmTest/ + - name: Upload Firebase Debug Log + uses: actions/upload-artifact@v4 + if: failure() + with: + name: "JVM Firebase Debug Log" + path: "**/firebase-debug.log" \ No newline at end of file diff --git a/.gitignore b/.gitignore index 4feaf670b..5891965c3 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ Firebase*.zip *.log /kotlin-js-store/ /kotlin-js-store/yarn.lock + +.kotlin/ diff --git a/firebase-analytics/build.gradle.kts b/firebase-analytics/build.gradle.kts index 26dec367f..9d8711cbf 100644 --- a/firebase-analytics/build.gradle.kts +++ b/firebase-analytics/build.gradle.kts @@ -1,3 +1,6 @@ +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree /* @@ -26,8 +29,8 @@ android { } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } testOptions.configureTestOptions() @@ -44,10 +47,20 @@ android { val supportIosTarget = project.property("skipIosTarget") != "true" kotlin { - + @OptIn(ExperimentalKotlinGradlePluginApi::class) + compilerOptions { + freeCompilerArgs.add("-Xexpect-actual-classes") + } targets.configureEach { compilations.configureEach { - kotlinOptions.freeCompilerArgs += "-Xexpect-actual-classes" + compileTaskProvider.configure { + compilerOptions { + if (this is KotlinJvmCompilerOptions) { + jvmTarget = JvmTarget.JVM_17 + } + freeCompilerArgs.add("-Xexpect-actual-classes") + } + } } } @@ -56,25 +69,9 @@ kotlin { instrumentedTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) unitTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) publishAllLibraryVariants() - compilations.configureEach { - kotlinOptions { - jvmTarget = "11" - } - } } - jvm { - compilations.getByName("main") { - kotlinOptions { - jvmTarget = "17" - } - } - compilations.getByName("test") { - kotlinOptions { - jvmTarget = "17" - } - } - } + jvm() if (supportIosTarget) { iosArm64() @@ -87,7 +84,7 @@ kotlin { } noPodspec() pod("FirebaseAnalytics") { - version = "10.25.0" + version = libs.versions.firebase.cocoapods.get() extraOpts += listOf("-compiler-option", "-fmodules") } } @@ -96,22 +93,18 @@ kotlin { js(IR) { useCommonJs() nodejs { - testTask( - Action { - useKarma { - useChromeHeadless() - } + testTask { + useKarma { + useChromeHeadless() } - ) + } } browser { - testTask( - Action { - useKarma { - useChromeHeadless() - } + testTask { + useKarma { + useChromeHeadless() } - ) + } } } @@ -154,6 +147,12 @@ if (project.property("firebase-analytics.skipIosTests") == "true") { } } +if (project.property("firebase-analytics.skipJvmTests") == "true") { + tasks.forEach { + if (it.name.contains("jvm", true) && it.name.contains("test", true)) { it.enabled = false } + } +} + if (project.property("firebase-analytics.skipJsTests") == "true") { tasks.forEach { if (it.name.contains("js", true) && it.name.contains("test", true)) { it.enabled = false } diff --git a/firebase-analytics/src/jvmMain/kotlin/dev/gitlive/firebase/analytics/analytics.jvm.kt b/firebase-analytics/src/jvmMain/kotlin/dev/gitlive/firebase/analytics/analytics.jvm.kt index 50fb3da39..ce0bae02a 100644 --- a/firebase-analytics/src/jvmMain/kotlin/dev/gitlive/firebase/analytics/analytics.jvm.kt +++ b/firebase-analytics/src/jvmMain/kotlin/dev/gitlive/firebase/analytics/analytics.jvm.kt @@ -8,7 +8,7 @@ actual val Firebase.analytics: FirebaseAnalytics get() = TODO("Not yet implemented") actual fun Firebase.analytics(app: FirebaseApp): FirebaseAnalytics { - TODO("Not yetimplemented") + TODO("Not yet implemented") } actual class FirebaseAnalytics { diff --git a/firebase-analytics/src/jvmTest/kotlin/dev/gitlive/firebase/analytics/analytics.kt b/firebase-analytics/src/jvmTest/kotlin/dev/gitlive/firebase/analytics/analytics.kt index d907efc70..10eb69e38 100644 --- a/firebase-analytics/src/jvmTest/kotlin/dev/gitlive/firebase/analytics/analytics.kt +++ b/firebase-analytics/src/jvmTest/kotlin/dev/gitlive/firebase/analytics/analytics.kt @@ -5,9 +5,11 @@ @file:JvmName("tests") package dev.gitlive.firebase.analytics +import dev.gitlive.firebase.testContext + actual val emulatorHost: String = "10.0.2.2" -actual val context: Any = Unit +actual val context: Any = testContext @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) actual annotation class IgnoreForAndroidUnitTest diff --git a/firebase-app/build.gradle.kts b/firebase-app/build.gradle.kts index 8300d70b0..d13e9341c 100644 --- a/firebase-app/build.gradle.kts +++ b/firebase-app/build.gradle.kts @@ -1,4 +1,6 @@ -import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree /* @@ -27,8 +29,8 @@ android { } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } testOptions.configureTestOptions() @@ -45,10 +47,20 @@ android { val supportIosTarget = project.property("skipIosTarget") != "true" kotlin { - + @OptIn(ExperimentalKotlinGradlePluginApi::class) + compilerOptions { + freeCompilerArgs.add("-Xexpect-actual-classes") + } targets.configureEach { compilations.configureEach { - kotlinOptions.freeCompilerArgs += "-Xexpect-actual-classes" + compileTaskProvider.configure { + compilerOptions { + if (this is KotlinJvmCompilerOptions) { + jvmTarget = JvmTarget.JVM_17 + } + freeCompilerArgs.add("-Xexpect-actual-classes") + } + } } } @@ -57,20 +69,9 @@ kotlin { instrumentedTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) unitTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) publishAllLibraryVariants() - compilations.configureEach { - kotlinOptions { - jvmTarget = "11" - } - } } - jvm { - compilations.getByName("main") { - kotlinOptions { - jvmTarget = "17" - } - } - } + jvm() if (supportIosTarget) { iosArm64() @@ -84,7 +85,7 @@ kotlin { } noPodspec() pod("FirebaseCore") { - version = "10.25.0" + version = libs.versions.firebase.cocoapods.get() } } } @@ -92,22 +93,18 @@ kotlin { js(IR) { useCommonJs() nodejs { - testTask( - Action { - useKarma { - useChromeHeadless() - } + testTask { + useKarma { + useChromeHeadless() } - ) + } } browser { - testTask( - Action { - useKarma { - useChromeHeadless() - } + testTask { + useKarma { + useChromeHeadless() } - ) + } } } @@ -153,6 +150,12 @@ if (project.property("firebase-app.skipIosTests") == "true") { } } +if (project.property("firebase-app.skipJvmTests") == "true") { + tasks.forEach { + if (it.name.contains("jvm", true) && it.name.contains("test", true)) { it.enabled = false } + } +} + if (project.property("firebase-app.skipJsTests") == "true") { tasks.forEach { if (it.name.contains("js", true) && it.name.contains("test", true)) { it.enabled = false } diff --git a/firebase-app/src/jvmTest/kotlin/dev/gitlive/firebase/firebase.kt b/firebase-app/src/jvmTest/kotlin/dev/gitlive/firebase/firebase.kt index 4bdd21eac..3ab2b6e7e 100644 --- a/firebase-app/src/jvmTest/kotlin/dev/gitlive/firebase/firebase.kt +++ b/firebase-app/src/jvmTest/kotlin/dev/gitlive/firebase/firebase.kt @@ -5,7 +5,7 @@ @file:JvmName("tests") package dev.gitlive.firebase -actual val context: Any = Unit +actual val context: Any = testContext @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) actual annotation class IgnoreForAndroidUnitTest diff --git a/firebase-auth/build.gradle.kts b/firebase-auth/build.gradle.kts index 4e897b973..ea20be920 100644 --- a/firebase-auth/build.gradle.kts +++ b/firebase-auth/build.gradle.kts @@ -1,4 +1,9 @@ +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetWithSimulatorTests +import org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeSimulatorTest /* * Copyright (c) 2020 GitLive Ltd. Use of this source code is governed by the Apache 2.0 license. @@ -26,8 +31,8 @@ android { } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } testOptions.configureTestOptions() @@ -44,10 +49,20 @@ android { val supportIosTarget = project.property("skipIosTarget") != "true" kotlin { - + @OptIn(ExperimentalKotlinGradlePluginApi::class) + compilerOptions { + freeCompilerArgs.add("-Xexpect-actual-classes") + } targets.configureEach { compilations.configureEach { - kotlinOptions.freeCompilerArgs += "-Xexpect-actual-classes" + compileTaskProvider.configure { + compilerOptions { + if (this is KotlinJvmCompilerOptions) { + jvmTarget = JvmTarget.JVM_17 + } + freeCompilerArgs.add("-Xexpect-actual-classes") + } + } } } @@ -56,17 +71,14 @@ kotlin { instrumentedTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) unitTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) publishAllLibraryVariants() - compilations.configureEach { - kotlinOptions { - jvmTarget = "11" - } - } } + jvm() + if (supportIosTarget) { iosArm64() - iosX64() - iosSimulatorArm64() + iosX64().enableKeychainForTests() + iosSimulatorArm64().enableKeychainForTests() cocoapods { ios.deploymentTarget = "12.0" framework { @@ -74,7 +86,7 @@ kotlin { } noPodspec() pod("FirebaseAuth") { - version = "10.25.0" + version = libs.versions.firebase.cocoapods.get() } } } @@ -82,29 +94,17 @@ kotlin { js(IR) { useCommonJs() nodejs { - testTask( - Action { - useKarma { - useChromeHeadless() - } + testTask { + useKarma { + useChromeHeadless() } - ) + } } browser { - testTask( - Action { - useKarma { - useChromeHeadless() - } + testTask { + useKarma { + useChromeHeadless() } - ) - } - } - - jvm { - compilations.getByName("main") { - kotlinOptions { - jvmTarget = "17" } } } @@ -150,12 +150,41 @@ if (project.property("firebase-auth.skipIosTests") == "true") { } } +if (project.property("firebase-auth.skipJvmTests") == "true") { + tasks.forEach { + if (it.name.contains("jvm", true) && it.name.contains("test", true)) { it.enabled = false } + } +} + if (project.property("firebase-auth.skipJsTests") == "true") { tasks.forEach { if (it.name.contains("js", true) && it.name.contains("test", true)) { it.enabled = false } } } +if (supportIosTarget) { + tasks.create("launchIosSimulator") { + commandLine("open", "-a", "Simulator") + } + + tasks.withType().configureEach { + dependsOn("launchIosSimulator") + standalone.set(false) + device.set("booted") + } +} + +fun KotlinNativeTargetWithSimulatorTests.enableKeychainForTests() { + testRuns.configureEach { + executionSource.binary.linkerOpts( + "-sectcreate", + "__TEXT", + "__entitlements", + file("$projectDir/src/commonTest/resources/entitlements.plist").absolutePath + ) + } +} + signing { val signingKey: String? by project val signingPassword: String? by project diff --git a/firebase-auth/src/androidMain/kotlin/dev/gitlive/firebase/auth/auth.kt b/firebase-auth/src/androidMain/kotlin/dev/gitlive/firebase/auth/auth.kt index 1ea49e666..d0484b643 100644 --- a/firebase-auth/src/androidMain/kotlin/dev/gitlive/firebase/auth/auth.kt +++ b/firebase-auth/src/androidMain/kotlin/dev/gitlive/firebase/auth/auth.kt @@ -57,6 +57,7 @@ actual class FirebaseAuth internal constructor(val android: com.google.firebase. actual suspend fun createUserWithEmailAndPassword(email: String, password: String) = AuthResult(android.createUserWithEmailAndPassword(email, password).await()) + @Suppress("DEPRECATION") actual suspend fun fetchSignInMethodsForEmail(email: String): List = android.fetchSignInMethodsForEmail(email).await().signInMethods.orEmpty() actual suspend fun sendPasswordResetEmail(email: String, actionCodeSettings: ActionCodeSettings?) { diff --git a/firebase-auth/src/androidMain/kotlin/dev/gitlive/firebase/auth/user.kt b/firebase-auth/src/androidMain/kotlin/dev/gitlive/firebase/auth/user.kt index ba7c7bb64..19499c6b9 100644 --- a/firebase-auth/src/androidMain/kotlin/dev/gitlive/firebase/auth/user.kt +++ b/firebase-auth/src/androidMain/kotlin/dev/gitlive/firebase/auth/user.kt @@ -43,13 +43,15 @@ actual class FirebaseUser internal constructor(val android: com.google.firebase. request.await() } actual suspend fun unlink(provider: String): FirebaseUser? = android.unlink(provider).await().user?.let { FirebaseUser(it) } + + @Suppress("DEPRECATION") actual suspend fun updateEmail(email: String) = android.updateEmail(email).await().run { Unit } actual suspend fun updatePassword(password: String) = android.updatePassword(password).await().run { Unit } actual suspend fun updatePhoneNumber(credential: PhoneAuthCredential) = android.updatePhoneNumber(credential.android).await().run { Unit } actual suspend fun updateProfile(displayName: String?, photoUrl: String?) { val request = UserProfileChangeRequest.Builder() - .apply { if(displayName !== UNCHANGED) setDisplayName(displayName) } - .apply { if(photoUrl !== UNCHANGED) setPhotoUri(photoUrl?.let { Uri.parse(it) }) } + .apply { setDisplayName(displayName) } + .apply { photoUri = photoUrl?.let { Uri.parse(it) } } .build() android.updateProfile(request).await() } diff --git a/firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/auth.kt b/firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/auth.kt index 8f6eb0c01..3f5a7b5a1 100644 --- a/firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/auth.kt +++ b/firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/auth.kt @@ -24,6 +24,7 @@ expect class FirebaseAuth { suspend fun checkActionCode(code: String): T suspend fun confirmPasswordReset(code: String, newPassword: String) suspend fun createUserWithEmailAndPassword(email: String, password: String): AuthResult + @Deprecated("Migrating off of this method is recommended as a security best-practice. Learn more in the Identity Platform documentation for [Email Enumeration Protection](https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection).") suspend fun fetchSignInMethodsForEmail(email: String): List suspend fun sendPasswordResetEmail(email: String, actionCodeSettings: ActionCodeSettings? = null) suspend fun sendSignInLinkToEmail(email: String, actionCodeSettings: ActionCodeSettings) diff --git a/firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/user.kt b/firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/user.kt index 92e9083b0..e7aa36217 100644 --- a/firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/user.kt +++ b/firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/user.kt @@ -4,9 +4,6 @@ package dev.gitlive.firebase.auth -//workaround for https://youtrack.jetbrains.com/issue/KT-48836 -internal val UNCHANGED = "" - expect class FirebaseUser { val uid: String val displayName: String? @@ -28,10 +25,11 @@ expect class FirebaseUser { suspend fun reauthenticateAndRetrieveData(credential: AuthCredential): AuthResult suspend fun sendEmailVerification(actionCodeSettings: ActionCodeSettings? = null) suspend fun unlink(provider: String): FirebaseUser? + @Deprecated("Use verifyBeforeUpdateEmail instead", replaceWith = ReplaceWith("verifyBeforeUpdateEmail(email)")) suspend fun updateEmail(email: String) suspend fun updatePassword(password: String) suspend fun updatePhoneNumber(credential: PhoneAuthCredential) - suspend fun updateProfile(displayName: String? = UNCHANGED, photoUrl: String? = UNCHANGED) + suspend fun updateProfile(displayName: String? = this.displayName, photoUrl: String? = this.photoURL) suspend fun verifyBeforeUpdateEmail(newEmail: String, actionCodeSettings: ActionCodeSettings? = null) } diff --git a/firebase-auth/src/commonTest/resources/entitlements.plist b/firebase-auth/src/commonTest/resources/entitlements.plist new file mode 100644 index 000000000..12a365278 --- /dev/null +++ b/firebase-auth/src/commonTest/resources/entitlements.plist @@ -0,0 +1,12 @@ + + + + + application-identifier + {PERSONAL_ID}.bundle.id + keychain-access-groups + + {PERSONAL_ID}.bundle.id + + + \ No newline at end of file diff --git a/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/user.kt b/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/user.kt index a765e10eb..1a15122c9 100644 --- a/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/user.kt +++ b/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/user.kt @@ -69,8 +69,8 @@ actual class FirebaseUser internal constructor(val ios: FIRUser) { actual suspend fun updatePhoneNumber(credential: PhoneAuthCredential) = ios.await { updatePhoneNumberCredential(credential.ios, it) }.run { Unit } actual suspend fun updateProfile(displayName: String?, photoUrl: String?) { val request = ios.profileChangeRequest() - .apply { if(displayName !== UNCHANGED) setDisplayName(displayName) } - .apply { if(photoUrl !== UNCHANGED) setPhotoURL(photoUrl?.let { NSURL.URLWithString(it) }) } + .apply { setDisplayName(displayName) } + .apply { setPhotoURL(photoUrl?.let { NSURL.URLWithString(it) }) } ios.await { request.commitChangesWithCompletion(it) } } actual suspend fun verifyBeforeUpdateEmail(newEmail: String, actionCodeSettings: ActionCodeSettings?) = ios.await { diff --git a/firebase-auth/src/jsMain/kotlin/dev/gitlive/firebase/auth/user.kt b/firebase-auth/src/jsMain/kotlin/dev/gitlive/firebase/auth/user.kt index d8b817a7e..2f1bccb8a 100644 --- a/firebase-auth/src/jsMain/kotlin/dev/gitlive/firebase/auth/user.kt +++ b/firebase-auth/src/jsMain/kotlin/dev/gitlive/firebase/auth/user.kt @@ -46,9 +46,9 @@ actual class FirebaseUser internal constructor(val js: User) { actual suspend fun updatePassword(password: String) = rethrow { updatePassword(js, password).await() } actual suspend fun updatePhoneNumber(credential: PhoneAuthCredential) = rethrow { updatePhoneNumber(js, credential.js).await() } actual suspend fun updateProfile(displayName: String?, photoUrl: String?): Unit = rethrow { - val request = listOfNotNull( - displayName.takeUnless { it === UNCHANGED }?.let { "displayName" to it }, - photoUrl.takeUnless { it === UNCHANGED }?.let { "photoURL" to it } + val request = listOf( + "displayName" to displayName, + "photoURL" to photoUrl, ) updateProfile(js, json(*request.toTypedArray())).await() } diff --git a/firebase-auth/src/jvmMain/kotlin/dev/gitlive/firebase/auth/user.kt b/firebase-auth/src/jvmMain/kotlin/dev/gitlive/firebase/auth/user.kt index ba7c7bb64..3a8ea3b92 100644 --- a/firebase-auth/src/jvmMain/kotlin/dev/gitlive/firebase/auth/user.kt +++ b/firebase-auth/src/jvmMain/kotlin/dev/gitlive/firebase/auth/user.kt @@ -48,8 +48,8 @@ actual class FirebaseUser internal constructor(val android: com.google.firebase. actual suspend fun updatePhoneNumber(credential: PhoneAuthCredential) = android.updatePhoneNumber(credential.android).await().run { Unit } actual suspend fun updateProfile(displayName: String?, photoUrl: String?) { val request = UserProfileChangeRequest.Builder() - .apply { if(displayName !== UNCHANGED) setDisplayName(displayName) } - .apply { if(photoUrl !== UNCHANGED) setPhotoUri(photoUrl?.let { Uri.parse(it) }) } + .apply { setDisplayName(displayName) } + .apply { setPhotoUri(photoUrl?.let { Uri.parse(it) }) } .build() android.updateProfile(request).await() } diff --git a/firebase-auth/src/jvmTest/kotlin/dev/gitlive/firebase/auth/auth.kt b/firebase-auth/src/jvmTest/kotlin/dev/gitlive/firebase/auth/auth.kt index d83d421fb..87a640e75 100644 --- a/firebase-auth/src/jvmTest/kotlin/dev/gitlive/firebase/auth/auth.kt +++ b/firebase-auth/src/jvmTest/kotlin/dev/gitlive/firebase/auth/auth.kt @@ -5,10 +5,11 @@ @file:JvmName("tests") package dev.gitlive.firebase.auth +import dev.gitlive.firebase.testContext actual val emulatorHost: String = "10.0.2.2" -actual val context: Any = Unit +actual val context: Any = testContext @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) actual annotation class IgnoreForAndroidUnitTest diff --git a/firebase-common-internal/build.gradle.kts b/firebase-common-internal/build.gradle.kts index 1f2f0d714..87ab100e9 100644 --- a/firebase-common-internal/build.gradle.kts +++ b/firebase-common-internal/build.gradle.kts @@ -1,3 +1,6 @@ +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree /* @@ -25,8 +28,8 @@ android { } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } testOptions.configureTestOptions() @@ -42,10 +45,20 @@ android { } kotlin { - + @OptIn(ExperimentalKotlinGradlePluginApi::class) + compilerOptions { + freeCompilerArgs.add("-Xexpect-actual-classes") + } targets.configureEach { compilations.configureEach { - kotlinOptions.freeCompilerArgs += "-Xexpect-actual-classes" + compileTaskProvider.configure { + compilerOptions { + if (this is KotlinJvmCompilerOptions) { + jvmTarget = JvmTarget.JVM_17 + } + freeCompilerArgs.add("-Xexpect-actual-classes") + } + } } } @@ -54,25 +67,9 @@ kotlin { instrumentedTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) unitTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) publishAllLibraryVariants() - compilations.configureEach { - kotlinOptions { - jvmTarget = "11" - } - } } - jvm { - compilations.getByName("main") { - kotlinOptions { - jvmTarget = "17" - } - } - compilations.getByName("test") { - kotlinOptions { - jvmTarget = "17" - } - } - } + jvm() val supportIosTarget = project.property("skipIosTarget") != "true" @@ -85,22 +82,18 @@ kotlin { js(IR) { useCommonJs() nodejs { - testTask( - Action { - useKarma { - useChromeHeadless() - } + testTask { + useKarma { + useChromeHeadless() } - ) + } } browser { - testTask( - Action { - useKarma { - useChromeHeadless() - } + testTask { + useKarma { + useChromeHeadless() } - ) + } } } @@ -137,7 +130,7 @@ kotlin { getByName("jsMain") { dependencies { - api(npm("firebase", "10.6.0")) + api(npm("firebase", "10.12.2")) } } @@ -160,6 +153,12 @@ if (project.property("firebase-common.skipIosTests") == "true") { } } +if (project.property("firebase-common.skipJvmTests") == "true") { + tasks.forEach { + if (it.name.contains("jvm", true) && it.name.contains("test", true)) { it.enabled = false } + } +} + if (project.property("firebase-common.skipJsTests") == "true") { tasks.forEach { if (it.name.contains("js", true) && it.name.contains("test", true)) { it.enabled = false } diff --git a/firebase-common-internal/src/commonMain/kotlin/dev/gitlive/firebase/internal/decoders.kt b/firebase-common-internal/src/commonMain/kotlin/dev/gitlive/firebase/internal/decoders.kt index ca9d0ed31..a673fb616 100644 --- a/firebase-common-internal/src/commonMain/kotlin/dev/gitlive/firebase/internal/decoders.kt +++ b/firebase-common-internal/src/commonMain/kotlin/dev/gitlive/firebase/internal/decoders.kt @@ -219,13 +219,13 @@ private fun decodeShort(value: Any?) = when(value) { private fun decodeBoolean(value: Any?) = when (value) { is Boolean -> value - is Number -> value != 0 + is Number -> value.toInt() != 0 is String -> value.toBoolean() else -> throw SerializationException("Expected $value to be boolean") } private fun decodeChar(value: Any?) = when(value) { - is Number -> value.toChar() + is Number -> value.toInt().toChar() is String -> value[0] else -> throw SerializationException("Expected $value to be char") } diff --git a/firebase-common-internal/src/commonMain/kotlin/dev/gitlive/firebase/internal/encoders.kt b/firebase-common-internal/src/commonMain/kotlin/dev/gitlive/firebase/internal/encoders.kt index 0015e3cfd..1caa20d46 100644 --- a/firebase-common-internal/src/commonMain/kotlin/dev/gitlive/firebase/internal/encoders.kt +++ b/firebase-common-internal/src/commonMain/kotlin/dev/gitlive/firebase/internal/encoders.kt @@ -22,7 +22,7 @@ inline fun encode(strategy: SerializationStrategy, value: T, buildSetting encode(strategy, value, EncodeSettingsImpl.Builder().apply(buildSettings).buildEncodeSettings()) @PublishedApi -internal inline fun encode(strategy: SerializationStrategy, value: T, encodeSettings: EncodeSettings): Any? = +internal fun encode(strategy: SerializationStrategy, value: T, encodeSettings: EncodeSettings): Any? = FirebaseEncoder(encodeSettings).apply { encodeSerializableValue(strategy, value) }.value @Deprecated("Deprecated. Use builder instead", replaceWith = ReplaceWith("encode(value) { this.encodeDefaults = shouldEncodeElementDefault }")) diff --git a/firebase-common-internal/src/jsMain/kotlin/dev/gitlive/firebase/internal/EncodedObject.kt b/firebase-common-internal/src/jsMain/kotlin/dev/gitlive/firebase/internal/EncodedObject.kt index 47f3aad5e..6c2724233 100644 --- a/firebase-common-internal/src/jsMain/kotlin/dev/gitlive/firebase/internal/EncodedObject.kt +++ b/firebase-common-internal/src/jsMain/kotlin/dev/gitlive/firebase/internal/EncodedObject.kt @@ -13,7 +13,7 @@ internal actual fun Any.asNativeMap(): Map<*, *>? { is String -> null is Map<*, *> -> { if (keys.all { it is String }) { - this as Json + json(*mapKeys { (key, _) -> key as String }.toList().toTypedArray()) } else { null } @@ -21,12 +21,13 @@ internal actual fun Any.asNativeMap(): Map<*, *>? { is Collection<*> -> null is Array<*> -> null else -> { + @Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE") this as Json } } ?: return null val mutableMap = mutableMapOf() for (key in js("Object").keys(json)) { - mutableMap[key] = json[key] + mutableMap[key as String] = json[key as String] } return mutableMap.toMap() } diff --git a/firebase-common/build.gradle.kts b/firebase-common/build.gradle.kts index 5531ada19..99a2fea11 100644 --- a/firebase-common/build.gradle.kts +++ b/firebase-common/build.gradle.kts @@ -1,3 +1,6 @@ +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree /* @@ -25,8 +28,8 @@ android { } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } testOptions.configureTestOptions() @@ -42,10 +45,20 @@ android { } kotlin { - + @OptIn(ExperimentalKotlinGradlePluginApi::class) + compilerOptions { + freeCompilerArgs.add("-Xexpect-actual-classes") + } targets.configureEach { compilations.configureEach { - kotlinOptions.freeCompilerArgs += "-Xexpect-actual-classes" + compileTaskProvider.configure { + compilerOptions { + if (this is KotlinJvmCompilerOptions) { + jvmTarget = JvmTarget.JVM_17 + } + freeCompilerArgs.add("-Xexpect-actual-classes") + } + } } } @@ -54,25 +67,9 @@ kotlin { instrumentedTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) unitTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) publishAllLibraryVariants() - compilations.configureEach { - kotlinOptions { - jvmTarget = "11" - } - } } - jvm { - compilations.getByName("main") { - kotlinOptions { - jvmTarget = "17" - } - } - compilations.getByName("test") { - kotlinOptions { - jvmTarget = "17" - } - } - } + jvm() val supportIosTarget = project.property("skipIosTarget") != "true" @@ -85,22 +82,18 @@ kotlin { js(IR) { useCommonJs() nodejs { - testTask( - Action { - useKarma { - useChromeHeadless() - } + testTask { + useKarma { + useChromeHeadless() } - ) + } } browser { - testTask( - Action { - useKarma { - useChromeHeadless() - } + testTask { + useKarma { + useChromeHeadless() } - ) + } } } @@ -136,7 +129,7 @@ kotlin { getByName("jsMain") { dependencies { - api(npm("firebase", "10.6.0")) + api(npm("firebase", "10.12.2")) } } @@ -159,6 +152,12 @@ if (project.property("firebase-common.skipIosTests") == "true") { } } +if (project.property("firebase-common.skipJvmTests") == "true") { + tasks.forEach { + if (it.name.contains("jvm", true) && it.name.contains("test", true)) { it.enabled = false } + } +} + if (project.property("firebase-common.skipJsTests") == "true") { tasks.forEach { if (it.name.contains("js", true) && it.name.contains("test", true)) { it.enabled = false } diff --git a/firebase-common/package.json b/firebase-common/package.json index 5723b5918..76a1013a3 100644 --- a/firebase-common/package.json +++ b/firebase-common/package.json @@ -1,6 +1,6 @@ { "name": "@gitlive/firebase-common", - "version": "1.12.0", + "version": "1.13.0", "description": "Wrapper around firebase for usage in Kotlin Multiplatform projects", "main": "firebase-common.js", "scripts": { diff --git a/firebase-config/build.gradle.kts b/firebase-config/build.gradle.kts index e9e896237..a5d672835 100644 --- a/firebase-config/build.gradle.kts +++ b/firebase-config/build.gradle.kts @@ -1,3 +1,6 @@ +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree /* @@ -26,8 +29,8 @@ android { } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } testOptions.configureTestOptions() @@ -44,10 +47,20 @@ android { val supportIosTarget = project.property("skipIosTarget") != "true" kotlin { - + @OptIn(ExperimentalKotlinGradlePluginApi::class) + compilerOptions { + freeCompilerArgs.add("-Xexpect-actual-classes") + } targets.configureEach { compilations.configureEach { - kotlinOptions.freeCompilerArgs += "-Xexpect-actual-classes" + compileTaskProvider.configure { + compilerOptions { + if (this is KotlinJvmCompilerOptions) { + jvmTarget = JvmTarget.JVM_17 + } + freeCompilerArgs.add("-Xexpect-actual-classes") + } + } } } @@ -56,25 +69,9 @@ kotlin { instrumentedTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) unitTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) publishAllLibraryVariants() - compilations.configureEach { - kotlinOptions { - jvmTarget = "11" - } - } } - jvm { - compilations.getByName("main") { - kotlinOptions { - jvmTarget = "17" - } - } - compilations.getByName("test") { - kotlinOptions { - jvmTarget = "17" - } - } - } + jvm() if (supportIosTarget) { iosArm64() @@ -87,7 +84,7 @@ kotlin { } noPodspec() pod("FirebaseRemoteConfig") { - version = "10.25.0" + version = libs.versions.firebase.cocoapods.get() } } } @@ -95,13 +92,11 @@ kotlin { js(IR) { useCommonJs() browser { - testTask( - Action { - useKarma { - useChromeHeadless() - } + testTask { + useKarma { + useChromeHeadless() } - ) + } } } @@ -150,6 +145,12 @@ if (project.property("firebase-config.skipIosTests") == "true") { } } +if (project.property("firebase-config.skipJvmTests") == "true") { + tasks.forEach { + if (it.name.contains("jvm", true) && it.name.contains("test", true)) { it.enabled = false } + } +} + if (project.property("firebase-config.skipJsTests") == "true") { tasks.forEach { if (it.name.contains("js", true) && it.name.contains("test", true)) { it.enabled = false } diff --git a/firebase-config/src/jvmTest/kotlin/dev/gitlive/firebase/remoteconfig/RemoteConfig.kt b/firebase-config/src/jvmTest/kotlin/dev/gitlive/firebase/remoteconfig/RemoteConfig.kt index 5a0333346..d03ab2fb6 100644 --- a/firebase-config/src/jvmTest/kotlin/dev/gitlive/firebase/remoteconfig/RemoteConfig.kt +++ b/firebase-config/src/jvmTest/kotlin/dev/gitlive/firebase/remoteconfig/RemoteConfig.kt @@ -5,7 +5,9 @@ @file:JvmName("tests") package dev.gitlive.firebase.remoteconfig -actual val context: Any = Unit +import dev.gitlive.firebase.testContext + +actual val context: Any = testContext @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) actual annotation class IgnoreForAndroidUnitTest diff --git a/firebase-crashlytics/build.gradle.kts b/firebase-crashlytics/build.gradle.kts index 6a3e503a6..692207800 100644 --- a/firebase-crashlytics/build.gradle.kts +++ b/firebase-crashlytics/build.gradle.kts @@ -1,3 +1,6 @@ +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree /* @@ -27,8 +30,8 @@ android { } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } testOptions.configureTestOptions() @@ -45,10 +48,20 @@ android { val supportIosTarget = project.property("skipIosTarget") != "true" kotlin { - + @OptIn(ExperimentalKotlinGradlePluginApi::class) + compilerOptions { + freeCompilerArgs.add("-Xexpect-actual-classes") + } targets.configureEach { compilations.configureEach { - kotlinOptions.freeCompilerArgs += "-Xexpect-actual-classes" + compileTaskProvider.configure { + compilerOptions { + if (this is KotlinJvmCompilerOptions) { + jvmTarget = JvmTarget.JVM_17 + } + freeCompilerArgs.add("-Xexpect-actual-classes") + } + } } } @@ -57,25 +70,9 @@ kotlin { instrumentedTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) unitTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) publishAllLibraryVariants() - compilations.configureEach { - kotlinOptions { - jvmTarget = "11" - } - } } -// jvm { -// compilations.getByName("main") { -// kotlinOptions { -// jvmTarget = "17" -// } -// } -// compilations.getByName("test") { -// kotlinOptions { -// jvmTarget = "17" -// } -// } -// } + // jvm() if (supportIosTarget) { iosArm64() @@ -88,7 +85,7 @@ kotlin { } noPodspec() pod("FirebaseCrashlytics") { - version = "10.25.0" + version = libs.versions.firebase.cocoapods.get() extraOpts += listOf("-compiler-option", "-fmodules") } } diff --git a/firebase-crashlytics/src/commonTest/kotlin/dev/gitlive/firebase/crashlytics/crashlytics.kt b/firebase-crashlytics/src/commonTest/kotlin/dev/gitlive/firebase/crashlytics/crashlytics.kt index 8c1933846..958f03ac3 100644 --- a/firebase-crashlytics/src/commonTest/kotlin/dev/gitlive/firebase/crashlytics/crashlytics.kt +++ b/firebase-crashlytics/src/commonTest/kotlin/dev/gitlive/firebase/crashlytics/crashlytics.kt @@ -10,10 +10,12 @@ import dev.gitlive.firebase.apps import dev.gitlive.firebase.initialize import dev.gitlive.firebase.runBlockingTest import dev.gitlive.firebase.runTest +import kotlinx.coroutines.delay import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertFalse +import kotlin.time.Duration.Companion.seconds expect val emulatorHost: String expect val context: Any @@ -51,37 +53,57 @@ class FirebaseCrashlyticsTest { @Test fun testRecordException() = runTest { crashlytics.recordException(Exception("Test Exception")) + + // Delay to ensure Crashlytics completes + delay(1.seconds) } @Test fun testLog() = runTest { crashlytics.log("Test Log") + + // Delay to ensure Crashlytics completes + delay(1.seconds) } @Test fun testSetUserId() = runTest { crashlytics.setUserId("Test User Id") + // Delay to ensure Crashlytics completes + delay(1.seconds) } @Test fun testSendUnsentReports() = runTest { crashlytics.sendUnsentReports() + + // Delay to ensure Crashlytics completes + delay(1.seconds) } @Test fun testDeleteUnsentReports() = runTest { crashlytics.deleteUnsentReports() + + // Delay to ensure Crashlytics completes + delay(1.seconds) } @Test fun testDidCrashOnPreviousExecution() = runTest { val didCrash = crashlytics.didCrashOnPreviousExecution() assertFalse { didCrash } + + // Delay to ensure Crashlytics completes + delay(1.seconds) } @Test fun testSetCrashlyticsCollectionEnabled() = runTest { crashlytics.setCrashlyticsCollectionEnabled(true) + + // Delay to ensure Crashlytics completes + delay(1.seconds) } } diff --git a/firebase-database/build.gradle.kts b/firebase-database/build.gradle.kts index 2c67ef315..0bba45ce1 100644 --- a/firebase-database/build.gradle.kts +++ b/firebase-database/build.gradle.kts @@ -1,3 +1,6 @@ +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree /* @@ -32,8 +35,8 @@ android { } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } testOptions.configureTestOptions() @@ -50,10 +53,20 @@ android { val supportIosTarget = project.property("skipIosTarget") != "true" kotlin { - + @OptIn(ExperimentalKotlinGradlePluginApi::class) + compilerOptions { + freeCompilerArgs.add("-Xexpect-actual-classes") + } targets.configureEach { compilations.configureEach { - kotlinOptions.freeCompilerArgs += "-Xexpect-actual-classes" + compileTaskProvider.configure { + compilerOptions { + if (this is KotlinJvmCompilerOptions) { + jvmTarget = JvmTarget.JVM_17 + } + freeCompilerArgs.add("-Xexpect-actual-classes") + } + } } } @@ -62,25 +75,9 @@ kotlin { instrumentedTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) unitTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) publishAllLibraryVariants() - compilations.configureEach { - kotlinOptions { - jvmTarget = "11" - } - } } - jvm { - compilations.getByName("main") { - kotlinOptions { - jvmTarget = "17" - } - } - compilations.getByName("test") { - kotlinOptions { - jvmTarget = "17" - } - } - } + jvm() if (supportIosTarget) { iosArm64() @@ -93,7 +90,7 @@ kotlin { } noPodspec() pod("FirebaseDatabase") { - version = "10.25.0" + version = libs.versions.firebase.cocoapods.get() } } } @@ -101,22 +98,18 @@ kotlin { js(IR) { useCommonJs() nodejs { - testTask( - Action { - useKarma { - useChromeHeadless() - } + testTask { + useKarma { + useChromeHeadless() } - ) + } } browser { - testTask( - Action { - useKarma { - useChromeHeadless() - } + testTask { + useKarma { + useChromeHeadless() } - ) + } } } @@ -170,6 +163,12 @@ if (project.property("firebase-database.skipIosTests") == "true") { } } +if (project.property("firebase-database.skipJvmTests") == "true") { + tasks.forEach { + if (it.name.contains("jvm", true) && it.name.contains("test", true)) { it.enabled = false } + } +} + if (project.property("firebase-database.skipJsTests") == "true") { tasks.forEach { if (it.name.contains("js", true) && it.name.contains("test", true)) { it.enabled = false } diff --git a/firebase-database/src/jvmTest/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/jvmTest/kotlin/dev/gitlive/firebase/database/database.kt index 1ddad5625..467e76ad4 100644 --- a/firebase-database/src/jvmTest/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/jvmTest/kotlin/dev/gitlive/firebase/database/database.kt @@ -5,9 +5,13 @@ @file:JvmName("tests") package dev.gitlive.firebase.database -actual val emulatorHost: String = "10.0.2.2" +import dev.gitlive.firebase.testContext -actual val context: Any = Unit +actual val emulatorHost: String = "localhost" + +actual val context: Any = testContext @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) actual annotation class IgnoreForAndroidUnitTest +@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) +actual annotation class IgnoreForAndroidTest diff --git a/firebase-firestore/build.gradle.kts b/firebase-firestore/build.gradle.kts index 1f01fb95d..5d7c5a714 100644 --- a/firebase-firestore/build.gradle.kts +++ b/firebase-firestore/build.gradle.kts @@ -1,3 +1,6 @@ +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree /* @@ -28,8 +31,8 @@ android { } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } testOptions.configureTestOptions() @@ -47,10 +50,20 @@ android { val supportIosTarget = project.property("skipIosTarget") != "true" kotlin { - + @OptIn(ExperimentalKotlinGradlePluginApi::class) + compilerOptions { + freeCompilerArgs.add("-Xexpect-actual-classes") + } targets.configureEach { compilations.configureEach { - kotlinOptions.freeCompilerArgs += "-Xexpect-actual-classes" + compileTaskProvider.configure { + compilerOptions { + if (this is KotlinJvmCompilerOptions) { + jvmTarget = JvmTarget.JVM_17 + } + freeCompilerArgs.add("-Xexpect-actual-classes") + } + } } } @@ -59,25 +72,9 @@ kotlin { instrumentedTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) unitTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) publishAllLibraryVariants() - compilations.configureEach { - kotlinOptions { - jvmTarget = "11" - } - } } - jvm { - compilations.getByName("main") { - kotlinOptions { - jvmTarget = "17" - } - } - compilations.getByName("test") { - kotlinOptions { - jvmTarget = "17" - } - } - } + jvm() if (supportIosTarget) { iosArm64() @@ -92,10 +89,10 @@ kotlin { // As of Firebase 10.17 Firestore has moved all ObjC headers to FirebaseFirestoreInternal and the kotlin cocoapods plugin does not handle this well // Adding it manually seems to resolve the issue pod("FirebaseFirestoreInternal") { - version = "10.23.0" + version = libs.versions.firebase.cocoapods.get() } pod("FirebaseFirestore") { - version = "10.23.0" + version = libs.versions.firebase.cocoapods.get() extraOpts += listOf("-compiler-option", "-fmodules") useInteropBindingFrom("FirebaseFirestoreInternal") } @@ -105,30 +102,26 @@ kotlin { js(IR) { useCommonJs() nodejs { - testTask( - Action { - useKarma { - useChromeHeadless() - // Explicitly specify Mocha here since it seems to be throwing random errors otherwise - useMocha { - timeout = "180s" - } + testTask { + useKarma { + useChromeHeadless() + // Explicitly specify Mocha here since it seems to be throwing random errors otherwise + useMocha { + timeout = "180s" } } - ) + } } browser { - testTask( - Action { - useKarma { - useChromeHeadless() - // Explicitly specify Mocha here since it seems to be throwing random errors otherwise - useMocha { - timeout = "180s" - } + testTask { + useKarma { + useChromeHeadless() + // Explicitly specify Mocha here since it seems to be throwing random errors otherwise + useMocha { + timeout = "180s" } } - ) + } } } @@ -181,6 +174,12 @@ if (project.property("firebase-firestore.skipIosTests") == "true") { } } +if (project.property("firebase-firestore.skipJvmTests") == "true") { + tasks.forEach { + if (it.name.contains("jvm", true) && it.name.contains("test", true)) { it.enabled = false } + } +} + if (project.property("firebase-firestore.skipJsTests") == "true") { tasks.forEach { if (it.name.contains("js", true) && it.name.contains("test", true)) { it.enabled = false } diff --git a/firebase-firestore/src/androidInstrumentedTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/androidInstrumentedTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 8c6035f28..143c1f350 100644 --- a/firebase-firestore/src/androidInstrumentedTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/androidInstrumentedTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -11,5 +11,6 @@ actual val emulatorHost: String = "10.0.2.2" actual val context: Any = InstrumentationRegistry.getInstrumentation().targetContext +@Suppress("UNCHECKED_CAST") actual fun encodedAsMap(encoded: Any?): Map = encoded as Map actual fun Map.asEncoded(): Any = this diff --git a/firebase-firestore/src/androidUnitTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/androidUnitTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 65d1a0bb7..a7a33ba85 100644 --- a/firebase-firestore/src/androidUnitTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/androidUnitTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -9,5 +9,6 @@ actual val emulatorHost: String = "10.0.2.2" actual val context: Any = "" +@Suppress("UNCHECKED_CAST") actual fun encodedAsMap(encoded: Any?): Map = encoded as Map actual fun Map.asEncoded(): Any = this diff --git a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 06aecd9a1..c6cce33b9 100644 --- a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -154,7 +154,7 @@ actual class DocumentChange(val js: JsDocumentChange) { actual val oldIndex: Int get() = js.oldIndex actual val type: ChangeType - get() = ChangeType.values().first { it.jsString == js.type } + get() = ChangeType.entries.first { it.jsString == js.type } } actual data class NativeDocumentSnapshot(val js: JsDocumentSnapshot) diff --git a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeFirebaseFirestoreWrapper.kt b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeFirebaseFirestoreWrapper.kt index 4c186869d..d56860720 100644 --- a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeFirebaseFirestoreWrapper.kt +++ b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeFirebaseFirestoreWrapper.kt @@ -15,6 +15,7 @@ import dev.gitlive.firebase.firestore.externals.setLogLevel import dev.gitlive.firebase.firestore.externals.writeBatch import dev.gitlive.firebase.firestore.firestoreSettings import dev.gitlive.firebase.firestore.rethrow +import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.await import kotlinx.coroutines.promise @@ -88,6 +89,7 @@ internal actual class NativeFirebaseFirestoreWrapper internal constructor( actual fun setLoggingEnabled(loggingEnabled: Boolean) = rethrow { setLogLevel(if (loggingEnabled) "error" else "silent") } + @OptIn(DelicateCoroutinesApi::class) actual suspend fun runTransaction(func: suspend NativeTransaction.() -> T) = rethrow { dev.gitlive.firebase.firestore.externals.runTransaction( js, diff --git a/firebase-firestore/src/jvmTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/jvmTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt new file mode 100644 index 000000000..742bd4e9b --- /dev/null +++ b/firebase-firestore/src/jvmTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -0,0 +1,11 @@ +package dev.gitlive.firebase.firestore + +import dev.gitlive.firebase.testContext + +actual val emulatorHost: String = "localhost" + +actual val context: Any = testContext + +@Suppress("UNCHECKED_CAST") +actual fun encodedAsMap(encoded: Any?): Map = encoded as Map +actual fun Map.asEncoded(): Any = this diff --git a/firebase-functions/build.gradle.kts b/firebase-functions/build.gradle.kts index b148115c2..9a6e33f97 100644 --- a/firebase-functions/build.gradle.kts +++ b/firebase-functions/build.gradle.kts @@ -1,3 +1,6 @@ +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree /* @@ -26,8 +29,8 @@ android { } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } testOptions.configureTestOptions() @@ -44,10 +47,20 @@ android { val supportIosTarget = project.property("skipIosTarget") != "true" kotlin { - + @OptIn(ExperimentalKotlinGradlePluginApi::class) + compilerOptions { + freeCompilerArgs.add("-Xexpect-actual-classes") + } targets.configureEach { compilations.configureEach { - kotlinOptions.freeCompilerArgs += "-Xexpect-actual-classes" + compileTaskProvider.configure { + compilerOptions { + if (this is KotlinJvmCompilerOptions) { + jvmTarget = JvmTarget.JVM_17 + } + freeCompilerArgs.add("-Xexpect-actual-classes") + } + } } } @@ -56,13 +69,10 @@ kotlin { instrumentedTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) unitTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) publishAllLibraryVariants() - compilations.configureEach { - kotlinOptions { - jvmTarget = "11" - } - } } + jvm() + if (supportIosTarget) { iosArm64() iosX64() @@ -74,7 +84,7 @@ kotlin { } noPodspec() pod("FirebaseFunctions") { - version = "10.25.0" + version = libs.versions.firebase.cocoapods.get() extraOpts += listOf("-compiler-option", "-fmodules") } } @@ -83,29 +93,17 @@ kotlin { js(IR) { useCommonJs() nodejs { - testTask( - Action { - useKarma { - useChromeHeadless() - } + testTask { + useKarma { + useChromeHeadless() } - ) + } } browser { - testTask( - Action { - useKarma { - useChromeHeadless() - } + testTask { + useKarma { + useChromeHeadless() } - ) - } - } - - jvm { - compilations.getByName("main") { - kotlinOptions { - jvmTarget = "17" } } } @@ -156,6 +154,12 @@ if (project.property("firebase-functions.skipIosTests") == "true") { } } +if (project.property("firebase-functions.skipJvmTests") == "true") { + tasks.forEach { + if (it.name.contains("jvm", true) && it.name.contains("test", true)) { it.enabled = false } + } +} + if (project.property("firebase-functions.skipJsTests") == "true") { tasks.forEach { if (it.name.contains("js", true) && it.name.contains("test", true)) { it.enabled = false } diff --git a/firebase-installations/build.gradle.kts b/firebase-installations/build.gradle.kts index c731db721..c19404e1c 100644 --- a/firebase-installations/build.gradle.kts +++ b/firebase-installations/build.gradle.kts @@ -1,3 +1,6 @@ +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree /* @@ -26,8 +29,8 @@ android { } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } testOptions.configureTestOptions() @@ -44,10 +47,20 @@ android { val supportIosTarget = project.property("skipIosTarget") != "true" kotlin { - + @OptIn(ExperimentalKotlinGradlePluginApi::class) + compilerOptions { + freeCompilerArgs.add("-Xexpect-actual-classes") + } targets.configureEach { compilations.configureEach { - kotlinOptions.freeCompilerArgs += "-Xexpect-actual-classes" + compileTaskProvider.configure { + compilerOptions { + if (this is KotlinJvmCompilerOptions) { + jvmTarget = JvmTarget.JVM_17 + } + freeCompilerArgs.add("-Xexpect-actual-classes") + } + } } } @@ -56,25 +69,9 @@ kotlin { instrumentedTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) unitTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) publishAllLibraryVariants() - compilations.configureEach { - kotlinOptions { - jvmTarget = "11" - } - } } - jvm { - compilations.getByName("main") { - kotlinOptions { - jvmTarget = "17" - } - } - compilations.getByName("test") { - kotlinOptions { - jvmTarget = "17" - } - } - } + jvm() if (supportIosTarget) { iosArm64() @@ -87,7 +84,7 @@ kotlin { } noPodspec() pod("FirebaseInstallations") { - version = "10.25.0" + version = libs.versions.firebase.cocoapods.get() } } } @@ -95,22 +92,18 @@ kotlin { js(IR) { useCommonJs() nodejs { - testTask( - Action { - useKarma { - useChromeHeadless() - } + testTask { + useKarma { + useChromeHeadless() } - ) + } } browser { - testTask( - Action { - useKarma { - useChromeHeadless() - } + testTask { + useKarma { + useChromeHeadless() } - ) + } } } @@ -157,6 +150,12 @@ if (project.property("firebase-installations.skipIosTests") == "true") { } } +if (project.property("firebase-installations.skipJvmTests") == "true") { + tasks.forEach { + if (it.name.contains("jvm", true) && it.name.contains("test", true)) { it.enabled = false } + } +} + if (project.property("firebase-installations.skipJsTests") == "true") { tasks.forEach { if (it.name.contains("js", true) && it.name.contains("test", true)) { it.enabled = false } diff --git a/firebase-messaging/build.gradle.kts b/firebase-messaging/build.gradle.kts index 2257ba918..48e5f5d27 100644 --- a/firebase-messaging/build.gradle.kts +++ b/firebase-messaging/build.gradle.kts @@ -1,3 +1,6 @@ +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree /* @@ -26,8 +29,8 @@ android { } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } testOptions.configureTestOptions() @@ -44,10 +47,20 @@ android { val supportIosTarget = project.property("skipIosTarget") != "true" kotlin { - + @OptIn(ExperimentalKotlinGradlePluginApi::class) + compilerOptions { + freeCompilerArgs.add("-Xexpect-actual-classes") + } targets.configureEach { compilations.configureEach { - kotlinOptions.freeCompilerArgs += "-Xexpect-actual-classes" + compileTaskProvider.configure { + compilerOptions { + if (this is KotlinJvmCompilerOptions) { + jvmTarget = JvmTarget.JVM_17 + } + freeCompilerArgs.add("-Xexpect-actual-classes") + } + } } } @@ -56,13 +69,10 @@ kotlin { instrumentedTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) unitTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) publishAllLibraryVariants() - compilations.configureEach { - kotlinOptions { - jvmTarget = "11" - } - } } + jvm() + if (supportIosTarget) { iosArm64() iosX64() @@ -82,34 +92,17 @@ kotlin { js(IR) { useCommonJs() nodejs { - testTask( - Action { - useKarma { - useChromeHeadless() - } + testTask { + useKarma { + useChromeHeadless() } - ) + } } browser { - testTask( - Action { - useKarma { - useChromeHeadless() - } + testTask { + useKarma { + useChromeHeadless() } - ) - } - } - - jvm { - compilations.getByName("main") { - kotlinOptions { - jvmTarget = "17" - } - } - compilations.getByName("test") { - kotlinOptions { - jvmTarget = "17" } } } @@ -155,6 +148,12 @@ if (project.property("firebase-messaging.skipIosTests") == "true") { } } +if (project.property("firebase-messaging.skipJvmTests") == "true") { + tasks.forEach { + if (it.name.contains("jvm", true) && it.name.contains("test", true)) { it.enabled = false } + } +} + if (project.property("firebase-messaging.skipJsTests") == "true") { tasks.forEach { if (it.name.contains("js", true) && it.name.contains("test", true)) { it.enabled = false } diff --git a/firebase-perf/build.gradle.kts b/firebase-perf/build.gradle.kts index 874041c85..f23237d72 100644 --- a/firebase-perf/build.gradle.kts +++ b/firebase-perf/build.gradle.kts @@ -1,3 +1,6 @@ +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree /* @@ -27,8 +30,8 @@ android { } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } testOptions.configureTestOptions() @@ -45,10 +48,20 @@ android { val supportIosTarget = project.property("skipIosTarget") != "true" kotlin { - + @OptIn(ExperimentalKotlinGradlePluginApi::class) + compilerOptions { + freeCompilerArgs.add("-Xexpect-actual-classes") + } targets.configureEach { compilations.configureEach { - kotlinOptions.freeCompilerArgs += "-Xexpect-actual-classes" + compileTaskProvider.configure { + compilerOptions { + if (this is KotlinJvmCompilerOptions) { + jvmTarget = JvmTarget.JVM_17 + } + freeCompilerArgs.add("-Xexpect-actual-classes") + } + } } } @@ -57,25 +70,9 @@ kotlin { instrumentedTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) unitTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) publishAllLibraryVariants() - compilations.configureEach { - kotlinOptions { - jvmTarget = "11" - } - } } - jvm { - compilations.getByName("main") { - kotlinOptions { - jvmTarget = "17" - } - } - compilations.getByName("test") { - kotlinOptions { - jvmTarget = "17" - } - } - } + jvm() if (supportIosTarget) { iosArm64() @@ -88,7 +85,7 @@ kotlin { } noPodspec() pod("FirebasePerformance") { - version = "10.25.0" + version = libs.versions.firebase.cocoapods.get() } } } @@ -96,13 +93,11 @@ kotlin { js(IR) { useCommonJs() browser { - testTask( - Action { - useKarma { - useChromeHeadless() - } + testTask { + useKarma { + useChromeHeadless() } - ) + } } } @@ -150,6 +145,12 @@ if (project.property("firebase-perf.skipIosTests") == "true") { } } +if (project.property("firebase-perf.skipJvmTests") == "true") { + tasks.forEach { + if (it.name.contains("jvm", true) && it.name.contains("test", true)) { it.enabled = false } + } +} + if (project.property("firebase-perf.skipJsTests") == "true") { tasks.forEach { if (it.name.contains("js", true) && it.name.contains("test", true)) { it.enabled = false } diff --git a/firebase-perf/src/jvmTest/kotlin/dev/gitlive/firebase/perf/performance.kt b/firebase-perf/src/jvmTest/kotlin/dev/gitlive/firebase/perf/performance.kt index f8134eecb..0985f3b1f 100644 --- a/firebase-perf/src/jvmTest/kotlin/dev/gitlive/firebase/perf/performance.kt +++ b/firebase-perf/src/jvmTest/kotlin/dev/gitlive/firebase/perf/performance.kt @@ -5,9 +5,11 @@ @file:JvmName("tests") package dev.gitlive.firebase.perf -actual val emulatorHost: String = "10.0.2.2" +import dev.gitlive.firebase.testContext -actual val context: Any = Unit +actual val emulatorHost: String = "localhost" + +actual val context: Any = testContext @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) actual annotation class IgnoreForAndroidUnitTest diff --git a/firebase-storage/build.gradle.kts b/firebase-storage/build.gradle.kts index a8048aa14..2ae659e33 100644 --- a/firebase-storage/build.gradle.kts +++ b/firebase-storage/build.gradle.kts @@ -1,3 +1,6 @@ +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree /* @@ -26,8 +29,8 @@ android { } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } testOptions.configureTestOptions() @@ -44,10 +47,20 @@ android { val supportIosTarget = project.property("skipIosTarget") != "true" kotlin { - + @OptIn(ExperimentalKotlinGradlePluginApi::class) + compilerOptions { + freeCompilerArgs.add("-Xexpect-actual-classes") + } targets.configureEach { compilations.configureEach { - kotlinOptions.freeCompilerArgs += "-Xexpect-actual-classes" + compileTaskProvider.configure { + compilerOptions { + if (this is KotlinJvmCompilerOptions) { + jvmTarget = JvmTarget.JVM_17 + } + freeCompilerArgs.add("-Xexpect-actual-classes") + } + } } } @@ -56,25 +69,9 @@ kotlin { instrumentedTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) unitTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) publishAllLibraryVariants() - compilations.configureEach { - kotlinOptions { - jvmTarget = "11" - } - } } - jvm { - compilations.getByName("main") { - kotlinOptions { - jvmTarget = "17" - } - } - compilations.getByName("test") { - kotlinOptions { - jvmTarget = "17" - } - } - } + jvm() if (supportIosTarget) { iosArm64() @@ -87,7 +84,7 @@ kotlin { } noPodspec() pod("FirebaseStorage") { - version = "10.25.0" + version = libs.versions.firebase.cocoapods.get() extraOpts += listOf("-compiler-option", "-fmodules") } } @@ -96,22 +93,18 @@ kotlin { js(IR) { useCommonJs() nodejs { - testTask( - Action { - useKarma { - useChromeHeadless() - } + testTask { + useKarma { + useChromeHeadless() } - ) + } } browser { - testTask( - Action { - useKarma { - useChromeHeadless() - } + testTask { + useKarma { + useChromeHeadless() } - ) + } } } @@ -154,6 +147,12 @@ if (project.property("firebase-storage.skipIosTests") == "true") { } } +if (project.property("firebase-storage.skipJvmTests") == "true") { + tasks.forEach { + if (it.name.contains("jvm", true) && it.name.contains("test", true)) { it.enabled = false } + } +} + if (project.property("firebase-storage.skipJsTests") == "true") { tasks.forEach { if (it.name.contains("js", true) && it.name.contains("test", true)) { it.enabled = false } diff --git a/firebase-storage/src/commonTest/kotlin/dev/gitlive/firebase/storage/storage.kt b/firebase-storage/src/commonTest/kotlin/dev/gitlive/firebase/storage/storage.kt index 009c9027e..dcd147944 100644 --- a/firebase-storage/src/commonTest/kotlin/dev/gitlive/firebase/storage/storage.kt +++ b/firebase-storage/src/commonTest/kotlin/dev/gitlive/firebase/storage/storage.kt @@ -9,13 +9,10 @@ import dev.gitlive.firebase.FirebaseOptions import dev.gitlive.firebase.apps import dev.gitlive.firebase.initialize import dev.gitlive.firebase.runBlockingTest -import kotlinx.coroutines.flow.channelFlow -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.launch +import dev.gitlive.firebase.runTest import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test -import kotlin.test.assertContentEquals import kotlin.test.assertEquals import kotlin.test.assertNotNull @@ -57,19 +54,20 @@ class FirebaseStorageTest { } @Test - fun testStorageNotNull() { + fun testStorageNotNull() = runTest { assertNotNull(storage) } @Test - fun testUploadShouldNotCrash() = runBlockingTest { + fun testUploadShouldNotCrash() = runTest { val data = createTestData() val ref = storage.reference("test").child("testUploadShouldNotCrash.txt") + ref.putData(data) } @Test - fun testUploadMetadata() = runBlockingTest { + fun testUploadMetadata() = runTest { val data = createTestData() val ref = storage.reference("test").child("testUploadMetadata.txt") val metadata = storageMetadata { @@ -81,11 +79,11 @@ class FirebaseStorageTest { assertNotNull(metadataResult) assertNotNull(metadataResult.contentType) - assertEquals(metadataResult.contentType, metadata.contentType) + assertEquals(metadata.contentType, metadataResult.contentType) } @Test - fun testUploadCustomMetadata() = runBlockingTest { + fun testUploadCustomMetadata() = runTest { val data = createTestData() val ref = storage.reference("test").child("testUploadCustomMetadata.txt") val metadata = storageMetadata { @@ -97,7 +95,7 @@ class FirebaseStorageTest { val metadataResult = ref.getMetadata() assertNotNull(metadataResult) - assertEquals(metadataResult.customMetadata["key"], metadata.customMetadata["key"]) + assertEquals( metadata.customMetadata["key"], metadataResult.customMetadata["key"]) } } diff --git a/firebase-storage/src/iosTest/kotlin/dev/gitlive/firebase/storage/storage.kt b/firebase-storage/src/iosTest/kotlin/dev/gitlive/firebase/storage/storage.kt index 7b3541194..51eb0b48a 100644 --- a/firebase-storage/src/iosTest/kotlin/dev/gitlive/firebase/storage/storage.kt +++ b/firebase-storage/src/iosTest/kotlin/dev/gitlive/firebase/storage/storage.kt @@ -4,7 +4,7 @@ package dev.gitlive.firebase.storage -actual val emulatorHost: String = "127.0.0.1" +actual val emulatorHost: String = "localhost" actual val context: Any = Unit diff --git a/firebase-storage/src/jsMain/kotlin/dev/gitlive/firebase/storage/externals/storage.kt b/firebase-storage/src/jsMain/kotlin/dev/gitlive/firebase/storage/externals/storage.kt index 13c23ce61..b1a793d54 100644 --- a/firebase-storage/src/jsMain/kotlin/dev/gitlive/firebase/storage/externals/storage.kt +++ b/firebase-storage/src/jsMain/kotlin/dev/gitlive/firebase/storage/externals/storage.kt @@ -4,26 +4,29 @@ package dev.gitlive.firebase.storage.externals import dev.gitlive.firebase.externals.FirebaseApp +import kotlin.js.Json import kotlin.js.Promise +import kotlin.js.collections.JsMap -external fun getStorage(app: FirebaseApp? = definedExternally, url: String): FirebaseStorage +external fun getStorage(app: FirebaseApp? = definedExternally, bucketUrl: String): FirebaseStorage external fun getStorage(app: FirebaseApp? = definedExternally): FirebaseStorage external fun ref(storage: FirebaseStorage, url: String? = definedExternally): StorageReference -external fun ref(ref: StorageReference, url: String? = definedExternally): StorageReference +external fun ref(ref: StorageReference, path: String? = definedExternally): StorageReference external fun getDownloadURL(ref: StorageReference): Promise -external fun getMetadata(ref: StorageReference): Promise +external fun getMetadata(ref: StorageReference): Promise +external fun updateMetadata(ref: StorageReference, metadata: SettableMetadata): Promise -external fun uploadBytes(ref: StorageReference, file: dynamic, metadata: StorageMetadata?): Promise +external fun uploadBytes(ref: StorageReference, file: dynamic, metadata: Json?): Promise +external fun uploadBytesResumable(ref: StorageReference, data: dynamic, metadata: Json?): UploadTask -external fun uploadBytesResumable(ref: StorageReference, data: dynamic, metadata: StorageMetadata?): UploadTask +external fun deleteObject(ref: StorageReference): Promise -external fun deleteObject(ref: StorageReference): Promise; - -external fun listAll(ref: StorageReference): Promise; +external fun list(ref: StorageReference, options: ListOptions?): Promise +external fun listAll(ref: StorageReference): Promise external fun connectStorageEmulator( storage: FirebaseStorage, @@ -46,7 +49,12 @@ external interface StorageReference { val storage: FirebaseStorage } -external open class ListResult { +external interface ListOptions { + val maxResults: Double? + val pageToken: String? +} + +external interface ListResult { val items: Array val nextPageToken: String val prefixes: Array @@ -54,37 +62,50 @@ external open class ListResult { external interface StorageError -external interface UploadTaskSnapshot { - val bytesTransferred: Number - val ref: StorageReference - val state: String - val task: UploadTask - val totalBytes: Number +external interface SettableMetadata { + val cacheControl: String? + val contentDisposition: String? + val contentEncoding: String? + val contentLanguage: String? + val contentType: String? + val customMetadata: Json? } -external class StorageMetadata { - val bucket: String? - var cacheControl: String? - var contentDisposition: String? - var contentEncoding: String? - var contentLanguage: String? - var contentType: String? - var customMetadata: Map? - val fullPath: String? - val generation: String? +external interface UploadMetadata : SettableMetadata { val md5Hash: String? - val metageneration: String? - val name: String? - val size: Number? - val timeCreated: String? - val updated: String? +} +external interface FullMetadata : UploadMetadata { + val bucket: String + val downloadTokens: Array? + val fullPath: String + val generation: String + val metageneration: String + val name: String + val ref: StorageReference? + val size: Double + val timeCreated: String + val updated: String } -external class UploadTask : Promise { +external interface UploadResult { + val metadata: FullMetadata + val ref: StorageReference +} + +external interface UploadTask { fun cancel(): Boolean; fun on(event: String, next: (snapshot: UploadTaskSnapshot) -> Unit, error: (a: StorageError) -> Unit, complete: () -> Unit): () -> Unit fun pause(): Boolean; fun resume(): Boolean; + fun then(onFulfilled: ((UploadTaskSnapshot) -> Unit)?, onRejected: ((StorageError) -> Unit)?): Promise val snapshot: UploadTaskSnapshot } + +external interface UploadTaskSnapshot { + val bytesTransferred: Double + val ref: StorageReference + val state: String + val task: UploadTask + val totalBytes: Double +} \ No newline at end of file diff --git a/firebase-storage/src/jsMain/kotlin/dev/gitlive/firebase/storage/storage.kt b/firebase-storage/src/jsMain/kotlin/dev/gitlive/firebase/storage/storage.kt index 6cb2b34a6..18430ca9e 100644 --- a/firebase-storage/src/jsMain/kotlin/dev/gitlive/firebase/storage/storage.kt +++ b/firebase-storage/src/jsMain/kotlin/dev/gitlive/firebase/storage/storage.kt @@ -14,6 +14,8 @@ import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.FlowCollector import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.emitAll +import kotlin.js.Json +import kotlin.js.json actual val Firebase.storage get() = FirebaseStorage(getStorage()) @@ -134,7 +136,7 @@ internal fun errorToException(error: dynamic) = (error?.code ?: error?.message ? } -internal fun StorageMetadata.toFirebaseStorageMetadata(): FirebaseStorageMetadata { +internal fun UploadMetadata.toFirebaseStorageMetadata(): FirebaseStorageMetadata { val sdkMetadata = this return storageMetadata { md5Hash = sdkMetadata.md5Hash @@ -143,19 +145,21 @@ internal fun StorageMetadata.toFirebaseStorageMetadata(): FirebaseStorageMetadat contentEncoding = sdkMetadata.contentEncoding contentLanguage = sdkMetadata.contentLanguage contentType = sdkMetadata.contentType - sdkMetadata.customMetadata?.entries?.forEach { - setCustomMetadata(it.key, it.value) - } + customMetadata = sdkMetadata.customMetadata?.let { metadata -> + val objectKeys = js("Object.keys") + objectKeys(metadata).unsafeCast>().associateWith { key -> + metadata[key]?.toString().orEmpty() + } + }.orEmpty().toMutableMap() } } -internal fun FirebaseStorageMetadata.toStorageMetadata(): StorageMetadata { - val metadata = StorageMetadata() - metadata.cacheControl = cacheControl - metadata.contentDisposition = contentDisposition - metadata.contentEncoding = contentEncoding - metadata.contentLanguage = contentLanguage - metadata.contentType = contentType - metadata.customMetadata = customMetadata - return metadata -} \ No newline at end of file +internal fun FirebaseStorageMetadata.toStorageMetadata(): Json = json( + "cacheControl" to cacheControl, + "contentDisposition" to contentDisposition, + "contentEncoding" to contentEncoding, + "contentLanguage" to contentLanguage, + "contentType" to contentType, + "customMetadata" to json(*customMetadata.toList().toTypedArray()), + "md5Hash" to md5Hash +) \ No newline at end of file diff --git a/firebase-storage/src/jsTest/kotlin/dev/gitlive/firebase/storage/storage.js.kt b/firebase-storage/src/jsTest/kotlin/dev/gitlive/firebase/storage/storage.js.kt index 8a8c266b6..a3c6ff594 100644 --- a/firebase-storage/src/jsTest/kotlin/dev/gitlive/firebase/storage/storage.js.kt +++ b/firebase-storage/src/jsTest/kotlin/dev/gitlive/firebase/storage/storage.js.kt @@ -1,5 +1,5 @@ package dev.gitlive.firebase.storage -actual fun createTestData(): Data { - TODO("Not yet implemented") -} \ No newline at end of file +import org.khronos.webgl.Uint8Array + +actual fun createTestData(): Data = Data(Uint8Array("test".encodeToByteArray().toTypedArray())) \ No newline at end of file diff --git a/firebase-storage/src/jvmTest/kotlin/dev/gitlive/firebase/storage/storage.kt b/firebase-storage/src/jvmTest/kotlin/dev/gitlive/firebase/storage/storage.kt index a030f38bd..022903848 100644 --- a/firebase-storage/src/jvmTest/kotlin/dev/gitlive/firebase/storage/storage.kt +++ b/firebase-storage/src/jvmTest/kotlin/dev/gitlive/firebase/storage/storage.kt @@ -5,9 +5,11 @@ @file:JvmName("tests") package dev.gitlive.firebase.storage -actual val emulatorHost: String = "10.0.2.2" +import dev.gitlive.firebase.testContext -actual val context: Any = Unit +actual val emulatorHost: String = "localhost" + +actual val context: Any = testContext @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) actual annotation class IgnoreForAndroidUnitTest diff --git a/gradle.properties b/gradle.properties index 4519739b9..40a407d2c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,7 +25,7 @@ skipIosTarget=false firebase-analytics.skipIosTests=true firebase-app.skipIosTests=false # We are skipping auth ios tests due to an issue with keychain and simulator. -firebase-auth.skipIosTests=true +firebase-auth.skipIosTests=false firebase-common.skipIosTests=false firebase-common-internal.skipIosTests=false firebase-config.skipIosTests=false @@ -38,6 +38,21 @@ firebase-perf.skipIosTests=false firebase-crashlytics.skipIosTests=false firebase-storage.skipIosTests=false +# We can have the functionality to skip jvm tests, due to compatibility issues. +firebase-analytics.skipJvmTests=true +firebase-app.skipJvmTests=false +firebase-auth.skipJvmTests=true +firebase-common.skipJvmTests=false +firebase-common-internal.skipJvmTests=false +firebase-config.skipJvmTests=true +firebase-database.skipJvmTests=false +firebase-firestore.skipJvmTests=false +firebase-functions.skipJvmTests=false +firebase-messaging.skipJvmTests=false +firebase-installations.skipJvmTests=false +firebase-perf.skipJvmTests=true +firebase-storage.skipJvmTests=true + # We can have the functionality to skip js tests, due to compatibility issues. firebase-analytics.skipJsTests=false firebase-app.skipJsTests=false diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 52d686000..3fa86e861 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,18 +1,19 @@ [versions] -agp = "8.2.0" +agp = "8.2.2" androidx-test-core = "1.5.0" androidx-test-junit = "1.1.5" androidx-test-runner = "1.5.2" -ben-manes-versions = "0.42.0" -firebase-bom = "33.0.0" +ben-manes-versions = "0.51.0" +firebase-bom = "33.1.1" gitlive-firebase-java-sdk = "0.4.3" gson = "2.11.0" junit = "4.13.2" -kotlin = "1.9.23" -kotlinx-coroutines = "1.7.3" -kotlinx-serialization = "1.6.0" -settings-api = "1.8" -settings-language = "1.9" +kotlin = "2.0.0" +kotlinx-coroutines = "1.9.0-RC" +kotlinx-serialization = "1.7.0" +settings-api = "2.0" +settings-language = "2.0" +firebase-cocoapods = "10.28.0" test-logger-plugin = "3.2.0" dokka = "1.9.20" @@ -40,6 +41,7 @@ junit = { module = "junit:junit", version.ref = "junit" } kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" } kotlinx-coroutines-play-services = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-play-services", version.ref = "kotlinx-coroutines" } kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinx-coroutines" } +kotlinx-coroutines-swing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "kotlinx-coroutines" } kotlinx-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "kotlinx-serialization" } dokka-base = { module = "org.jetbrains.dokka:dokka-base", version.ref = "dokka" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 17655d0ef..0d1842103 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/test-utils/build.gradle.kts b/test-utils/build.gradle.kts index 8d3ed9a84..aca6eb343 100644 --- a/test-utils/build.gradle.kts +++ b/test-utils/build.gradle.kts @@ -1,3 +1,8 @@ +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree + /* * Copyright (c) 2023 GitLive Ltd. Use of this source code is governed by the Apache 2.0 license. */ @@ -25,8 +30,8 @@ android { } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } packaging { @@ -40,35 +45,31 @@ android { } kotlin { - + @OptIn(ExperimentalKotlinGradlePluginApi::class) + compilerOptions { + freeCompilerArgs.add("-Xexpect-actual-classes") + } targets.configureEach { compilations.configureEach { - kotlinOptions.freeCompilerArgs += "-Xexpect-actual-classes" + compileTaskProvider.configure { + compilerOptions { + if (this is KotlinJvmCompilerOptions) { + jvmTarget = JvmTarget.JVM_17 + } + freeCompilerArgs.add("-Xexpect-actual-classes") + } + } } } @Suppress("OPT_IN_USAGE") androidTarget { + instrumentedTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) + unitTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) publishAllLibraryVariants() - compilations.configureEach { - kotlinOptions { - jvmTarget = "11" - } - } } - jvm { - compilations.getByName("main") { - kotlinOptions { - jvmTarget = "17" - } - } - compilations.getByName("test") { - kotlinOptions { - jvmTarget = "17" - } - } - } + jvm() val supportIosTarget = project.property("skipIosTarget") != "true" @@ -105,14 +106,16 @@ kotlin { } } - getByName("jvmMain") { - kotlin.srcDir("src/androidMain/kotlin") - } - getByName("jsMain") { dependencies { implementation(kotlin("test-js")) } } + + getByName("jvmMain") { + dependencies { + api(libs.kotlinx.coroutines.swing) + } + } } } diff --git a/test-utils/src/jvmMain/kotlin/dev/gitlive/firebase/TestUtils.kt b/test-utils/src/jvmMain/kotlin/dev/gitlive/firebase/TestUtils.kt new file mode 100644 index 000000000..56ff3e31f --- /dev/null +++ b/test-utils/src/jvmMain/kotlin/dev/gitlive/firebase/TestUtils.kt @@ -0,0 +1,31 @@ +@file:JvmName("TestUtilsJvm") +/* + * Copyright (c) 2020 GitLive Ltd. Use of this source code is governed by the Apache 2.0 license. + */ + +package dev.gitlive.firebase + +import android.app.Application +import com.google.firebase.FirebasePlatform +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.runBlocking +import kotlin.time.Duration.Companion.minutes + +val testContext = Application().apply { + FirebasePlatform.initializeFirebasePlatform(object : FirebasePlatform() { + val storage = mutableMapOf() + override fun store(key: String, value: String) = storage.set(key, value) + override fun retrieve(key: String) = storage[key] + override fun clear(key: String) { storage.remove(key) } + override fun log(msg: String) = println(msg) + }) +} + +actual fun runTest(test: suspend CoroutineScope.() -> Unit) = kotlinx.coroutines.test.runTest(timeout = 5.minutes) { test() } +actual fun runBlockingTest(action: suspend CoroutineScope.() -> Unit) = runBlocking(block = action) + +actual fun nativeMapOf(vararg pairs: Pair): Any = mapOf(*pairs) +actual fun nativeListOf(vararg elements: Any?): Any = listOf(*elements) +actual fun nativeAssertEquals(expected: Any?, actual: Any?) { + kotlin.test.assertEquals(expected, actual) +} \ No newline at end of file