Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/android_unit_tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
# Tasks are listed per package rather than using the root testDebugUnitTest
# task, which would also build the Dart sources of the aggregate test app.
# Add a task here when a package gains an android/src/test directory.
run: cd tests/android && ./gradlew :firebase_crashlytics:testDebugUnitTest
run: cd tests/android && ./gradlew :firebase_crashlytics:testDebugUnitTest :cloud_functions:testDebugUnitTest
- name: 'Upload test reports'
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
Expand Down
5 changes: 5 additions & 0 deletions packages/cloud_functions/cloud_functions/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ android {
implementation 'com.google.firebase:firebase-functions'
implementation 'androidx.annotation:annotation:1.7.0'
implementation 'org.reactivestreams:reactive-streams:1.0.4'
testImplementation 'junit:junit:4.13.2'
}

testOptions {
unitTests.returnDefaultValues = true
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ class FlutterFirebaseFunctionsPlugin : FlutterPlugin, FlutterFirebasePlugin, Clo
}
}

details["code"] = code.replace("_", "-").lowercase(Locale.getDefault())
details["code"] = mapFunctionsErrorCode(code)
details["message"] = message

if (additionalData != null) {
Expand Down Expand Up @@ -164,6 +164,11 @@ class FlutterFirebaseFunctionsPlugin : FlutterPlugin, FlutterFirebasePlugin, Clo

companion object {
private const val METHOD_CHANNEL_NAME = "plugins.flutter.io/firebase_functions"

// Locale.ROOT: Turkish/Azerbaijani map 'I' → 'ı' under Locale.getDefault().
internal fun mapFunctionsErrorCode(code: String): String {
return code.replace("_", "-").lowercase(Locale.ROOT)
}
}

override fun call(arguments: Map<String, Any?>, callback: (Result<Any?>) -> Unit) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright 2026 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package io.flutter.plugins.firebase.functions

import java.util.Locale
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4

@RunWith(JUnit4::class)
class FlutterFirebaseFunctionsPluginTest {
private lateinit var originalLocale: Locale

@Before
fun setUp() {
originalLocale = Locale.getDefault()
}

@After
fun tearDown() {
Locale.setDefault(originalLocale)
}

@Test
fun mapFunctionsErrorCode_usesRootLocaleUnderTurkishDefault() {
Locale.setDefault(Locale("tr", "TR"))

// Canary: the JVM's Turkish locale really does map 'I' to 'ı'.
assertEquals(
"faıled-precondıtıon",
"FAILED_PRECONDITION".replace("_", "-").lowercase(Locale.getDefault()))

assertMappedCodes()
}

@Test
fun mapFunctionsErrorCode_usesRootLocaleUnderAzerbaijaniDefault() {
Locale.setDefault(Locale("az", "AZ"))

assertEquals(
"faıled-precondıtıon",
"FAILED_PRECONDITION".replace("_", "-").lowercase(Locale.getDefault()))

assertMappedCodes()
}

private fun assertMappedCodes() {
val expected =
mapOf(
"FAILED_PRECONDITION" to "failed-precondition",
"INVALID_ARGUMENT" to "invalid-argument",
"PERMISSION_DENIED" to "permission-denied",
"UNAUTHENTICATED" to "unauthenticated",
"INTERNAL" to "internal",
"UNAVAILABLE" to "unavailable",
"DEADLINE_EXCEEDED" to "deadline-exceeded",
"ALREADY_EXISTS" to "already-exists",
"UNIMPLEMENTED" to "unimplemented",
"UNKNOWN" to "unknown",
"NOT_FOUND" to "not-found",
"ABORTED" to "aborted",
)

for ((enumName, canonical) in expected) {
assertEquals(canonical, FlutterFirebaseFunctionsPlugin.mapFunctionsErrorCode(enumName))
}
}
}
Loading