diff --git a/.github/workflows/create_releases.yml b/.github/workflows/create_releases.yml new file mode 100644 index 00000000000..f19582a34a0 --- /dev/null +++ b/.github/workflows/create_releases.yml @@ -0,0 +1,49 @@ +name: Create release + +on: + workflow_dispatch: + inputs: + name: + description: 'Release name' + required: true + type: string + +jobs: + create-branches: + runs-on: ubuntu-latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - name: Create base branch + uses: peterjgrainger/action-create-branch@v2.2.0 + with: + branch: '${{ inputs.name }}' + - name: Create release branch + uses: peterjgrainger/action-create-branch@v2.2.0 + with: + branch: '${{ inputs.name }}.release' + + create-pull-request: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Create release configuration template + run: | + git config user.name 'Create Release GA' + git config user.email 'noreply@google.com' + echo "[release]" > release.cfg + echo "name = ${{ inputs.name }}" >> release.cfg + echo "mode = RELEASE" >> release.cfg + echo "" >> release.cfg + echo "[modules]" >> release.cfg + echo "" >> release.cfg + git add release.cfg + git commit -a -m 'Create release config' + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v4 + with: + base: '${{ inputs.name }}' + branch: '${{ inputs.name }}.release' + title: '${{ inputs.name}} release' diff --git a/appcheck/firebase-appcheck-debug-testing/gradle.properties b/appcheck/firebase-appcheck-debug-testing/gradle.properties index 0e34974b3c7..9fcdf80f72a 100644 --- a/appcheck/firebase-appcheck-debug-testing/gradle.properties +++ b/appcheck/firebase-appcheck-debug-testing/gradle.properties @@ -1,2 +1,2 @@ -version=16.0.3 -latestReleasedVersion=16.0.2 +version=16.1.1 +latestReleasedVersion=16.1.0 diff --git a/appcheck/firebase-appcheck-debug/gradle.properties b/appcheck/firebase-appcheck-debug/gradle.properties index 0e34974b3c7..9fcdf80f72a 100644 --- a/appcheck/firebase-appcheck-debug/gradle.properties +++ b/appcheck/firebase-appcheck-debug/gradle.properties @@ -1,2 +1,2 @@ -version=16.0.3 -latestReleasedVersion=16.0.2 +version=16.1.1 +latestReleasedVersion=16.1.0 diff --git a/appcheck/firebase-appcheck-debug/src/test/java/com/google/firebase/appcheck/debug/internal/DebugAppCheckProviderTest.java b/appcheck/firebase-appcheck-debug/src/test/java/com/google/firebase/appcheck/debug/internal/DebugAppCheckProviderTest.java index c3a6e3535af..3d53b398fd2 100644 --- a/appcheck/firebase-appcheck-debug/src/test/java/com/google/firebase/appcheck/debug/internal/DebugAppCheckProviderTest.java +++ b/appcheck/firebase-appcheck-debug/src/test/java/com/google/firebase/appcheck/debug/internal/DebugAppCheckProviderTest.java @@ -43,10 +43,12 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; +import org.robolectric.annotation.LooperMode; /** Tests for {@link DebugAppCheckProvider}. */ @RunWith(RobolectricTestRunner.class) @Config(manifest = Config.NONE) +@LooperMode(LooperMode.Mode.LEGACY) public class DebugAppCheckProviderTest { private static final String DEBUG_SECRET = "debugSecret"; diff --git a/appcheck/firebase-appcheck-interop/gradle.properties b/appcheck/firebase-appcheck-interop/gradle.properties index 0e34974b3c7..9fcdf80f72a 100644 --- a/appcheck/firebase-appcheck-interop/gradle.properties +++ b/appcheck/firebase-appcheck-interop/gradle.properties @@ -1,2 +1,2 @@ -version=16.0.3 -latestReleasedVersion=16.0.2 +version=16.1.1 +latestReleasedVersion=16.1.0 diff --git a/appcheck/firebase-appcheck-playintegrity/gradle.properties b/appcheck/firebase-appcheck-playintegrity/gradle.properties index 0e34974b3c7..9fcdf80f72a 100644 --- a/appcheck/firebase-appcheck-playintegrity/gradle.properties +++ b/appcheck/firebase-appcheck-playintegrity/gradle.properties @@ -1,2 +1,2 @@ -version=16.0.3 -latestReleasedVersion=16.0.2 +version=16.1.1 +latestReleasedVersion=16.1.0 diff --git a/appcheck/firebase-appcheck-playintegrity/src/test/java/com/google/firebase/appcheck/playintegrity/internal/PlayIntegrityAppCheckProviderTest.java b/appcheck/firebase-appcheck-playintegrity/src/test/java/com/google/firebase/appcheck/playintegrity/internal/PlayIntegrityAppCheckProviderTest.java index 1cbcd153bb3..4d748b7d78f 100644 --- a/appcheck/firebase-appcheck-playintegrity/src/test/java/com/google/firebase/appcheck/playintegrity/internal/PlayIntegrityAppCheckProviderTest.java +++ b/appcheck/firebase-appcheck-playintegrity/src/test/java/com/google/firebase/appcheck/playintegrity/internal/PlayIntegrityAppCheckProviderTest.java @@ -47,10 +47,12 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; +import org.robolectric.annotation.LooperMode; /** Tests for {@link PlayIntegrityAppCheckProvider}. */ @RunWith(RobolectricTestRunner.class) @Config(manifest = Config.NONE) +@LooperMode(LooperMode.Mode.LEGACY) public class PlayIntegrityAppCheckProviderTest { private static final String PROJECT_NUMBER = "123456"; diff --git a/appcheck/firebase-appcheck-safetynet/gradle.properties b/appcheck/firebase-appcheck-safetynet/gradle.properties index 0e34974b3c7..9fcdf80f72a 100644 --- a/appcheck/firebase-appcheck-safetynet/gradle.properties +++ b/appcheck/firebase-appcheck-safetynet/gradle.properties @@ -1,2 +1,2 @@ -version=16.0.3 -latestReleasedVersion=16.0.2 +version=16.1.1 +latestReleasedVersion=16.1.0 diff --git a/appcheck/firebase-appcheck-safetynet/src/test/java/com/google/firebase/appcheck/safetynet/internal/SafetyNetAppCheckProviderTest.java b/appcheck/firebase-appcheck-safetynet/src/test/java/com/google/firebase/appcheck/safetynet/internal/SafetyNetAppCheckProviderTest.java index d53fdba566e..6072e3693f1 100644 --- a/appcheck/firebase-appcheck-safetynet/src/test/java/com/google/firebase/appcheck/safetynet/internal/SafetyNetAppCheckProviderTest.java +++ b/appcheck/firebase-appcheck-safetynet/src/test/java/com/google/firebase/appcheck/safetynet/internal/SafetyNetAppCheckProviderTest.java @@ -46,10 +46,12 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; +import org.robolectric.annotation.LooperMode; /** Tests for {@link SafetyNetAppCheckProvider}. */ @RunWith(RobolectricTestRunner.class) @Config(manifest = Config.NONE) +@LooperMode(LooperMode.Mode.LEGACY) public class SafetyNetAppCheckProviderTest { private static final String API_KEY = "apiKey"; diff --git a/appcheck/firebase-appcheck/gradle.properties b/appcheck/firebase-appcheck/gradle.properties index 0e34974b3c7..9fcdf80f72a 100644 --- a/appcheck/firebase-appcheck/gradle.properties +++ b/appcheck/firebase-appcheck/gradle.properties @@ -1,2 +1,2 @@ -version=16.0.3 -latestReleasedVersion=16.0.2 +version=16.1.1 +latestReleasedVersion=16.1.0 diff --git a/appcheck/firebase-appcheck/src/test/java/com/google/firebase/appcheck/internal/DefaultFirebaseAppCheckTest.java b/appcheck/firebase-appcheck/src/test/java/com/google/firebase/appcheck/internal/DefaultFirebaseAppCheckTest.java index 95330db0ed4..7b065e4a827 100644 --- a/appcheck/firebase-appcheck/src/test/java/com/google/firebase/appcheck/internal/DefaultFirebaseAppCheckTest.java +++ b/appcheck/firebase-appcheck/src/test/java/com/google/firebase/appcheck/internal/DefaultFirebaseAppCheckTest.java @@ -41,10 +41,12 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; +import org.robolectric.annotation.LooperMode; /** Tests for {@link DefaultFirebaseAppCheck}. */ @RunWith(RobolectricTestRunner.class) @Config(manifest = Config.NONE) +@LooperMode(LooperMode.Mode.LEGACY) public class DefaultFirebaseAppCheckTest { private static final String EXCEPTION_TEXT = "exceptionText"; diff --git a/appcheck/firebase-appcheck/src/test/java/com/google/firebase/appcheck/internal/DefaultTokenRefresherTest.java b/appcheck/firebase-appcheck/src/test/java/com/google/firebase/appcheck/internal/DefaultTokenRefresherTest.java index 67b7fd5805b..44805aca22b 100644 --- a/appcheck/firebase-appcheck/src/test/java/com/google/firebase/appcheck/internal/DefaultTokenRefresherTest.java +++ b/appcheck/firebase-appcheck/src/test/java/com/google/firebase/appcheck/internal/DefaultTokenRefresherTest.java @@ -33,8 +33,10 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.LooperMode; @RunWith(RobolectricTestRunner.class) +@LooperMode(LooperMode.Mode.LEGACY) public class DefaultTokenRefresherTest { private static final long TIME_TO_REFRESH_MILLIS = 1000L; diff --git a/build.gradle b/build.gradle index 30208819267..0fd95e3e400 100644 --- a/build.gradle +++ b/build.gradle @@ -51,7 +51,7 @@ ext { supportAnnotationsVersion = '28.0.0' googleTruthVersion = '1.1.2' grpcVersion = '1.48.1' - robolectricVersion = '4.3.1' + robolectricVersion = '4.9' protocVersion = '3.17.3' javaliteVersion = '3.17.3' } @@ -66,6 +66,7 @@ firebaseContinuousIntegration { ignorePaths = [ /.*\.gitignore$/, /.*\/.*.md$/, + /.*\.github.*/, ] } diff --git a/buildSrc/src/main/java/com/google/firebase/gradle/bomgenerator/BomGeneratorTask.java b/buildSrc/src/main/java/com/google/firebase/gradle/bomgenerator/BomGeneratorTask.java index 56589e10f1e..ad6bc36f6ce 100644 --- a/buildSrc/src/main/java/com/google/firebase/gradle/bomgenerator/BomGeneratorTask.java +++ b/buildSrc/src/main/java/com/google/firebase/gradle/bomgenerator/BomGeneratorTask.java @@ -58,14 +58,12 @@ public class BomGeneratorTask extends DefaultTask { "com.google.firebase:firebase-appcheck-playintegrity", "com.google.firebase:firebase-appcheck-safetynet", "com.google.firebase:firebase-appcheck", - "com.google.firebase:firebase-appindexing", "com.google.firebase:firebase-auth", "com.google.firebase:firebase-auth-ktx", "com.google.firebase:firebase-common", "com.google.firebase:firebase-common-ktx", "com.google.firebase:firebase-config", "com.google.firebase:firebase-config-ktx", - "com.google.firebase:firebase-core", "com.google.firebase:firebase-crashlytics", "com.google.firebase:firebase-crashlytics-ktx", "com.google.firebase:firebase-crashlytics-ndk", @@ -78,7 +76,6 @@ public class BomGeneratorTask extends DefaultTask { "com.google.firebase:firebase-firestore-ktx", "com.google.firebase:firebase-functions", "com.google.firebase:firebase-functions-ktx", - "com.google.firebase:firebase-iid", "com.google.firebase:firebase-inappmessaging", "com.google.firebase:firebase-inappmessaging-display", "com.google.firebase:firebase-inappmessaging-display-ktx", @@ -109,6 +106,9 @@ public class BomGeneratorTask extends DefaultTask { "firebase-appcheck-interop", "firebase-appdistribution-gradle", "firebase-appindexing-license", + "firebase-appindexing", + "firebase-iid", + "firebase-core", "firebase-auth-common", "firebase-auth-impl", "firebase-auth-interop", diff --git a/firebase-abt/gradle.properties b/firebase-abt/gradle.properties index f8301e25bcc..fd551588b1b 100644 --- a/firebase-abt/gradle.properties +++ b/firebase-abt/gradle.properties @@ -1,2 +1,2 @@ -version=21.0.3 -latestReleasedVersion=21.0.2 +version=21.1.1 +latestReleasedVersion=21.1.0 diff --git a/firebase-appdistribution-api/src/test/java/com/google/firebase/appdistribution/internal/FirebaseAppDistributionProxyTest.java b/firebase-appdistribution-api/src/test/java/com/google/firebase/appdistribution/internal/FirebaseAppDistributionProxyTest.java index 6c391a87752..3c0a207e250 100644 --- a/firebase-appdistribution-api/src/test/java/com/google/firebase/appdistribution/internal/FirebaseAppDistributionProxyTest.java +++ b/firebase-appdistribution-api/src/test/java/com/google/firebase/appdistribution/internal/FirebaseAppDistributionProxyTest.java @@ -33,8 +33,10 @@ import org.junit.runner.RunWith; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.LooperMode; @RunWith(RobolectricTestRunner.class) +@LooperMode(LooperMode.Mode.LEGACY) public class FirebaseAppDistributionProxyTest { private FirebaseAppDistribution firebaseAppDistribution; diff --git a/firebase-appdistribution/firebase-appdistribution.gradle b/firebase-appdistribution/firebase-appdistribution.gradle index 0ed95168456..1934320538d 100644 --- a/firebase-appdistribution/firebase-appdistribution.gradle +++ b/firebase-appdistribution/firebase-appdistribution.gradle @@ -51,7 +51,7 @@ dependencies { runtimeOnly project(':firebase-installations') testImplementation 'junit:junit:4.13.2' - testImplementation "org.robolectric:robolectric:4.8.1" + testImplementation "org.robolectric:robolectric:$robolectricVersion" testImplementation "com.google.truth:truth:$googleTruthVersion" testImplementation 'org.mockito:mockito-inline:3.4.0' testImplementation 'androidx.test:core:1.2.0' diff --git a/firebase-common/data-collection-tests/src/test/java/com/google/firebase/DataCollectionPostNDefaultDisabledTest.java b/firebase-common/data-collection-tests/src/test/java/com/google/firebase/DataCollectionPostNDefaultDisabledTest.java index 72c091a5e2f..75b66ad6183 100644 --- a/firebase-common/data-collection-tests/src/test/java/com/google/firebase/DataCollectionPostNDefaultDisabledTest.java +++ b/firebase-common/data-collection-tests/src/test/java/com/google/firebase/DataCollectionPostNDefaultDisabledTest.java @@ -27,9 +27,11 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.annotation.Config; +import org.robolectric.annotation.LooperMode; @RunWith(AndroidJUnit4.class) @Config(sdk = 25) +@LooperMode(LooperMode.Mode.LEGACY) public class DataCollectionPostNDefaultDisabledTest { @Test diff --git a/firebase-common/data-collection-tests/src/test/java/com/google/firebase/DataCollectionPreNDefaultDisabledTest.java b/firebase-common/data-collection-tests/src/test/java/com/google/firebase/DataCollectionPreNDefaultDisabledTest.java index 75896f168fb..95199d0ce50 100644 --- a/firebase-common/data-collection-tests/src/test/java/com/google/firebase/DataCollectionPreNDefaultDisabledTest.java +++ b/firebase-common/data-collection-tests/src/test/java/com/google/firebase/DataCollectionPreNDefaultDisabledTest.java @@ -25,6 +25,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.annotation.Config; +import org.robolectric.annotation.LooperMode; @RunWith(AndroidJUnit4.class) @Config(sdk = 19) @@ -81,6 +82,7 @@ public void setDataCollectionDefaultEnabledTrue_shouldUpdateSharedPrefs() { } @Test + @LooperMode(LooperMode.Mode.LEGACY) public void setDataCollectionDefaultEnabledTrue_shouldEmitEvents() { withApp( app -> { diff --git a/firebase-common/gradle.properties b/firebase-common/gradle.properties index 18ebccb64b0..165d1ccbf35 100644 --- a/firebase-common/gradle.properties +++ b/firebase-common/gradle.properties @@ -1,3 +1,3 @@ -version=20.1.3 -latestReleasedVersion=20.1.2 +version=20.2.1 +latestReleasedVersion=20.2.0 android.enableUnitTestBinaryResources=true diff --git a/firebase-config/gradle.properties b/firebase-config/gradle.properties index aacbd727d0e..5a8818a73fa 100644 --- a/firebase-config/gradle.properties +++ b/firebase-config/gradle.properties @@ -14,7 +14,7 @@ # limitations under the License. # -version=21.1.3 -latestReleasedVersion=21.1.2 +version=21.2.1 +latestReleasedVersion=21.2.0 android.enableUnitTestBinaryResources=true diff --git a/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java b/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java index 52517a5438b..2d68c26e693 100644 --- a/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java +++ b/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java @@ -82,6 +82,7 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; +import org.robolectric.annotation.LooperMode; import org.skyscreamer.jsonassert.JSONAssert; /** @@ -93,6 +94,7 @@ @Config( manifest = Config.NONE, shadows = {ShadowPreconditions.class}) +@LooperMode(LooperMode.Mode.LEGACY) public final class FirebaseRemoteConfigTest { private static final String APP_ID = "1:14368190084:android:09cb977358c6f241"; private static final String API_KEY = "AIzaSyabcdefghijklmnopqrstuvwxyz1234567"; diff --git a/firebase-config/src/test/java/com/google/firebase/remoteconfig/RemoteConfigComponentTest.java b/firebase-config/src/test/java/com/google/firebase/remoteconfig/RemoteConfigComponentTest.java index 52b8c5300c9..36e35daac35 100644 --- a/firebase-config/src/test/java/com/google/firebase/remoteconfig/RemoteConfigComponentTest.java +++ b/firebase-config/src/test/java/com/google/firebase/remoteconfig/RemoteConfigComponentTest.java @@ -48,6 +48,7 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; +import org.robolectric.annotation.LooperMode; /** * Unit tests for the Firebase Remote Config Component. @@ -92,6 +93,7 @@ public void setUp() { } @Test + @LooperMode(LooperMode.Mode.LEGACY) public void frc2p_doesNotCallAbt() throws Exception { FirebaseRemoteConfig fireperfFrc = @@ -106,6 +108,7 @@ public void frc2p_doesNotCallAbt() throws Exception { } @Test + @LooperMode(LooperMode.Mode.LEGACY) public void frcNonMainFirebaseApp_doesNotCallAbt() throws Exception { when(mockFirebaseApp.getName()).thenReturn("secondary"); diff --git a/firebase-config/src/test/java/com/google/firebase/remoteconfig/internal/ConfigFetchHandlerTest.java b/firebase-config/src/test/java/com/google/firebase/remoteconfig/internal/ConfigFetchHandlerTest.java index 5dab49b6afa..922aefb547b 100644 --- a/firebase-config/src/test/java/com/google/firebase/remoteconfig/internal/ConfigFetchHandlerTest.java +++ b/firebase-config/src/test/java/com/google/firebase/remoteconfig/internal/ConfigFetchHandlerTest.java @@ -90,6 +90,7 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; +import org.robolectric.annotation.LooperMode; import org.skyscreamer.jsonassert.JSONAssert; /** @@ -99,6 +100,7 @@ */ @RunWith(RobolectricTestRunner.class) @Config(manifest = Config.NONE) +@LooperMode(LooperMode.Mode.LEGACY) public class ConfigFetchHandlerTest { private static final String INSTALLATION_ID = "'fL71_VyL3uo9jNMWu1L60S"; private static final String INSTALLATION_AUTH_TOKEN = diff --git a/firebase-crashlytics-ndk/gradle.properties b/firebase-crashlytics-ndk/gradle.properties index d7552b04cd2..e684e8451bc 100644 --- a/firebase-crashlytics-ndk/gradle.properties +++ b/firebase-crashlytics-ndk/gradle.properties @@ -1,2 +1,2 @@ -version=18.2.14 -latestReleasedVersion=18.2.13 +version=18.3.2 +latestReleasedVersion=18.3.13 diff --git a/firebase-crashlytics/firebase-crashlytics.gradle b/firebase-crashlytics/firebase-crashlytics.gradle index d2c1796d35b..a63495c83c7 100644 --- a/firebase-crashlytics/firebase-crashlytics.gradle +++ b/firebase-crashlytics/firebase-crashlytics.gradle @@ -79,7 +79,7 @@ dependencies { testImplementation 'androidx.test:runner:1.4.0' testImplementation 'androidx.test:core:1.4.0' - testImplementation "org.robolectric:robolectric:4.5" + testImplementation "org.robolectric:robolectric:$robolectricVersion" testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:3.4.3' diff --git a/firebase-crashlytics/gradle.properties b/firebase-crashlytics/gradle.properties index d7552b04cd2..e684e8451bc 100644 --- a/firebase-crashlytics/gradle.properties +++ b/firebase-crashlytics/gradle.properties @@ -1,2 +1,2 @@ -version=18.2.14 -latestReleasedVersion=18.2.13 +version=18.3.2 +latestReleasedVersion=18.3.13 diff --git a/firebase-database/firebase-database.gradle b/firebase-database/firebase-database.gradle index f19c95f4d3f..37231be536e 100644 --- a/firebase-database/firebase-database.gradle +++ b/firebase-database/firebase-database.gradle @@ -100,6 +100,7 @@ dependencies { testImplementation 'junit:junit:4.12' testImplementation 'org.mockito:mockito-core:2.25.0' + testImplementation 'org.codehaus.plexus:plexus-utils:3.4.2' testImplementation "org.robolectric:robolectric:$robolectricVersion" testImplementation 'com.firebase:firebase-token-generator:2.0.0' testImplementation 'com.fasterxml.jackson.core:jackson-core:2.9.8' diff --git a/firebase-database/gradle.properties b/firebase-database/gradle.properties index b5000bdda31..2485d5054e8 100644 --- a/firebase-database/gradle.properties +++ b/firebase-database/gradle.properties @@ -12,6 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -version=20.0.7 -latestReleasedVersion=20.0.6 +version=20.1.1 +latestReleasedVersion=20.1.0 android.enableUnitTestBinaryResources=true diff --git a/firebase-datatransport/gradle.properties b/firebase-datatransport/gradle.properties index 10a76e53f6f..a3fbce1885e 100644 --- a/firebase-datatransport/gradle.properties +++ b/firebase-datatransport/gradle.properties @@ -1,4 +1,4 @@ -version=18.1.7 -latestReleasedVersion=18.1.6 +version=18.1.8 +latestReleasedVersion=18.1.7 android.enableUnitTestBinaryResources=true diff --git a/firebase-dynamic-links/gradle.properties b/firebase-dynamic-links/gradle.properties index bc3efbb0b64..0f91d376021 100644 --- a/firebase-dynamic-links/gradle.properties +++ b/firebase-dynamic-links/gradle.properties @@ -12,6 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -version=21.0.3 -latestReleasedVersion=21.0.2 +version=21.1.1 +latestReleasedVersion=21.1.0 android.enableUnitTestBinaryResources=true diff --git a/firebase-firestore/firebase-firestore.gradle b/firebase-firestore/firebase-firestore.gradle index 804331a3b17..dd113dc1812 100644 --- a/firebase-firestore/firebase-firestore.gradle +++ b/firebase-firestore/firebase-firestore.gradle @@ -150,9 +150,7 @@ dependencies { testImplementation 'androidx.test:core:1.2.0' testImplementation "org.hamcrest:hamcrest-junit:2.0.0.0" testImplementation 'org.mockito:mockito-core:2.25.0' - testImplementation ("org.robolectric:robolectric:4.8.2") { - exclude group: 'com.google.protobuf', module: 'protobuf-java' - } + testImplementation "org.robolectric:robolectric:$robolectricVersion" testImplementation "com.google.truth:truth:$googleTruthVersion" testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.9.8' testImplementation 'com.google.guava:guava-testlib:12.0-rc2' diff --git a/firebase-firestore/gradle.properties b/firebase-firestore/gradle.properties index d0583730c2c..d6bbbcf314f 100644 --- a/firebase-firestore/gradle.properties +++ b/firebase-firestore/gradle.properties @@ -1,2 +1,2 @@ -version=24.3.2 -latestReleasedVersion=24.3.1 +version=24.4.1 +latestReleasedVersion=24.4.0 diff --git a/firebase-firestore/ktx/ktx.gradle b/firebase-firestore/ktx/ktx.gradle index 04e58cf4d5c..f778db02c98 100644 --- a/firebase-firestore/ktx/ktx.gradle +++ b/firebase-firestore/ktx/ktx.gradle @@ -59,6 +59,7 @@ dependencies { implementation 'androidx.annotation:annotation:1.1.0' implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion" implementation 'com.google.android.gms:play-services-basement:18.1.0' + testCompileOnly "com.google.protobuf:protobuf-java:$protocVersion" testImplementation project(':firebase-database-collection') testImplementation 'org.mockito:mockito-core:2.25.0' testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.9.8' diff --git a/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/CountTest.java b/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/CountTest.java index 5636cecffdd..396847e2174 100644 --- a/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/CountTest.java +++ b/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/CountTest.java @@ -14,6 +14,7 @@ package com.google.firebase.firestore; +import static com.google.common.truth.Truth.assertThat; import static com.google.firebase.firestore.testutil.IntegrationTestUtil.testCollection; import static com.google.firebase.firestore.testutil.IntegrationTestUtil.testCollectionWithDocs; import static com.google.firebase.firestore.testutil.IntegrationTestUtil.testFirestore; @@ -25,10 +26,14 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeFalse; import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.google.android.gms.tasks.Task; import com.google.firebase.firestore.testutil.IntegrationTestUtil; +import java.util.Collections; import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; @@ -253,4 +258,23 @@ public void testFailWithoutNetwork() { AggregateQuerySnapshot snapshot = waitFor(collection.count().get(AggregateSource.SERVER)); assertEquals(3L, snapshot.getCount()); } + + @Test + public void testFailWithGoodMessageIfMissingIndex() { + assumeFalse( + "Skip this test when running against the Firestore emulator because the Firestore emulator " + + "does not use indexes and never fails with a 'missing index' error", + BuildConfig.USE_EMULATOR_FOR_TESTS); + + CollectionReference collection = testCollectionWithDocs(Collections.emptyMap()); + Query compositeIndexQuery = collection.whereEqualTo("field1", 42).whereLessThan("field2", 99); + AggregateQuery compositeIndexCountQuery = compositeIndexQuery.count(); + Task task = compositeIndexCountQuery.get(AggregateSource.SERVER); + + Throwable throwable = assertThrows(Throwable.class, () -> waitFor(task)); + + Throwable cause = throwable.getCause(); + assertThat(cause).hasMessageThat().ignoringCase().contains("index"); + assertThat(cause).hasMessageThat().contains("https://console.firebase.google.com"); + } } diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/util/LogicUtils.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/util/LogicUtils.java index 616ce50730f..1f97ccc0703 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/util/LogicUtils.java +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/util/LogicUtils.java @@ -19,6 +19,8 @@ import com.google.firebase.firestore.core.CompositeFilter; import com.google.firebase.firestore.core.FieldFilter; import com.google.firebase.firestore.core.Filter; +import com.google.firebase.firestore.core.InFilter; +import com.google.firestore.v1.Value; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -286,6 +288,39 @@ protected static Filter computeDistributedNormalForm(Filter filter) { return runningResult; } + /** + * The `in` filter is only a syntactic sugar over a disjunction of equalities. For instance: `a in + * [1,2,3]` is in fact `a==1 || a==2 || a==3`. This method expands any `in` filter in the given + * input into a disjunction of equality filters and returns the expanded filter. + */ + protected static Filter computeInExpansion(Filter filter) { + assertFieldFilterOrCompositeFilter(filter); + + List expandedFilters = new ArrayList<>(); + + if (filter instanceof FieldFilter) { + if (filter instanceof InFilter) { + // We have reached a field filter with `in` operator. + for (Value value : ((InFilter) filter).getValue().getArrayValue().getValuesList()) { + expandedFilters.add( + FieldFilter.create( + ((InFilter) filter).getField(), FieldFilter.Operator.EQUAL, value)); + } + return new CompositeFilter(expandedFilters, CompositeFilter.Operator.OR); + } else { + // We have reached other kinds of field filters. + return filter; + } + } + + // We have a composite filter. + CompositeFilter compositeFilter = (CompositeFilter) filter; + for (Filter subfilter : compositeFilter.getFilters()) { + expandedFilters.add(computeInExpansion(subfilter)); + } + return new CompositeFilter(expandedFilters, compositeFilter.getOperator()); + } + /** * Given a composite filter, returns the list of terms in its disjunctive normal form. * @@ -302,7 +337,9 @@ public static List getDnfTerms(CompositeFilter filter) { return Collections.emptyList(); } - Filter result = computeDistributedNormalForm(filter); + // The `in` operator is a syntactic sugar over a disjunction of equalities. We should first + // replace such filters with equality filters before running the DNF transform. + Filter result = computeDistributedNormalForm(computeInExpansion(filter)); hardAssert( isDisjunctiveNormalForm(result), diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/core/FilterTest.java b/firebase-firestore/src/test/java/com/google/firebase/firestore/core/FilterTest.java index 5e14fdef546..1fdbc88951b 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/core/FilterTest.java +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/core/FilterTest.java @@ -56,7 +56,7 @@ public void testCompositeFilterMembers() { CompositeFilter orFilter = orFilters(A, B, C); assertTrue(orFilter.isDisjunction()); - assertEquals(andFilter.getFilters(), Arrays.asList(A, B, C)); + assertEquals(orFilter.getFilters(), Arrays.asList(A, B, C)); } @Test diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/local/QueryEngineTestCase.java b/firebase-firestore/src/test/java/com/google/firebase/firestore/local/QueryEngineTestCase.java index d3286e293f1..aedd1401c74 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/local/QueryEngineTestCase.java +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/local/QueryEngineTestCase.java @@ -640,4 +640,206 @@ public void orQueryWithArrayMembership() throws Exception { expectFullCollectionScan(() -> runQuery(query2, MISSING_LAST_LIMBO_FREE_SNAPSHOT)); assertEquals(docSet(query2.comparator(), doc1, doc4, doc6), result2); } + + @Test + public void queryWithMultipleInsOnTheSameField() throws Exception { + MutableDocument doc1 = doc("coll/1", 1, map("a", 1, "b", 0)); + MutableDocument doc2 = doc("coll/2", 1, map("b", 1)); + MutableDocument doc3 = doc("coll/3", 1, map("a", 3, "b", 2)); + MutableDocument doc4 = doc("coll/4", 1, map("a", 1, "b", 3)); + MutableDocument doc5 = doc("coll/5", 1, map("a", 1)); + MutableDocument doc6 = doc("coll/6", 1, map("a", 2)); + addDocument(doc1, doc2, doc3, doc4, doc5, doc6); + + // a IN [1,2,3] && a IN [0,1,4] should result in "a==1". + Query query1 = + query("coll") + .filter( + andFilters( + filter("a", "in", Arrays.asList(1, 2, 3)), + filter("a", "in", Arrays.asList(0, 1, 4)))); + DocumentSet result1 = + expectFullCollectionScan(() -> runQuery(query1, MISSING_LAST_LIMBO_FREE_SNAPSHOT)); + assertEquals(docSet(query1.comparator(), doc1, doc4, doc5), result1); + + // a IN [2,3] && a IN [0,1,4] is never true and so the result should be an empty set. + Query query2 = + query("coll") + .filter( + andFilters( + filter("a", "in", Arrays.asList(2, 3)), + filter("a", "in", Arrays.asList(0, 1, 4)))); + DocumentSet result2 = + expectFullCollectionScan(() -> runQuery(query2, MISSING_LAST_LIMBO_FREE_SNAPSHOT)); + assertEquals(docSet(query2.comparator()), result2); + + // a IN [0,3] || a IN [0,2] should union them (similar to: a IN [0,2,3]). + Query query3 = + query("coll") + .filter( + orFilters( + filter("a", "in", Arrays.asList(0, 3)), + filter("a", "in", Arrays.asList(0, 2)))); + + DocumentSet result3 = + expectFullCollectionScan(() -> runQuery(query3, MISSING_LAST_LIMBO_FREE_SNAPSHOT)); + assertEquals(docSet(query3.comparator(), doc3, doc6), result3); + } + + @Test + public void queryWithMultipleInsOnDifferentFields() throws Exception { + MutableDocument doc1 = doc("coll/1", 1, map("a", 1, "b", 0)); + MutableDocument doc2 = doc("coll/2", 1, map("b", 1)); + MutableDocument doc3 = doc("coll/3", 1, map("a", 3, "b", 2)); + MutableDocument doc4 = doc("coll/4", 1, map("a", 1, "b", 3)); + MutableDocument doc5 = doc("coll/5", 1, map("a", 1)); + MutableDocument doc6 = doc("coll/6", 1, map("a", 2)); + addDocument(doc1, doc2, doc3, doc4, doc5, doc6); + + Query query1 = + query("coll") + .filter( + orFilters( + filter("a", "in", Arrays.asList(2, 3)), + filter("b", "in", Arrays.asList(0, 2)))); + DocumentSet result1 = + expectFullCollectionScan(() -> runQuery(query1, MISSING_LAST_LIMBO_FREE_SNAPSHOT)); + assertEquals(docSet(query1.comparator(), doc1, doc3, doc6), result1); + + Query query2 = + query("coll") + .filter( + andFilters( + filter("a", "in", Arrays.asList(2, 3)), + filter("b", "in", Arrays.asList(0, 2)))); + DocumentSet result2 = + expectFullCollectionScan(() -> runQuery(query2, MISSING_LAST_LIMBO_FREE_SNAPSHOT)); + assertEquals(docSet(query2.comparator(), doc3), result2); + } + + @Test + public void queryInWithArrayContainsAny() throws Exception { + MutableDocument doc1 = doc("coll/1", 1, map("a", 1, "b", Arrays.asList(0))); + MutableDocument doc2 = doc("coll/2", 1, map("b", Arrays.asList(1))); + MutableDocument doc3 = doc("coll/3", 1, map("a", 3, "b", Arrays.asList(2, 7), "c", 10)); + MutableDocument doc4 = doc("coll/4", 1, map("a", 1, "b", Arrays.asList(3, 7))); + MutableDocument doc5 = doc("coll/5", 1, map("a", 1)); + MutableDocument doc6 = doc("coll/6", 1, map("a", 2, "c", 20)); + addDocument(doc1, doc2, doc3, doc4, doc5, doc6); + + Query query1 = + query("coll") + .filter( + orFilters( + filter("a", "in", Arrays.asList(2, 3)), + filter("b", "array-contains-any", Arrays.asList(0, 7)))); + DocumentSet result1 = + expectFullCollectionScan(() -> runQuery(query1, MISSING_LAST_LIMBO_FREE_SNAPSHOT)); + assertEquals(docSet(query1.comparator(), doc1, doc3, doc4, doc6), result1); + + Query query2 = + query("coll") + .filter( + andFilters( + filter("a", "in", Arrays.asList(2, 3)), + filter("b", "array-contains-any", Arrays.asList(0, 7)))); + + DocumentSet result2 = + expectFullCollectionScan(() -> runQuery(query2, MISSING_LAST_LIMBO_FREE_SNAPSHOT)); + assertEquals(docSet(query2.comparator(), doc3), result2); + + Query query3 = + query("coll") + .filter( + orFilters( + andFilters(filter("a", "in", Arrays.asList(2, 3)), filter("c", "==", 10)), + filter("b", "array-contains-any", Arrays.asList(0, 7)))); + DocumentSet result3 = + expectFullCollectionScan(() -> runQuery(query3, MISSING_LAST_LIMBO_FREE_SNAPSHOT)); + assertEquals(docSet(query3.comparator(), doc1, doc3, doc4), result3); + + Query query4 = + query("coll") + .filter( + andFilters( + filter("a", "in", Arrays.asList(2, 3)), + orFilters( + filter("b", "array-contains-any", Arrays.asList(0, 7)), + filter("c", "==", 20)))); + DocumentSet result4 = + expectFullCollectionScan(() -> runQuery(query4, MISSING_LAST_LIMBO_FREE_SNAPSHOT)); + assertEquals(docSet(query4.comparator(), doc3, doc6), result4); + } + + @Test + public void queryInWithArrayContains() throws Exception { + MutableDocument doc1 = doc("coll/1", 1, map("a", 1, "b", Arrays.asList(0))); + MutableDocument doc2 = doc("coll/2", 1, map("b", Arrays.asList(1))); + MutableDocument doc3 = doc("coll/3", 1, map("a", 3, "b", Arrays.asList(2, 7), "c", 10)); + MutableDocument doc4 = doc("coll/4", 1, map("a", 1, "b", Arrays.asList(3, 7))); + MutableDocument doc5 = doc("coll/5", 1, map("a", 1)); + MutableDocument doc6 = doc("coll/6", 1, map("a", 2, "c", 20)); + addDocument(doc1, doc2, doc3, doc4, doc5, doc6); + + Query query1 = + query("coll") + .filter( + orFilters( + filter("a", "in", Arrays.asList(2, 3)), filter("b", "array-contains", 3))); + DocumentSet result1 = + expectFullCollectionScan(() -> runQuery(query1, MISSING_LAST_LIMBO_FREE_SNAPSHOT)); + assertEquals(docSet(query1.comparator(), doc3, doc4, doc6), result1); + + Query query2 = + query("coll") + .filter( + andFilters( + filter("a", "in", Arrays.asList(2, 3)), filter("b", "array-contains", 7))); + + DocumentSet result2 = + expectFullCollectionScan(() -> runQuery(query2, MISSING_LAST_LIMBO_FREE_SNAPSHOT)); + assertEquals(docSet(query2.comparator(), doc3), result2); + + Query query3 = + query("coll") + .filter( + orFilters( + filter("a", "in", Arrays.asList(2, 3)), + andFilters(filter("b", "array-contains", 3), filter("a", "==", 1)))); + DocumentSet result3 = + expectFullCollectionScan(() -> runQuery(query3, MISSING_LAST_LIMBO_FREE_SNAPSHOT)); + assertEquals(docSet(query3.comparator(), doc3, doc4, doc6), result3); + + Query query4 = + query("coll") + .filter( + andFilters( + filter("a", "in", Arrays.asList(2, 3)), + orFilters(filter("b", "array-contains", 7), filter("a", "==", 1)))); + DocumentSet result4 = + expectFullCollectionScan(() -> runQuery(query4, MISSING_LAST_LIMBO_FREE_SNAPSHOT)); + assertEquals(docSet(query4.comparator(), doc3), result4); + } + + @Test + public void orderByEquality() throws Exception { + MutableDocument doc1 = doc("coll/1", 1, map("a", 1, "b", Arrays.asList(0))); + MutableDocument doc2 = doc("coll/2", 1, map("b", Arrays.asList(1))); + MutableDocument doc3 = doc("coll/3", 1, map("a", 3, "b", Arrays.asList(2, 7), "c", 10)); + MutableDocument doc4 = doc("coll/4", 1, map("a", 1, "b", Arrays.asList(3, 7))); + MutableDocument doc5 = doc("coll/5", 1, map("a", 1)); + MutableDocument doc6 = doc("coll/6", 1, map("a", 2, "c", 20)); + addDocument(doc1, doc2, doc3, doc4, doc5, doc6); + + Query query1 = query("coll").filter(filter("a", "==", 1)).orderBy(orderBy("a")); + DocumentSet result1 = + expectFullCollectionScan(() -> runQuery(query1, MISSING_LAST_LIMBO_FREE_SNAPSHOT)); + assertEquals(docSet(query1.comparator(), doc1, doc4, doc5), result1); + + Query query2 = + query("coll").filter(filter("a", "in", Arrays.asList(2, 3))).orderBy(orderBy("a")); + DocumentSet result2 = + expectFullCollectionScan(() -> runQuery(query2, MISSING_LAST_LIMBO_FREE_SNAPSHOT)); + assertEquals(docSet(query2.comparator(), doc6, doc3), result2); + } } diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/local/SQLiteQueryEngineTest.java b/firebase-firestore/src/test/java/com/google/firebase/firestore/local/SQLiteQueryEngineTest.java index f63e1c2a13d..06f1c80f879 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/local/SQLiteQueryEngineTest.java +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/local/SQLiteQueryEngineTest.java @@ -277,4 +277,265 @@ public void orQueryWithArrayMembershipUsingIndexes() throws Exception { expectOptimizedCollectionScan(() -> runQuery(query2, SnapshotVersion.NONE)); assertEquals(docSet(query2.comparator(), doc1, doc4, doc6), result2); } + + @Test + public void queryWithMultipleInsOnTheSameField() throws Exception { + MutableDocument doc1 = doc("coll/1", 1, map("a", 1, "b", 0)); + MutableDocument doc2 = doc("coll/2", 1, map("b", 1)); + MutableDocument doc3 = doc("coll/3", 1, map("a", 3, "b", 2)); + MutableDocument doc4 = doc("coll/4", 1, map("a", 1, "b", 3)); + MutableDocument doc5 = doc("coll/5", 1, map("a", 1)); + MutableDocument doc6 = doc("coll/6", 1, map("a", 2)); + addDocument(doc1, doc2, doc3, doc4, doc5, doc6); + indexManager.addFieldIndex(fieldIndex("coll", "a", Kind.ASCENDING)); + indexManager.addFieldIndex(fieldIndex("coll", "a", Kind.DESCENDING)); + indexManager.addFieldIndex(fieldIndex("coll", "b", Kind.ASCENDING)); + indexManager.addFieldIndex(fieldIndex("coll", "b", Kind.DESCENDING)); + indexManager.updateIndexEntries(docMap(doc1, doc2, doc3, doc4, doc5, doc6)); + indexManager.updateCollectionGroup("coll", IndexOffset.fromDocument(doc6)); + + // a IN [1,2,3] && a IN [0,1,4] should result in "a==1". + Query query1 = + query("coll") + .filter( + andFilters( + filter("a", "in", Arrays.asList(1, 2, 3)), + filter("a", "in", Arrays.asList(0, 1, 4)))); + DocumentSet result1 = + expectOptimizedCollectionScan(() -> runQuery(query1, SnapshotVersion.NONE)); + assertEquals(docSet(query1.comparator(), doc1, doc4, doc5), result1); + + // a IN [2,3] && a IN [0,1,4] is never true and so the result should be an empty set. + Query query2 = + query("coll") + .filter( + andFilters( + filter("a", "in", Arrays.asList(2, 3)), + filter("a", "in", Arrays.asList(0, 1, 4)))); + + DocumentSet result2 = + expectOptimizedCollectionScan(() -> runQuery(query2, SnapshotVersion.NONE)); + assertEquals(docSet(query2.comparator()), result2); + + // a IN [0,3] || a IN [0,2] should union them (similar to: a IN [0,2,3]). + Query query3 = + query("coll") + .filter( + orFilters( + filter("a", "in", Arrays.asList(0, 3)), + filter("a", "in", Arrays.asList(0, 2)))); + + DocumentSet result3 = + expectOptimizedCollectionScan(() -> runQuery(query3, SnapshotVersion.NONE)); + assertEquals(docSet(query3.comparator(), doc3, doc6), result3); + + // Nested composite filter: (a IN [0,1,2,3] && (a IN [0,2] || (b>1 && a IN [1,3])) + Query query4 = + query("coll") + .filter( + andFilters( + filter("a", "in", Arrays.asList(0, 1, 2, 3)), + orFilters( + filter("a", "in", Arrays.asList(0, 2)), + andFilters(filter("b", ">=", 1), filter("a", "in", Arrays.asList(1, 3)))))); + + DocumentSet result4 = + expectOptimizedCollectionScan(() -> runQuery(query4, SnapshotVersion.NONE)); + assertEquals(docSet(query4.comparator(), doc3, doc4), result4); + } + + @Test + public void queryWithMultipleInsOnDifferentFields() throws Exception { + MutableDocument doc1 = doc("coll/1", 1, map("a", 1, "b", 0)); + MutableDocument doc2 = doc("coll/2", 1, map("b", 1)); + MutableDocument doc3 = doc("coll/3", 1, map("a", 3, "b", 2)); + MutableDocument doc4 = doc("coll/4", 1, map("a", 1, "b", 3)); + MutableDocument doc5 = doc("coll/5", 1, map("a", 1)); + MutableDocument doc6 = doc("coll/6", 1, map("a", 2)); + addDocument(doc1, doc2, doc3, doc4, doc5, doc6); + indexManager.addFieldIndex(fieldIndex("coll", "a", Kind.ASCENDING)); + indexManager.addFieldIndex(fieldIndex("coll", "a", Kind.DESCENDING)); + indexManager.addFieldIndex(fieldIndex("coll", "b", Kind.ASCENDING)); + indexManager.addFieldIndex(fieldIndex("coll", "b", Kind.DESCENDING)); + indexManager.updateIndexEntries(docMap(doc1, doc2, doc3, doc4, doc5, doc6)); + indexManager.updateCollectionGroup("coll", IndexOffset.fromDocument(doc6)); + + Query query1 = + query("coll") + .filter( + orFilters( + filter("a", "in", Arrays.asList(2, 3)), + filter("b", "in", Arrays.asList(0, 2)))); + DocumentSet result1 = + expectOptimizedCollectionScan(() -> runQuery(query1, SnapshotVersion.NONE)); + assertEquals(docSet(query1.comparator(), doc1, doc3, doc6), result1); + + Query query2 = + query("coll") + .filter( + andFilters( + filter("a", "in", Arrays.asList(2, 3)), + filter("b", "in", Arrays.asList(0, 2)))); + + DocumentSet result2 = + expectOptimizedCollectionScan(() -> runQuery(query2, SnapshotVersion.NONE)); + assertEquals(docSet(query2.comparator(), doc3), result2); + + // Nested composite filter: (b in [0,3] && (b IN [1] || (b in [2,3] && a IN [1,3])) + Query query3 = + query("coll") + .filter( + andFilters( + filter("b", "in", Arrays.asList(0, 3)), + orFilters( + filter("b", "in", Arrays.asList(1)), + andFilters( + filter("b", "in", Arrays.asList(2, 3)), + filter("a", "in", Arrays.asList(1, 3)))))); + + DocumentSet result3 = + expectOptimizedCollectionScan(() -> runQuery(query3, SnapshotVersion.NONE)); + assertEquals(docSet(query3.comparator(), doc4), result3); + } + + @Test + public void queryInWithArrayContainsAny() throws Exception { + MutableDocument doc1 = doc("coll/1", 1, map("a", 1, "b", Arrays.asList(0))); + MutableDocument doc2 = doc("coll/2", 1, map("b", Arrays.asList(1))); + MutableDocument doc3 = doc("coll/3", 1, map("a", 3, "b", Arrays.asList(2, 7), "c", 10)); + MutableDocument doc4 = doc("coll/4", 1, map("a", 1, "b", Arrays.asList(3, 7))); + MutableDocument doc5 = doc("coll/5", 1, map("a", 1)); + MutableDocument doc6 = doc("coll/6", 1, map("a", 2, "c", 20)); + addDocument(doc1, doc2, doc3, doc4, doc5, doc6); + indexManager.addFieldIndex(fieldIndex("coll", "a", Kind.ASCENDING)); + indexManager.addFieldIndex(fieldIndex("coll", "a", Kind.DESCENDING)); + indexManager.addFieldIndex(fieldIndex("coll", "b", Kind.CONTAINS)); + indexManager.updateIndexEntries(docMap(doc1, doc2, doc3, doc4, doc5, doc6)); + indexManager.updateCollectionGroup("coll", IndexOffset.fromDocument(doc6)); + + Query query1 = + query("coll") + .filter( + orFilters( + filter("a", "in", Arrays.asList(2, 3)), + filter("b", "array-contains-any", Arrays.asList(0, 7)))); + DocumentSet result1 = + expectOptimizedCollectionScan(() -> runQuery(query1, SnapshotVersion.NONE)); + assertEquals(docSet(query1.comparator(), doc1, doc3, doc4, doc6), result1); + + Query query2 = + query("coll") + .filter( + andFilters( + filter("a", "in", Arrays.asList(2, 3)), + filter("b", "array-contains-any", Arrays.asList(0, 7)))); + + DocumentSet result2 = + expectOptimizedCollectionScan(() -> runQuery(query2, SnapshotVersion.NONE)); + assertEquals(docSet(query2.comparator(), doc3), result2); + + Query query3 = + query("coll") + .filter( + orFilters( + andFilters(filter("a", "in", Arrays.asList(2, 3)), filter("c", "==", 10)), + filter("b", "array-contains-any", Arrays.asList(0, 7)))); + DocumentSet result3 = + expectOptimizedCollectionScan(() -> runQuery(query3, SnapshotVersion.NONE)); + assertEquals(docSet(query3.comparator(), doc1, doc3, doc4), result3); + + Query query4 = + query("coll") + .filter( + andFilters( + filter("a", "in", Arrays.asList(2, 3)), + orFilters( + filter("b", "array-contains-any", Arrays.asList(0, 7)), + filter("c", "==", 20)))); + DocumentSet result4 = + expectOptimizedCollectionScan(() -> runQuery(query4, SnapshotVersion.NONE)); + assertEquals(docSet(query4.comparator(), doc3, doc6), result4); + } + + @Test + public void queryInWithArrayContains() throws Exception { + MutableDocument doc1 = doc("coll/1", 1, map("a", 1, "b", Arrays.asList(0))); + MutableDocument doc2 = doc("coll/2", 1, map("b", Arrays.asList(1))); + MutableDocument doc3 = doc("coll/3", 1, map("a", 3, "b", Arrays.asList(2, 7), "c", 10)); + MutableDocument doc4 = doc("coll/4", 1, map("a", 1, "b", Arrays.asList(3, 7))); + MutableDocument doc5 = doc("coll/5", 1, map("a", 1)); + MutableDocument doc6 = doc("coll/6", 1, map("a", 2, "c", 20)); + addDocument(doc1, doc2, doc3, doc4, doc5, doc6); + indexManager.addFieldIndex(fieldIndex("coll", "a", Kind.ASCENDING)); + indexManager.addFieldIndex(fieldIndex("coll", "a", Kind.DESCENDING)); + indexManager.addFieldIndex(fieldIndex("coll", "b", Kind.CONTAINS)); + indexManager.updateIndexEntries(docMap(doc1, doc2, doc3, doc4, doc5, doc6)); + indexManager.updateCollectionGroup("coll", IndexOffset.fromDocument(doc6)); + + Query query1 = + query("coll") + .filter( + orFilters( + filter("a", "in", Arrays.asList(2, 3)), filter("b", "array-contains", 3))); + DocumentSet result1 = + expectOptimizedCollectionScan(() -> runQuery(query1, SnapshotVersion.NONE)); + assertEquals(docSet(query1.comparator(), doc3, doc4, doc6), result1); + + Query query2 = + query("coll") + .filter( + andFilters( + filter("a", "in", Arrays.asList(2, 3)), filter("b", "array-contains", 7))); + + DocumentSet result2 = + expectOptimizedCollectionScan(() -> runQuery(query2, SnapshotVersion.NONE)); + assertEquals(docSet(query2.comparator(), doc3), result2); + + Query query3 = + query("coll") + .filter( + orFilters( + filter("a", "in", Arrays.asList(2, 3)), + andFilters(filter("b", "array-contains", 3), filter("a", "==", 1)))); + DocumentSet result3 = + expectOptimizedCollectionScan(() -> runQuery(query3, SnapshotVersion.NONE)); + assertEquals(docSet(query3.comparator(), doc3, doc4, doc6), result3); + + Query query4 = + query("coll") + .filter( + andFilters( + filter("a", "in", Arrays.asList(2, 3)), + orFilters(filter("b", "array-contains", 7), filter("a", "==", 1)))); + DocumentSet result4 = + expectOptimizedCollectionScan(() -> runQuery(query4, SnapshotVersion.NONE)); + assertEquals(docSet(query4.comparator(), doc3), result4); + } + + @Test + public void orderByEquality() throws Exception { + MutableDocument doc1 = doc("coll/1", 1, map("a", 1, "b", Arrays.asList(0))); + MutableDocument doc2 = doc("coll/2", 1, map("b", Arrays.asList(1))); + MutableDocument doc3 = doc("coll/3", 1, map("a", 3, "b", Arrays.asList(2, 7), "c", 10)); + MutableDocument doc4 = doc("coll/4", 1, map("a", 1, "b", Arrays.asList(3, 7))); + MutableDocument doc5 = doc("coll/5", 1, map("a", 1)); + MutableDocument doc6 = doc("coll/6", 1, map("a", 2, "c", 20)); + addDocument(doc1, doc2, doc3, doc4, doc5, doc6); + indexManager.addFieldIndex(fieldIndex("coll", "a", Kind.ASCENDING)); + indexManager.addFieldIndex(fieldIndex("coll", "a", Kind.DESCENDING)); + indexManager.addFieldIndex(fieldIndex("coll", "b", Kind.CONTAINS)); + indexManager.updateIndexEntries(docMap(doc1, doc2, doc3, doc4, doc5, doc6)); + indexManager.updateCollectionGroup("coll", IndexOffset.fromDocument(doc6)); + + Query query1 = query("coll").filter(filter("a", "==", 1)).orderBy(orderBy("a")); + DocumentSet result1 = + expectOptimizedCollectionScan(() -> runQuery(query1, SnapshotVersion.NONE)); + assertEquals(docSet(query1.comparator(), doc1, doc4, doc5), result1); + + Query query2 = + query("coll").filter(filter("a", "in", Arrays.asList(2, 3))).orderBy(orderBy("a")); + DocumentSet result2 = + expectOptimizedCollectionScan(() -> runQuery(query2, SnapshotVersion.NONE)); + assertEquals(docSet(query2.comparator(), doc6, doc3), result2); + } } diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/util/LogicUtilsTest.java b/firebase-firestore/src/test/java/com/google/firebase/firestore/util/LogicUtilsTest.java index e48a8f92f53..89b8ca99ee3 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/util/LogicUtilsTest.java +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/util/LogicUtilsTest.java @@ -21,6 +21,7 @@ import static com.google.firebase.firestore.util.LogicUtils.applyAssociation; import static com.google.firebase.firestore.util.LogicUtils.applyDistribution; import static com.google.firebase.firestore.util.LogicUtils.computeDistributedNormalForm; +import static com.google.firebase.firestore.util.LogicUtils.computeInExpansion; import static com.google.firebase.firestore.util.LogicUtils.getDnfTerms; import static org.junit.Assert.assertEquals; @@ -275,4 +276,79 @@ public void testComputeDnf8() { assertThat(computeDistributedNormalForm(compositeFilter)).isEqualTo(expectedResult); assertThat(getDnfTerms(compositeFilter)).isEqualTo(Arrays.asList(expectedDnfTerms)); } + + @Test + public void testInExpansionForFieldFilters() { + FieldFilter input1 = filter("a", "in", Arrays.asList(1, 2, 3)); + FieldFilter input2 = filter("a", "<", 1); + FieldFilter input3 = filter("a", "<=", 1); + FieldFilter input4 = filter("a", "==", 1); + FieldFilter input5 = filter("a", "!=", 1); + FieldFilter input6 = filter("a", ">", 1); + FieldFilter input7 = filter("a", ">=", 1); + FieldFilter input8 = filter("a", "array-contains", 1); + FieldFilter input9 = filter("a", "array-contains-any", Arrays.asList(1, 2)); + FieldFilter input10 = filter("a", "not-in", Arrays.asList(1, 2)); + + assertThat(computeInExpansion(input1)) + .isEqualTo(orFilters(filter("a", "==", 1), filter("a", "==", 2), filter("a", "==", 3))); + + // Other operators should remain the same + assertThat(computeInExpansion(input2)).isEqualTo(input2); + assertThat(computeInExpansion(input3)).isEqualTo(input3); + assertThat(computeInExpansion(input4)).isEqualTo(input4); + assertThat(computeInExpansion(input5)).isEqualTo(input5); + assertThat(computeInExpansion(input6)).isEqualTo(input6); + assertThat(computeInExpansion(input7)).isEqualTo(input7); + assertThat(computeInExpansion(input8)).isEqualTo(input8); + assertThat(computeInExpansion(input9)).isEqualTo(input9); + assertThat(computeInExpansion(input10)).isEqualTo(input10); + } + + @Test + public void testInExpansionForCompositeFilters() { + CompositeFilter cf1 = + andFilters(filter("a", "==", 1), filter("b", "in", Arrays.asList(2, 3, 4))); + + assertThat(computeInExpansion(cf1)) + .isEqualTo( + andFilters( + filter("a", "==", 1), + orFilters(filter("b", "==", 2), filter("b", "==", 3), filter("b", "==", 4)))); + + CompositeFilter cf2 = + orFilters(filter("a", "==", 1), filter("b", "in", Arrays.asList(2, 3, 4))); + + assertThat(computeInExpansion(cf2)) + .isEqualTo( + orFilters( + filter("a", "==", 1), + orFilters(filter("b", "==", 2), filter("b", "==", 3), filter("b", "==", 4)))); + + CompositeFilter cf3 = + andFilters( + filter("a", "==", 1), + orFilters(filter("b", "==", 2), filter("c", "in", Arrays.asList(2, 3, 4)))); + + assertThat(computeInExpansion(cf3)) + .isEqualTo( + andFilters( + filter("a", "==", 1), + orFilters( + filter("b", "==", 2), + orFilters(filter("c", "==", 2), filter("c", "==", 3), filter("c", "==", 4))))); + + CompositeFilter cf4 = + orFilters( + filter("a", "==", 1), + andFilters(filter("b", "==", 2), filter("c", "in", Arrays.asList(2, 3, 4)))); + + assertThat(computeInExpansion(cf4)) + .isEqualTo( + orFilters( + filter("a", "==", 1), + andFilters( + filter("b", "==", 2), + orFilters(filter("c", "==", 2), filter("c", "==", 3), filter("c", "==", 4))))); + } } diff --git a/firebase-firestore/src/test/resources/json/bundle_spec_test.json b/firebase-firestore/src/test/resources/json/bundle_spec_test.json index 1e21bbea06a..e45f01e937a 100644 --- a/firebase-firestore/src/test/resources/json/bundle_spec_test.json +++ b/firebase-firestore/src/test/resources/json/bundle_spec_test.json @@ -1386,7 +1386,7 @@ "value": { "value": "patched" }, - "version": 250 + "version": 0 } ], "query": { @@ -1670,7 +1670,7 @@ "value": { "value": "patched" }, - "version": 250 + "version": 0 } ], "query": { diff --git a/firebase-firestore/src/test/resources/json/existence_filter_spec_test.json b/firebase-firestore/src/test/resources/json/existence_filter_spec_test.json index 5798b877996..3083b762224 100644 --- a/firebase-firestore/src/test/resources/json/existence_filter_spec_test.json +++ b/firebase-firestore/src/test/resources/json/existence_filter_spec_test.json @@ -1,4 +1,240 @@ { + "Existence filter clears resume token": { + "describeName": "Existence Filters:", + "itName": "Existence filter clears resume token", + "tags": [ + "durable-persistence" + ], + "config": { + "numClients": 1, + "useGarbageCollection": true + }, + "steps": [ + { + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + { + "key": "collection/1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/2", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 1000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "key": "collection/1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/2", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "watchFilter": [ + [ + 2 + ], + "collection/1" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 2000 + }, + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "restart": true, + "expectedState": { + "activeLimboDocs": [ + ], + "activeTargets": { + }, + "enqueuedLimboDocs": [ + ] + } + }, + { + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "key": "collection/1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/2", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + } + ] + }, "Existence filter handled at global snapshot": { "describeName": "Existence Filters:", "itName": "Existence filter handled at global snapshot", @@ -1072,242 +1308,6 @@ } ] }, - "Existence filter mismatch invalidates index-free query": { - "describeName": "Existence Filters:", - "itName": "Existence filter clears resume token", - "tags": [ - "durable-persistence" - ], - "config": { - "numClients": 1, - "useGarbageCollection": true - }, - "steps": [ - { - "userListen": { - "query": { - "filters": [ - ], - "orderBys": [ - ], - "path": "collection" - }, - "targetId": 2 - }, - "expectedState": { - "activeTargets": { - "2": { - "queries": [ - { - "filters": [ - ], - "orderBys": [ - ], - "path": "collection" - } - ], - "resumeToken": "" - } - } - } - }, - { - "watchAck": [ - 2 - ] - }, - { - "watchEntity": { - "docs": [ - { - "key": "collection/1", - "options": { - "hasCommittedMutations": false, - "hasLocalMutations": false - }, - "value": { - "v": 1 - }, - "version": 1000 - }, - { - "key": "collection/2", - "options": { - "hasCommittedMutations": false, - "hasLocalMutations": false - }, - "value": { - "v": 2 - }, - "version": 1000 - } - ], - "targets": [ - 2 - ] - } - }, - { - "watchCurrent": [ - [ - 2 - ], - "resume-token-1000" - ] - }, - { - "watchSnapshot": { - "targetIds": [ - ], - "version": 1000 - }, - "expectedSnapshotEvents": [ - { - "added": [ - { - "key": "collection/1", - "options": { - "hasCommittedMutations": false, - "hasLocalMutations": false - }, - "value": { - "v": 1 - }, - "version": 1000 - }, - { - "key": "collection/2", - "options": { - "hasCommittedMutations": false, - "hasLocalMutations": false - }, - "value": { - "v": 2 - }, - "version": 1000 - } - ], - "errorCode": 0, - "fromCache": false, - "hasPendingWrites": false, - "query": { - "filters": [ - ], - "orderBys": [ - ], - "path": "collection" - } - } - ] - }, - { - "watchFilter": [ - [ - 2 - ], - "collection/1" - ] - }, - { - "watchSnapshot": { - "targetIds": [ - ], - "version": 2000 - }, - "expectedSnapshotEvents": [ - { - "errorCode": 0, - "fromCache": true, - "hasPendingWrites": false, - "query": { - "filters": [ - ], - "orderBys": [ - ], - "path": "collection" - } - } - ] - }, - { - "restart": true, - "expectedState": { - "activeLimboDocs": [ - ], - "activeTargets": { - }, - "enqueuedLimboDocs": [ - ] - } - }, - { - "userListen": { - "query": { - "filters": [ - ], - "orderBys": [ - ], - "path": "collection" - }, - "targetId": 2 - }, - "expectedSnapshotEvents": [ - { - "added": [ - { - "key": "collection/1", - "options": { - "hasCommittedMutations": false, - "hasLocalMutations": false - }, - "value": { - "v": 1 - }, - "version": 1000 - }, - { - "key": "collection/2", - "options": { - "hasCommittedMutations": false, - "hasLocalMutations": false - }, - "value": { - "v": 2 - }, - "version": 1000 - } - ], - "errorCode": 0, - "fromCache": true, - "hasPendingWrites": false, - "query": { - "filters": [ - ], - "orderBys": [ - ], - "path": "collection" - } - } - ], - "expectedState": { - "activeTargets": { - "2": { - "queries": [ - { - "filters": [ - ], - "orderBys": [ - ], - "path": "collection" - } - ], - "resumeToken": "" - } - } - } - } - ] - }, "Existence filter mismatch triggers re-run of query": { "describeName": "Existence Filters:", "itName": "Existence filter mismatch triggers re-run of query", diff --git a/firebase-firestore/src/test/resources/json/index_spec_test.json b/firebase-firestore/src/test/resources/json/index_spec_test.json new file mode 100644 index 00000000000..885b3357fed --- /dev/null +++ b/firebase-firestore/src/test/resources/json/index_spec_test.json @@ -0,0 +1,252 @@ +{ + "Index Creation succeeds even if not primary": { + "describeName": "Client Side Index", + "itName": "Index Creation succeeds even if not primary", + "tags": [ + "multi-client" + ], + "config": { + "numClients": 2, + "useGarbageCollection": false + }, + "steps": [ + { + "clientIndex": 0, + "drainQueue": true, + "expectedState": { + "isPrimary": true + } + }, + { + "clientIndex": 1, + "drainQueue": true, + "expectedState": { + "isPrimary": false + } + }, + { + "clientIndex": 1, + "setIndexConfiguration": { + "indexes": [ + { + "collectionGroup": "restaurants", + "fields": [ + { + "fieldPath": "price", + "order": "ASCENDING" + } + ], + "queryScope": "COLLECTION" + } + ] + }, + "expectedState": { + "indexes": [ + { + "collectionGroup": "restaurants", + "fields": [ + { + "fieldPath": { + "len": 1, + "offset": 0, + "segments": [ + "price" + ] + }, + "kind": 0 + } + ], + "indexId": -1, + "indexState": { + "offset": { + "documentKey": { + "path": { + "len": 0, + "offset": 0, + "segments": [ + ] + } + }, + "largestBatchId": -1, + "readTime": { + "timestamp": { + "nanoseconds": 0, + "seconds": 0 + } + } + }, + "sequenceNumber": 0 + } + } + ] + } + }, + { + "clientIndex": 0, + "drainQueue": true, + "expectedState": { + "indexes": [ + { + "collectionGroup": "restaurants", + "fields": [ + { + "fieldPath": { + "len": 1, + "offset": 0, + "segments": [ + "price" + ] + }, + "kind": 0 + } + ], + "indexId": -1, + "indexState": { + "offset": { + "documentKey": { + "path": { + "len": 0, + "offset": 0, + "segments": [ + ] + } + }, + "largestBatchId": -1, + "readTime": { + "timestamp": { + "nanoseconds": 0, + "seconds": 0 + } + } + }, + "sequenceNumber": 0 + } + } + ] + } + } + ] + }, + "Index Creation visible on all clients": { + "describeName": "Client Side Index", + "itName": "Index Creation visible on all clients", + "tags": [ + "multi-client" + ], + "config": { + "numClients": 2, + "useGarbageCollection": false + }, + "steps": [ + { + "clientIndex": 0, + "drainQueue": true, + "expectedState": { + "isPrimary": true + } + }, + { + "clientIndex": 0, + "setIndexConfiguration": { + "indexes": [ + { + "collectionGroup": "restaurants", + "fields": [ + { + "fieldPath": "price", + "order": "ASCENDING" + } + ], + "queryScope": "COLLECTION" + } + ] + }, + "expectedState": { + "indexes": [ + { + "collectionGroup": "restaurants", + "fields": [ + { + "fieldPath": { + "len": 1, + "offset": 0, + "segments": [ + "price" + ] + }, + "kind": 0 + } + ], + "indexId": -1, + "indexState": { + "offset": { + "documentKey": { + "path": { + "len": 0, + "offset": 0, + "segments": [ + ] + } + }, + "largestBatchId": -1, + "readTime": { + "timestamp": { + "nanoseconds": 0, + "seconds": 0 + } + } + }, + "sequenceNumber": 0 + } + } + ] + } + }, + { + "clientIndex": 1, + "drainQueue": true, + "expectedState": { + "indexes": [ + { + "collectionGroup": "restaurants", + "fields": [ + { + "fieldPath": { + "len": 1, + "offset": 0, + "segments": [ + "price" + ] + }, + "kind": 0 + } + ], + "indexId": -1, + "indexState": { + "offset": { + "documentKey": { + "path": { + "len": 0, + "offset": 0, + "segments": [ + ] + } + }, + "largestBatchId": -1, + "readTime": { + "timestamp": { + "nanoseconds": 0, + "seconds": 0 + } + } + }, + "sequenceNumber": 0 + } + } + ], + "isPrimary": false + } + } + ] + } +} diff --git a/firebase-firestore/src/test/resources/json/limbo_spec_test.json b/firebase-firestore/src/test/resources/json/limbo_spec_test.json index 57f57e909e5..0b6abe08a2b 100644 --- a/firebase-firestore/src/test/resources/json/limbo_spec_test.json +++ b/firebase-firestore/src/test/resources/json/limbo_spec_test.json @@ -2627,7 +2627,7 @@ "matches": true, "modified": true }, - "version": 1000 + "version": 0 } ], "query": { @@ -2690,7 +2690,7 @@ "matches": true, "modified": true }, - "version": 1000 + "version": 0 } ], "errorCode": 0, diff --git a/firebase-firestore/src/test/resources/json/orderby_spec_test.json b/firebase-firestore/src/test/resources/json/orderby_spec_test.json index 858919c3721..6c395803606 100644 --- a/firebase-firestore/src/test/resources/json/orderby_spec_test.json +++ b/firebase-firestore/src/test/resources/json/orderby_spec_test.json @@ -154,7 +154,7 @@ "key": "b", "sort": 2 }, - "version": 1001 + "version": 0 } ], "errorCode": 0, diff --git a/firebase-firestore/src/test/resources/json/perf_spec_test.json b/firebase-firestore/src/test/resources/json/perf_spec_test.json index 5276fb92fb9..8dc52457023 100644 --- a/firebase-firestore/src/test/resources/json/perf_spec_test.json +++ b/firebase-firestore/src/test/resources/json/perf_spec_test.json @@ -224390,7 +224390,7 @@ "value": { "v": 1 }, - "version": 2 + "version": 0 } ], "query": { @@ -224493,7 +224493,7 @@ "value": { "v": 2 }, - "version": 4 + "version": 0 } ], "query": { @@ -224596,7 +224596,7 @@ "value": { "v": 3 }, - "version": 6 + "version": 0 } ], "query": { @@ -224699,7 +224699,7 @@ "value": { "v": 4 }, - "version": 8 + "version": 0 } ], "query": { @@ -224802,7 +224802,7 @@ "value": { "v": 5 }, - "version": 10 + "version": 0 } ], "query": { @@ -224905,7 +224905,7 @@ "value": { "v": 6 }, - "version": 12 + "version": 0 } ], "query": { @@ -225008,7 +225008,7 @@ "value": { "v": 7 }, - "version": 14 + "version": 0 } ], "query": { @@ -225111,7 +225111,7 @@ "value": { "v": 8 }, - "version": 16 + "version": 0 } ], "query": { @@ -225214,7 +225214,7 @@ "value": { "v": 9 }, - "version": 18 + "version": 0 } ], "query": { @@ -225317,7 +225317,7 @@ "value": { "v": 10 }, - "version": 20 + "version": 0 } ], "query": { diff --git a/firebase-firestore/src/test/resources/json/query_spec_test.json b/firebase-firestore/src/test/resources/json/query_spec_test.json index f2c1ce51e6c..18ade7a1e2c 100644 --- a/firebase-firestore/src/test/resources/json/query_spec_test.json +++ b/firebase-firestore/src/test/resources/json/query_spec_test.json @@ -1493,7 +1493,7 @@ "value": { "match": true }, - "version": 1000 + "version": 0 } ], "query": { @@ -1534,7 +1534,7 @@ "value": { "match": true }, - "version": 1000 + "version": 0 } ], "errorCode": 0, diff --git a/firebase-firestore/src/test/resources/json/recovery_spec_test.json b/firebase-firestore/src/test/resources/json/recovery_spec_test.json index c42d58c9d41..9ae9f2349ff 100644 --- a/firebase-firestore/src/test/resources/json/recovery_spec_test.json +++ b/firebase-firestore/src/test/resources/json/recovery_spec_test.json @@ -1012,6 +1012,20 @@ }, "targetId": 2 }, + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], "expectedState": { "activeTargets": { "2": { diff --git a/firebase-firestore/src/test/resources/json/write_spec_test.json b/firebase-firestore/src/test/resources/json/write_spec_test.json index 4ad3f39f4be..dc5c135b72f 100644 --- a/firebase-firestore/src/test/resources/json/write_spec_test.json +++ b/firebase-firestore/src/test/resources/json/write_spec_test.json @@ -126,7 +126,7 @@ "value": { "v": 2 }, - "version": 1000 + "version": 0 } ], "query": { @@ -336,7 +336,7 @@ "value": { "v": 3 }, - "version": 1000 + "version": 0 } ], "query": { @@ -597,7 +597,7 @@ "value": { "v": 2 }, - "version": 1000 + "version": 0 } ], "query": { @@ -1092,7 +1092,7 @@ }, "v": 2 }, - "version": 1000 + "version": 0 } ], "query": { @@ -1308,7 +1308,7 @@ "value": { "v": 2 }, - "version": 1000 + "version": 0 } ], "query": { @@ -1567,7 +1567,7 @@ "value": { "v": 2 }, - "version": 1000 + "version": 0 } ], "query": { @@ -1858,7 +1858,7 @@ "local": 5, "remote": 2 }, - "version": 2000 + "version": 0 } ], "query": { @@ -5547,7 +5547,7 @@ "value": { "v": 2 }, - "version": 1000 + "version": 0 } ], "query": { @@ -5650,7 +5650,7 @@ "value": { "v": 2 }, - "version": 500 + "version": 0 } ], "query": { diff --git a/firebase-functions/CHANGELOG.md b/firebase-functions/CHANGELOG.md index 5e3bddd2072..a691e830912 100644 --- a/firebase-functions/CHANGELOG.md +++ b/firebase-functions/CHANGELOG.md @@ -1,5 +1,13 @@ # Unreleased +* [changed] Updated dependency of `firebase-iid` to its latest + version (v21.1.0). + +## Kotlin +The Kotlin extensions library transitively includes the updated +`firebase-functions` library. The Kotlin extensions library has no additional +updates. + # 20.2.0 * [unchanged] Updated to accommodate the release of the updated [functions_client] Kotlin extensions library. diff --git a/firebase-functions/firebase-functions.gradle b/firebase-functions/firebase-functions.gradle index 00689311183..7cfe379f959 100644 --- a/firebase-functions/firebase-functions.gradle +++ b/firebase-functions/firebase-functions.gradle @@ -56,13 +56,14 @@ dependencies { implementation 'com.google.android.gms:play-services-basement:18.1.0' implementation 'com.google.android.gms:play-services-base:18.0.1' implementation 'com.google.android.gms:play-services-tasks:18.0.1' - implementation ('com.google.firebase:firebase-iid:20.0.1') { - exclude group: 'com.google.firebase', module: 'firebase-common' + implementation ('com.google.firebase:firebase-iid:21.1.0') { + exclude group: "com.google.firebase", module: "firebase-common" + exclude group: "com.google.firebase", module: "firebase-components" } implementation ('com.google.firebase:firebase-auth-interop:18.0.0') { exclude group: 'com.google.firebase', module: 'firebase-common' } - implementation 'com.google.firebase:firebase-iid-interop:17.0.0' + implementation 'com.google.firebase:firebase-iid-interop:17.1.0' implementation 'com.squareup.okhttp3:okhttp:3.12.1' diff --git a/firebase-functions/gradle.properties b/firebase-functions/gradle.properties index 3ed71215d9c..165d1ccbf35 100644 --- a/firebase-functions/gradle.properties +++ b/firebase-functions/gradle.properties @@ -1,3 +1,3 @@ -version=20.1.2 -latestReleasedVersion=20.1.1 +version=20.2.1 +latestReleasedVersion=20.2.0 android.enableUnitTestBinaryResources=true diff --git a/firebase-inappmessaging-display/gradle.properties b/firebase-inappmessaging-display/gradle.properties index a23fa35b103..3b49e0fc3a8 100644 --- a/firebase-inappmessaging-display/gradle.properties +++ b/firebase-inappmessaging-display/gradle.properties @@ -1,2 +1,2 @@ -version=20.1.4 -latestReleasedVersion=20.1.3 +version=20.2.1 +latestReleasedVersion=20.2.0 diff --git a/firebase-inappmessaging-display/src/test/java/com/google/firebase/inappmessaging/display/FirebaseInAppMessagingDisplayTest.java b/firebase-inappmessaging-display/src/test/java/com/google/firebase/inappmessaging/display/FirebaseInAppMessagingDisplayTest.java index 692bbc43db8..8537144a18c 100644 --- a/firebase-inappmessaging-display/src/test/java/com/google/firebase/inappmessaging/display/FirebaseInAppMessagingDisplayTest.java +++ b/firebase-inappmessaging-display/src/test/java/com/google/firebase/inappmessaging/display/FirebaseInAppMessagingDisplayTest.java @@ -87,11 +87,13 @@ import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; +import org.robolectric.annotation.LooperMode; import org.robolectric.shadows.ShadowActivity; import org.robolectric.shadows.ShadowPackageManager; @RunWith(RobolectricTestRunner.class) @Config(sdk = 21, qualifiers = "port") +@LooperMode(LooperMode.Mode.LEGACY) public class FirebaseInAppMessagingDisplayTest { private com.google.firebase.inappmessaging.display.FirebaseInAppMessagingDisplay fiamUI; diff --git a/firebase-inappmessaging/gradle.properties b/firebase-inappmessaging/gradle.properties index a23fa35b103..3b49e0fc3a8 100644 --- a/firebase-inappmessaging/gradle.properties +++ b/firebase-inappmessaging/gradle.properties @@ -1,2 +1,2 @@ -version=20.1.4 -latestReleasedVersion=20.1.3 +version=20.2.1 +latestReleasedVersion=20.2.0 diff --git a/firebase-inappmessaging/src/test/java/com/google/firebase/inappmessaging/internal/InAppMessageStreamManagerTest.java b/firebase-inappmessaging/src/test/java/com/google/firebase/inappmessaging/internal/InAppMessageStreamManagerTest.java index 47235113be7..8675e959981 100644 --- a/firebase-inappmessaging/src/test/java/com/google/firebase/inappmessaging/internal/InAppMessageStreamManagerTest.java +++ b/firebase-inappmessaging/src/test/java/com/google/firebase/inappmessaging/internal/InAppMessageStreamManagerTest.java @@ -71,10 +71,12 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; +import org.robolectric.annotation.LooperMode; // TODO: Refactor and clean this logic @RunWith(RobolectricTestRunner.class) @Config(manifest = Config.NONE) +@LooperMode(LooperMode.Mode.LEGACY) public class InAppMessageStreamManagerTest { private static final long PAST = 1000000; private static final long NOW = PAST + 100000; diff --git a/firebase-inappmessaging/src/test/java/com/google/firebase/inappmessaging/internal/MetricsLoggerClientTest.java b/firebase-inappmessaging/src/test/java/com/google/firebase/inappmessaging/internal/MetricsLoggerClientTest.java index ca074a008e7..921018e98ea 100644 --- a/firebase-inappmessaging/src/test/java/com/google/firebase/inappmessaging/internal/MetricsLoggerClientTest.java +++ b/firebase-inappmessaging/src/test/java/com/google/firebase/inappmessaging/internal/MetricsLoggerClientTest.java @@ -55,9 +55,11 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; +import org.robolectric.annotation.LooperMode; @RunWith(RobolectricTestRunner.class) @Config(manifest = Config.NONE) +@LooperMode(LooperMode.Mode.LEGACY) public class MetricsLoggerClientTest { private static final long PAST = 1000000; private static final long NOW = PAST + 100000; diff --git a/firebase-installations-interop/gradle.properties b/firebase-installations-interop/gradle.properties index b58bf86ff76..5328ce212de 100644 --- a/firebase-installations-interop/gradle.properties +++ b/firebase-installations-interop/gradle.properties @@ -1,2 +1,2 @@ -version=17.0.3 -latestReleasedVersion=17.0.2 +version=17.1.1 +latestReleasedVersion=17.1.0 diff --git a/firebase-installations/gradle.properties b/firebase-installations/gradle.properties index 22bb5703b26..5328ce212de 100644 --- a/firebase-installations/gradle.properties +++ b/firebase-installations/gradle.properties @@ -1,2 +1,2 @@ -version=17.0.4 -latestReleasedVersion=17.0.3 +version=17.1.1 +latestReleasedVersion=17.1.0 diff --git a/firebase-installations/src/test/java/com/google/firebase/installations/FirebaseInstallationsTest.java b/firebase-installations/src/test/java/com/google/firebase/installations/FirebaseInstallationsTest.java index d50f5628796..56fdd7af9bd 100644 --- a/firebase-installations/src/test/java/com/google/firebase/installations/FirebaseInstallationsTest.java +++ b/firebase-installations/src/test/java/com/google/firebase/installations/FirebaseInstallationsTest.java @@ -14,12 +14,11 @@ package com.google.firebase.installations; +import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; -import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; @@ -207,7 +206,7 @@ public void testGetId_noNetwork_noIid() throws Exception { String fid = onCompleteListener.await(); assertWithMessage("getId Task failed.").that(fid).isEqualTo(TEST_FID_1); PersistedInstallationEntry entry = persistedInstallation.readPersistedInstallationEntryValue(); - assertThat(entry.getFirebaseInstallationId(), equalTo(TEST_FID_1)); + assertThat(entry.getFirebaseInstallationId()).isEqualTo(TEST_FID_1); // Waiting for Task that registers FID on the FIS Servers executor.awaitTermination(500, TimeUnit.MILLISECONDS); @@ -215,7 +214,7 @@ public void testGetId_noNetwork_noIid() throws Exception { // The storage should still have the same ID and the status should indicate that the // fid is registered. entry = persistedInstallation.readPersistedInstallationEntryValue(); - assertThat(entry.getFirebaseInstallationId(), equalTo(TEST_FID_1)); + assertThat(entry.getFirebaseInstallationId()).isEqualTo(TEST_FID_1); assertTrue("the entry isn't unregistered: " + entry, entry.isUnregistered()); } @@ -242,7 +241,7 @@ public void testGetId_noNetwork_iidPresent() throws Exception { String fid = onCompleteListener.await(); assertWithMessage("getId Task failed.").that(fid).isEqualTo(TEST_INSTANCE_ID_1); PersistedInstallationEntry entry = persistedInstallation.readPersistedInstallationEntryValue(); - assertThat(entry.getFirebaseInstallationId(), equalTo(TEST_INSTANCE_ID_1)); + assertThat(entry.getFirebaseInstallationId()).isEqualTo(TEST_INSTANCE_ID_1); // Waiting for Task that registers FID on the FIS Servers executor.awaitTermination(500, TimeUnit.MILLISECONDS); @@ -250,7 +249,7 @@ public void testGetId_noNetwork_iidPresent() throws Exception { // The storage should still have the same ID and the status should indicate that the // fid is registered. entry = persistedInstallation.readPersistedInstallationEntryValue(); - assertThat(entry.getFirebaseInstallationId(), equalTo(TEST_INSTANCE_ID_1)); + assertThat(entry.getFirebaseInstallationId()).isEqualTo(TEST_INSTANCE_ID_1); assertTrue("the entry doesn't have an uregistered fid: " + entry, entry.isUnregistered()); } @@ -279,7 +278,7 @@ public void testGetId_noNetwork_fidAlreadyGenerated() throws Exception { // The storage should still have the same ID and the status should indicate that the // fid is registered. PersistedInstallationEntry entry = persistedInstallation.readPersistedInstallationEntryValue(); - assertThat(entry.getFirebaseInstallationId(), equalTo("generatedFid")); + assertThat(entry.getFirebaseInstallationId()).isEqualTo("generatedFid"); assertTrue("the entry doesn't have an uregistered fid: " + entry, entry.isUnregistered()); } @@ -313,7 +312,7 @@ public void testGetId_ValidIdAndToken_NoBackendCalls() throws Exception { // check that the fid is still the expected one and is registered PersistedInstallationEntry entry = persistedInstallation.readPersistedInstallationEntryValue(); - assertThat(entry.getFirebaseInstallationId(), equalTo(TEST_FID_1)); + assertThat(entry.getFirebaseInstallationId()).isEqualTo(TEST_FID_1); assertTrue("the entry doesn't have a registered fid: " + entry, entry.isRegistered()); } @@ -350,7 +349,7 @@ public void testGetId_UnRegisteredId_IssueCreateIdCall() throws Exception { // check that the fid is still the expected one and is registered PersistedInstallationEntry entry = persistedInstallation.readPersistedInstallationEntryValue(); - assertThat(entry.getFirebaseInstallationId(), equalTo(TEST_FID_1)); + assertThat(entry.getFirebaseInstallationId()).isEqualTo(TEST_FID_1); assertTrue("the entry doesn't have a registered fid: " + entry, entry.isRegistered()); } @@ -368,7 +367,7 @@ public void testReadToken_wildcard() { .commit(); IidStore iidStore = new IidStore(prefs, "123"); - assertThat(iidStore.readToken(), equalTo("tokenWILDCARD")); + assertThat(iidStore.readToken()).isEqualTo("tokenWILDCARD"); } @Test @@ -385,7 +384,7 @@ public void testReadToken_fcm() { .commit(); IidStore iidStore = new IidStore(prefs, "123"); - assertThat(iidStore.readToken(), equalTo("tokenFCM")); + assertThat(iidStore.readToken()).isEqualTo("tokenFCM"); } @Test @@ -402,7 +401,7 @@ public void testReadToken_gcm() { .commit(); IidStore iidStore = new IidStore(prefs, "123"); - assertThat(iidStore.readToken(), equalTo("tokenGCM")); + assertThat(iidStore.readToken()).isEqualTo("tokenGCM"); } @Test @@ -419,7 +418,7 @@ public void testReadToken_empty() { .commit(); IidStore iidStore = new IidStore(prefs, "123"); - assertThat(iidStore.readToken(), equalTo("tokenEMPTY")); + assertThat(iidStore.readToken()).isEqualTo("tokenEMPTY"); } @Test @@ -453,7 +452,7 @@ public void testReadToken_withJsonformatting() { .commit(); IidStore iidStore = new IidStore(prefs, "123"); - assertThat(iidStore.readToken(), equalTo("thetoken")); + assertThat(iidStore.readToken()).isEqualTo("thetoken"); } @Test @@ -490,10 +489,10 @@ public void testFidListener_fidChanged_successful() throws Exception { // Waiting for Task that registers FID on the FIS Servers executor.awaitTermination(500, TimeUnit.MILLISECONDS); PersistedInstallationEntry entry = persistedInstallation.readPersistedInstallationEntryValue(); - assertThat(entry.getFirebaseInstallationId(), equalTo(TEST_FID_2)); + assertThat(entry.getFirebaseInstallationId()).isEqualTo(TEST_FID_2); // Verify FidListener receives fid changes. - assertThat(fidListener.getLatestFid(), equalTo(TEST_FID_2)); + assertThat(fidListener.getLatestFid()).isEqualTo(TEST_FID_2); assertNull(fidListener2.getLatestFid()); } @@ -513,7 +512,7 @@ public void testGetId_migrateIid_successful() throws Exception { String fid = onCompleteListener.await(); assertWithMessage("getId Task failed.").that(fid).isEqualTo(TEST_INSTANCE_ID_1); PersistedInstallationEntry entry = persistedInstallation.readPersistedInstallationEntryValue(); - assertThat(entry.getFirebaseInstallationId(), equalTo(TEST_INSTANCE_ID_1)); + assertThat(entry.getFirebaseInstallationId()).isEqualTo(TEST_INSTANCE_ID_1); // Waiting for Task that registers FID on the FIS Servers executor.awaitTermination(500, TimeUnit.MILLISECONDS); @@ -521,7 +520,7 @@ public void testGetId_migrateIid_successful() throws Exception { // The storage should still have the same ID and the status should indicate that the // fid si registered. entry = persistedInstallation.readPersistedInstallationEntryValue(); - assertThat(entry.getFirebaseInstallationId(), equalTo(TEST_INSTANCE_ID_1)); + assertThat(entry.getFirebaseInstallationId()).isEqualTo(TEST_INSTANCE_ID_1); assertTrue("the entry doesn't have a registered fid: " + entry, entry.isRegistered()); } @@ -555,7 +554,7 @@ public void testGetId_multipleCalls_sameFIDReturned() throws Exception { verify(mockBackend, times(1)) .createFirebaseInstallation(TEST_API_KEY, TEST_FID_1, TEST_PROJECT_ID, TEST_APP_ID_1, null); PersistedInstallationEntry entry = persistedInstallation.readPersistedInstallationEntryValue(); - assertThat(entry.getFirebaseInstallationId(), equalTo(TEST_FID_1)); + assertThat(entry.getFirebaseInstallationId()).isEqualTo(TEST_FID_1); assertTrue("the entry isn't doesn't have a registered fid: " + entry, entry.isRegistered()); } @@ -592,7 +591,7 @@ public void testGetId_expiredAuthTokenThrowsException_statusUpdated() throws Exc // Validate that registration status is still REGISTER PersistedInstallationEntry entry = persistedInstallation.readPersistedInstallationEntryValue(); - assertThat(entry.getFirebaseInstallationId(), equalTo(TEST_FID_1)); + assertThat(entry.getFirebaseInstallationId()).isEqualTo(TEST_FID_1); assertTrue("the entry doesn't have a registered fid: " + entry, entry.isRegistered()); } @@ -713,7 +712,7 @@ public void testGetId_ServerError_UnregisteredFID() throws Exception { // There is nothing more we can do. PersistedInstallationEntry updatedInstallationEntry = persistedInstallation.readPersistedInstallationEntryValue(); - assertThat(updatedInstallationEntry.getFirebaseInstallationId(), equalTo(TEST_FID_1)); + assertThat(updatedInstallationEntry.getFirebaseInstallationId()).isEqualTo(TEST_FID_1); assertTrue( "the entry doesn't have an error fid: " + updatedInstallationEntry, updatedInstallationEntry.isErrored()); @@ -744,7 +743,7 @@ public void testGetId_fidRegistrationFailed_statusNotUpdated() throws Exception // We expect that the IOException will cause the request to fail, but it will not // cause the FID to be put into the error state because we expect this to eventually succeed. PersistedInstallationEntry entry = persistedInstallation.readPersistedInstallationEntryValue(); - assertThat(entry.getFirebaseInstallationId(), equalTo(TEST_FID_1)); + assertThat(entry.getFirebaseInstallationId()).isEqualTo(TEST_FID_1); assertTrue("the entry doesn't have an unregistered fid: " + entry, entry.isUnregistered()); } @@ -761,7 +760,7 @@ public void testGetAuthToken_fidDoesNotExist_successful() throws Exception { onCompleteListener.await(); PersistedInstallationEntry entry = persistedInstallation.readPersistedInstallationEntryValue(); - assertThat(entry.getAuthToken(), equalTo(TEST_AUTH_TOKEN)); + assertThat(entry.getAuthToken()).isEqualTo(TEST_AUTH_TOKEN); } @Test @@ -1019,7 +1018,7 @@ public void testGetAuthToken_multipleCallsForceRefresh_fetchedNewTokenTwice() th verify(mockBackend, times(1)) .generateAuthToken(TEST_API_KEY, TEST_FID_1, TEST_PROJECT_ID, TEST_REFRESH_TOKEN); PersistedInstallationEntry entry = persistedInstallation.readPersistedInstallationEntryValue(); - assertThat(entry.getAuthToken(), equalTo(TEST_AUTH_TOKEN_3)); + assertThat(entry.getAuthToken()).isEqualTo(TEST_AUTH_TOKEN_3); } @Test diff --git a/firebase-messaging-directboot/gradle.properties b/firebase-messaging-directboot/gradle.properties index f46c5c34c0c..77db708897c 100644 --- a/firebase-messaging-directboot/gradle.properties +++ b/firebase-messaging-directboot/gradle.properties @@ -1,3 +1,3 @@ -version=23.0.9 -latestReleasedVersion=23.0.8 +version=23.1.1 +latestReleasedVersion=23.1.0 android.enableUnitTestBinaryResources=true \ No newline at end of file diff --git a/firebase-messaging/firebase-messaging.gradle b/firebase-messaging/firebase-messaging.gradle index 8058419fc09..a220aae3c1f 100644 --- a/firebase-messaging/firebase-messaging.gradle +++ b/firebase-messaging/firebase-messaging.gradle @@ -113,7 +113,7 @@ dependencies { testImplementation 'com.google.android.gms:play-services-cloud-messaging:17.0.1' testImplementation 'androidx.test:rules:1.2.0' testImplementation 'androidx.test:runner:1.2.0' - testImplementation "org.robolectric:robolectric:4.6.1" + testImplementation "org.robolectric:robolectric:$robolectricVersion" testImplementation 'junit:junit:4.13-beta-2' testImplementation 'org.mockito:mockito-core:2.25.0' testImplementation "com.google.truth:truth:$googleTruthVersion" diff --git a/firebase-messaging/gradle.properties b/firebase-messaging/gradle.properties index 5a4462bd8d0..c813b4b050e 100644 --- a/firebase-messaging/gradle.properties +++ b/firebase-messaging/gradle.properties @@ -1,3 +1,3 @@ -version=23.0.9 -latestReleasedVersion=23.0.8 +version=23.1.1 +latestReleasedVersion=23.1.0 android.enableUnitTestBinaryResources=true diff --git a/firebase-ml-modeldownloader/gradle.properties b/firebase-ml-modeldownloader/gradle.properties index ef7100452f9..3e325db02c3 100644 --- a/firebase-ml-modeldownloader/gradle.properties +++ b/firebase-ml-modeldownloader/gradle.properties @@ -1 +1 @@ -version=24.0.6 +version=24.1.1 diff --git a/firebase-ml-modeldownloader/src/main/java/com/google/firebase/ml/modeldownloader/internal/CustomModelDownloadService.java b/firebase-ml-modeldownloader/src/main/java/com/google/firebase/ml/modeldownloader/internal/CustomModelDownloadService.java index 45f4e212117..1775ba00d79 100644 --- a/firebase-ml-modeldownloader/src/main/java/com/google/firebase/ml/modeldownloader/internal/CustomModelDownloadService.java +++ b/firebase-ml-modeldownloader/src/main/java/com/google/firebase/ml/modeldownloader/internal/CustomModelDownloadService.java @@ -16,6 +16,7 @@ import android.content.Context; import android.content.pm.PackageManager; +import android.text.TextUtils; import android.util.JsonReader; import android.util.Log; import androidx.annotation.NonNull; @@ -148,6 +149,12 @@ public Task getNewDownloadUrlWithExpiry(String projectNumber, Strin public Task getCustomModelDetails( String projectNumber, String modelName, String modelHash) { try { + + if (TextUtils.isEmpty(modelName)) + throw new FirebaseMlException( + "Error cannot retrieve model from reading an empty modelName", + FirebaseMlException.INVALID_ARGUMENT); + URL url = new URL(String.format(DOWNLOAD_MODEL_REGEX, downloadHost, projectNumber, modelName)); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); @@ -206,6 +213,8 @@ public Task getCustomModelDetails( new FirebaseMlException( "Error reading custom model from download service: " + e.getMessage(), FirebaseMlException.INVALID_ARGUMENT)); + } catch (FirebaseMlException e) { + return Tasks.forException(e); } } diff --git a/firebase-ml-modeldownloader/src/test/java/com/google/firebase/ml/modeldownloader/internal/ModelFileDownloadServiceTest.java b/firebase-ml-modeldownloader/src/test/java/com/google/firebase/ml/modeldownloader/internal/ModelFileDownloadServiceTest.java index f431ebbeb70..90e6eed211c 100644 --- a/firebase-ml-modeldownloader/src/test/java/com/google/firebase/ml/modeldownloader/internal/ModelFileDownloadServiceTest.java +++ b/firebase-ml-modeldownloader/src/test/java/com/google/firebase/ml/modeldownloader/internal/ModelFileDownloadServiceTest.java @@ -61,8 +61,10 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.LooperMode; @RunWith(RobolectricTestRunner.class) +@LooperMode(LooperMode.Mode.LEGACY) public class ModelFileDownloadServiceTest { private static final String TEST_PROJECT_ID = "777777777777"; diff --git a/firebase-perf/CHANGELOG.md b/firebase-perf/CHANGELOG.md index 97f9f0b2b17..f1da211f000 100644 --- a/firebase-perf/CHANGELOG.md +++ b/firebase-perf/CHANGELOG.md @@ -1,5 +1,15 @@ # Unreleased +* + +## Kotlin +The Kotlin extensions library transitively includes the updated +`firebase-performance` library. The Kotlin extensions library has the following +additional updates: + +* [feature] Added a [`trace(String, Trace.() -> T)`](https://firebase.google.com/docs/reference/kotlin/com/google/firebase/perf/ktx/package-summary#trace(kotlin.String,kotlin.Function1)) + extension function to create a custom trace with the given name. + # 20.2.0 * [unchanged] Updated to accommodate the release of the updated [perfmon] Kotlin extensions library. diff --git a/firebase-perf/firebase-perf.gradle b/firebase-perf/firebase-perf.gradle index fb5938575ea..62107170b8e 100644 --- a/firebase-perf/firebase-perf.gradle +++ b/firebase-perf/firebase-perf.gradle @@ -119,9 +119,7 @@ dependencies { testImplementation 'junit:junit:4.12' testImplementation 'org.mockito:mockito-core:2.28.2' testImplementation 'org.mockito:mockito-inline:2.13.0' - testImplementation ("org.robolectric:robolectric:$robolectricVersion") { - exclude group: 'com.google.protobuf', module: 'protobuf-java' - } + testImplementation "org.robolectric:robolectric:$robolectricVersion" testImplementation "com.google.truth:truth:$googleTruthVersion" testImplementation group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.6' testImplementation 'androidx.test:core:1.2.0' diff --git a/firebase-perf/gradle.properties b/firebase-perf/gradle.properties index 3d1d3baae96..b6570c9ac6b 100644 --- a/firebase-perf/gradle.properties +++ b/firebase-perf/gradle.properties @@ -15,7 +15,7 @@ # # -version=20.1.2 -latestReleasedVersion=20.1.1 +version=20.2.1 +latestReleasedVersion=20.2.0 android.enableUnitTestBinaryResources=true diff --git a/firebase-perf/ktx/api.txt b/firebase-perf/ktx/api.txt index 0dd6cae8883..2498d874952 100644 --- a/firebase-perf/ktx/api.txt +++ b/firebase-perf/ktx/api.txt @@ -5,6 +5,7 @@ package com.google.firebase.perf.ktx { method @NonNull public static com.google.firebase.perf.FirebasePerformance getPerformance(@NonNull com.google.firebase.ktx.Firebase); method public static inline void trace(@NonNull com.google.firebase.perf.metrics.HttpMetric, @NonNull kotlin.jvm.functions.Function1 block); method public static inline T trace(@NonNull com.google.firebase.perf.metrics.Trace, @NonNull kotlin.jvm.functions.Function1 block); + method public static inline T trace(@NonNull String name, @NonNull kotlin.jvm.functions.Function1 block); } } diff --git a/firebase-perf/ktx/ktx.gradle b/firebase-perf/ktx/ktx.gradle index 98cf5e5a454..74bbabc2308 100644 --- a/firebase-perf/ktx/ktx.gradle +++ b/firebase-perf/ktx/ktx.gradle @@ -50,6 +50,7 @@ dependencies { implementation project(':firebase-perf') implementation 'androidx.annotation:annotation:1.1.0' + testCompileOnly "com.google.protobuf:protobuf-java:$protocVersion" testImplementation "org.robolectric:robolectric:$robolectricVersion" testImplementation 'junit:junit:4.12' testImplementation "com.google.truth:truth:$googleTruthVersion" diff --git a/firebase-perf/ktx/src/main/kotlin/com/google/firebase/perf/ktx/Performance.kt b/firebase-perf/ktx/src/main/kotlin/com/google/firebase/perf/ktx/Performance.kt index 365a6f2afb2..244c2d67226 100644 --- a/firebase-perf/ktx/src/main/kotlin/com/google/firebase/perf/ktx/Performance.kt +++ b/firebase-perf/ktx/src/main/kotlin/com/google/firebase/perf/ktx/Performance.kt @@ -48,6 +48,12 @@ inline fun Trace.trace(block: Trace.() -> T): T { } } +/** + * Creates a [Trace] object with given [name] and measures the time it takes to + * run the [block] wrapped by calls to [start] and [stop]. + */ +inline fun trace(name: String, block: Trace.() -> T): T = Trace.create(name).trace(block) + internal const val LIBRARY_NAME: String = "fire-perf-ktx" /** @suppress */ diff --git a/firebase-storage/gradle.properties b/firebase-storage/gradle.properties index 2fcd97394ef..2485d5054e8 100644 --- a/firebase-storage/gradle.properties +++ b/firebase-storage/gradle.properties @@ -12,6 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -version=20.0.3 -latestReleasedVersion=20.0.2 +version=20.1.1 +latestReleasedVersion=20.1.0 android.enableUnitTestBinaryResources=true diff --git a/smoke-tests/build.gradle b/smoke-tests/build.gradle index 0d77cdcb43e..88551123c28 100644 --- a/smoke-tests/build.gradle +++ b/smoke-tests/build.gradle @@ -69,7 +69,6 @@ dependencies { implementation "com.google.firebase:firebase-annotations" implementation "com.google.firebase:firebase-appdistribution:16.0.0-beta03" implementation "com.google.firebase:firebase-appdistribution-api:16.0.0-beta03" - implementation "com.google.firebase:firebase-appindexing" implementation "com.google.firebase:firebase-auth" implementation "com.google.firebase:firebase-common" implementation "com.google.firebase:firebase-config" @@ -84,6 +83,10 @@ dependencies { implementation "com.google.firebase:firebase-perf" implementation "com.google.firebase:firebase-storage" + // TODO(yifany): remove after the issue is fixed + // https://github.com/firebase/firebase-android-sdk/issues/4206 + implementation "com.google.firebase:firebase-iid:21.1.0" + // Common utilities (application side) implementation "androidx.test:rules:1.4.0" implementation "androidx.test:runner:1.4.0" diff --git a/smoke-tests/src/main/java/com/google/firebase/testing/BuildOnlyTest.java b/smoke-tests/src/main/java/com/google/firebase/testing/BuildOnlyTest.java index 3e7b77f1b70..af44f6607ca 100644 --- a/smoke-tests/src/main/java/com/google/firebase/testing/BuildOnlyTest.java +++ b/smoke-tests/src/main/java/com/google/firebase/testing/BuildOnlyTest.java @@ -16,7 +16,6 @@ import static com.google.common.truth.Truth.assertThat; -import com.google.firebase.appindexing.FirebaseAppIndex; import com.google.firebase.inappmessaging.FirebaseInAppMessaging; import com.google.firebase.messaging.FirebaseMessaging; import com.google.firebase.ml.modeldownloader.FirebaseModelDownloader; @@ -32,11 +31,6 @@ @RunWith(JUnit4.class) public final class BuildOnlyTest { - @Test - public void appindexing_IsNotNull() { - assertThat(FirebaseAppIndex.getInstance(getApplicationContext())).isNotNull(); - } - @Test public void inappmessaging_IsNotNull() { assertThat(FirebaseInAppMessaging.getInstance()).isNotNull(); diff --git a/transport/transport-backend-cct/gradle.properties b/transport/transport-backend-cct/gradle.properties index 0c32b609ca6..74fa8f92932 100644 --- a/transport/transport-backend-cct/gradle.properties +++ b/transport/transport-backend-cct/gradle.properties @@ -12,5 +12,5 @@ # See the License for the specific language governing permissions and # limitations under the License. -version=3.1.8 -latestReleasedVersion=3.1.7 +version=3.1.9 +latestReleasedVersion=3.1.8 diff --git a/transport/transport-backend-cct/transport-backend-cct.gradle b/transport/transport-backend-cct/transport-backend-cct.gradle index c5973dde1a5..c86de2da62c 100644 --- a/transport/transport-backend-cct/transport-backend-cct.gradle +++ b/transport/transport-backend-cct/transport-backend-cct.gradle @@ -65,14 +65,14 @@ dependencies { testImplementation 'com.google.protobuf:protobuf-java-util:3.11.0' testImplementation 'androidx.test:core:1.2.0' - testImplementation 'junit:junit:4.12' testImplementation "com.google.truth:truth:$googleTruthVersion" testImplementation 'com.google.truth.extensions:truth-proto-extension:1.0' testImplementation 'com.github.tomakehurst:wiremock:2.26.3' //Android compatible version of Apache httpclient. testImplementation 'org.apache.httpcomponents:httpclient-android:4.3.5.1' - testImplementation "org.robolectric:robolectric:$robolectricVersion" - testImplementation 'junit:junit:4.13-beta-2' + // Keep Robolectric to 4.3.1 for httpclient and TelephonyManager compatibility. + testImplementation "org.robolectric:robolectric:4.3.1" + testImplementation 'junit:junit:4.13.1' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' diff --git a/transport/transport-runtime/gradle.properties b/transport/transport-runtime/gradle.properties index dda29bf0493..173c7edf124 100644 --- a/transport/transport-runtime/gradle.properties +++ b/transport/transport-runtime/gradle.properties @@ -12,6 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -version=3.1.8 -latestReleasedVersion=3.1.7 +version=3.1.9 +latestReleasedVersion=3.1.8 android.enableUnitTestBinaryResources=true