From 72fc2b89ee98fb6e2317b3136fa2dcc9500ee492 Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Sat, 16 Sep 2023 23:50:21 +0300
Subject: [PATCH 01/11] java records
---
build.gradle | 4 +-
buildSrc/build.gradle.kts | 8 +-
.../firebase/gradle/plugins/Metalava.kt | 2 +
.../bandwagoner/bandwagoner.gradle | 1 +
.../firebase-dynamic-links.gradle | 3 +
firebase-firestore-sdk34/CHANGELOG.md | 0
firebase-firestore-sdk34/README.md | 118 ++
.../firebase-firestore-sdk34.gradle | 184 +++
firebase-firestore-sdk34/gradle.properties | 2 +
firebase-firestore-sdk34/lint.xml | 27 +
firebase-firestore-sdk34/proguard.txt | 18 +
.../firebase/firestore/sdk34/DocumentId.java | 46 +
.../firestore/sdk34/PropertyName.java | 30 +
.../firestore/sdk34/ServerTimestamp.java | 31 +
.../google/firebase/firestore/TestUtil.java | 126 ++
.../src/test/AndroidManifest.xml | 14 +
.../firestore/sdk34/LocalFirestoreHelper.java | 406 ++++++
.../sdk34/RecordDocumentReferenceTest.java | 332 +++++
.../sdk34/util/RecordMapperTest.java | 1104 +++++++++++++++++
firebase-firestore/gradle.properties | 2 +-
firebase-firestore/ktx/ktx.gradle | 4 +
.../firebase/firestore/util/BeanMapper.java | 132 ++
.../firestore/util/CustomClassMapper.java | 199 +--
.../firestore/util/DeserializeContext.java | 76 ++
.../firebase/firestore/util/RecordMapper.java | 307 +++++
firebase-perf/dev-app/dev-app.gradle | 1 +
firebase-perf/e2e-app/e2e-app.gradle | 1 +
gradle.properties | 8 +-
gradle/libs.versions.toml | 12 +-
gradle/wrapper/gradle-wrapper.properties | 2 +-
subprojects.cfg | 39 +-
31 files changed, 3061 insertions(+), 178 deletions(-)
create mode 100755 firebase-firestore-sdk34/CHANGELOG.md
create mode 100755 firebase-firestore-sdk34/README.md
create mode 100755 firebase-firestore-sdk34/firebase-firestore-sdk34.gradle
create mode 100755 firebase-firestore-sdk34/gradle.properties
create mode 100755 firebase-firestore-sdk34/lint.xml
create mode 100755 firebase-firestore-sdk34/proguard.txt
create mode 100755 firebase-firestore-sdk34/src/main/java/com/google/firebase/firestore/sdk34/DocumentId.java
create mode 100755 firebase-firestore-sdk34/src/main/java/com/google/firebase/firestore/sdk34/PropertyName.java
create mode 100755 firebase-firestore-sdk34/src/main/java/com/google/firebase/firestore/sdk34/ServerTimestamp.java
create mode 100755 firebase-firestore-sdk34/src/roboUtil/java/com/google/firebase/firestore/TestUtil.java
create mode 100755 firebase-firestore-sdk34/src/test/AndroidManifest.xml
create mode 100755 firebase-firestore-sdk34/src/test/java/com/google/firebase/firestore/sdk34/LocalFirestoreHelper.java
create mode 100755 firebase-firestore-sdk34/src/test/java/com/google/firebase/firestore/sdk34/RecordDocumentReferenceTest.java
create mode 100755 firebase-firestore-sdk34/src/test/java/com/google/firebase/firestore/sdk34/util/RecordMapperTest.java
create mode 100755 firebase-firestore/src/main/java/com/google/firebase/firestore/util/BeanMapper.java
create mode 100755 firebase-firestore/src/main/java/com/google/firebase/firestore/util/DeserializeContext.java
create mode 100755 firebase-firestore/src/main/java/com/google/firebase/firestore/util/RecordMapper.java
diff --git a/build.gradle b/build.gradle
index 5b4a27741e9..c0a4afb5fe4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -33,7 +33,7 @@ buildscript {
}
dependencies {
- classpath 'com.google.protobuf:protobuf-gradle-plugin:0.9.2'
+ classpath libs.protobuf.gradle.plugin
classpath 'net.ltgt.gradle:gradle-errorprone-plugin:1.3.0'
classpath 'gradle.plugin.com.github.sherter.google-java-format:google-java-format-gradle-plugin:0.9'
classpath 'com.google.gms:google-services:4.3.15'
@@ -71,9 +71,11 @@ firebaseContinuousIntegration {
]
}
+/*
if(JavaVersion.current() != JavaVersion.VERSION_11){
throw new GradleException("This build must be run with java 11. You're using ${JavaVersion.current()}.")
}
+*/
configure(subprojects) {
repositories {
diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts
index 0695e90f00d..86925948c81 100644
--- a/buildSrc/build.gradle.kts
+++ b/buildSrc/build.gradle.kts
@@ -15,7 +15,7 @@
plugins {
id("com.ncorti.ktfmt.gradle") version "0.11.0"
id("com.github.sherter.google-java-format") version "0.9"
- kotlin("plugin.serialization") version "1.7.10"
+ kotlin("plugin.serialization") version "1.8.10"
`kotlin-dsl`
}
@@ -48,7 +48,7 @@ dependencies {
implementation("com.google.firebase:perf-plugin:$perfPluginVersion")
implementation("com.google.auto.value:auto-value-annotations:1.8.1")
annotationProcessor("com.google.auto.value:auto-value:1.6.5")
- implementation(kotlin("gradle-plugin", "1.7.10"))
+ implementation(kotlin("gradle-plugin", "1.8.10"))
implementation("org.json:json:20210307")
implementation("org.eclipse.aether:aether-api:1.0.0.v20140518")
@@ -65,8 +65,8 @@ dependencies {
implementation(libs.kotlinx.serialization.json)
implementation("com.google.code.gson:gson:2.8.9")
- implementation("com.android.tools.build:gradle:7.4.2")
- implementation("com.android.tools.build:builder-test-api:7.4.2")
+ implementation(libs.gradle)
+ implementation(libs.builder.test.api)
implementation("gradle.plugin.com.github.sherter.google-java-format:google-java-format-gradle-plugin:0.9")
testImplementation(libs.bundles.kotest)
diff --git a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/Metalava.kt b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/Metalava.kt
index 077bf3de53f..5c9b28addb9 100644
--- a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/Metalava.kt
+++ b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/Metalava.kt
@@ -59,6 +59,7 @@ fun Project.runMetalavaWithArgs(
"HiddenAbstractMethod"
) + arguments
+/*
project.javaexec {
main = "com.android.tools.metalava.Driver"
classpath = project.metalavaConfig
@@ -66,6 +67,7 @@ fun Project.runMetalavaWithArgs(
isIgnoreExitValue = ignoreFailure
if (stdOut != null) errorOutput = stdOut
}
+*/
}
abstract class GenerateStubsTask : DefaultTask() {
diff --git a/firebase-config/bandwagoner/bandwagoner.gradle b/firebase-config/bandwagoner/bandwagoner.gradle
index b45dd3bbf0b..af0ce005871 100644
--- a/firebase-config/bandwagoner/bandwagoner.gradle
+++ b/firebase-config/bandwagoner/bandwagoner.gradle
@@ -22,6 +22,7 @@ apply plugin: 'org.jetbrains.kotlin.android'
// apply plugin: 'com.google.gms.google-services'
android {
+ namespace "com.googletest.firebase.remoteconfig.bandwagoner"
compileSdkVersion project.targetSdkVersion
lintOptions {
abortOnError false
diff --git a/firebase-dynamic-links/firebase-dynamic-links.gradle b/firebase-dynamic-links/firebase-dynamic-links.gradle
index 7c39b661989..0d8043ab991 100644
--- a/firebase-dynamic-links/firebase-dynamic-links.gradle
+++ b/firebase-dynamic-links/firebase-dynamic-links.gradle
@@ -47,6 +47,9 @@ android {
}
testOptions.unitTests.includeAndroidResources = true
+ buildFeatures {
+ aidl true
+ }
}
dependencies {
diff --git a/firebase-firestore-sdk34/CHANGELOG.md b/firebase-firestore-sdk34/CHANGELOG.md
new file mode 100755
index 00000000000..e69de29bb2d
diff --git a/firebase-firestore-sdk34/README.md b/firebase-firestore-sdk34/README.md
new file mode 100755
index 00000000000..b05694bd4a7
--- /dev/null
+++ b/firebase-firestore-sdk34/README.md
@@ -0,0 +1,118 @@
+# firebase-firestore
+
+This is the Cloud Firestore component of the Firebase Android SDK.
+
+Cloud Firestore is a flexible, scalable database for mobile, web, and server
+development from Firebase and Google Cloud Platform. Like Firebase Realtime
+Database, it keeps your data in sync across client apps through realtime
+listeners and offers offline support for mobile and web so you can build
+responsive apps that work regardless of network latency or Internet
+connectivity. Cloud Firestore also offers seamless integration with other
+Firebase and Google Cloud Platform products, including Cloud Functions.
+
+## Building
+
+All Gradle commands should be run from the source root (which is one level up
+from this folder). See the README.md in the source root for instructions on
+publishing/testing Cloud Firestore.
+
+To build Cloud Firestore, from the source root run:
+```bash
+./gradlew :firebase-firestore:assembleRelease
+```
+
+## Unit Testing
+
+To run unit tests for Cloud Firestore, from the source root run:
+```bash
+./gradlew :firebase-firestore:check
+```
+
+## Integration Testing
+
+Running integration tests requires a Firebase project because they would try
+to connect to the Firestore backends.
+
+See [here](../README.md#project-setup) for how to setup a project.
+
+Once you setup the project, download `google-services.json` and place it in
+the source root.
+
+Make sure you have created a Firestore instance for your project, before
+you proceed.
+
+By default, integration tests run against the Firestore emulator.
+
+### Setting up the Firestore Emulator
+
+The integration tests require that the Firestore emulator is running on port
+8080, which is default when running it via CLI.
+
+ * [Install the Firebase CLI](https://firebase.google.com/docs/cli/).
+ ```
+ npm install -g firebase-tools
+ ```
+ * [Install the Firestore
+ emulator](https://firebase.google.com/docs/firestore/security/test-rules-emulator#install_the_emulator).
+ ```
+ firebase setup:emulators:firestore
+ ```
+ * Run the emulator
+ ```
+ firebase emulators:start --only firestore
+ ```
+ * Select the `Firestore Integration Tests (Firestore Emulator)` run
+ configuration to run all integration tests.
+
+To run the integration tests against prod, select `FirestoreProdIntegrationTest`
+run configuration.
+
+### Run on Local Android Emulator
+
+Then simply run:
+```bash
+./gradlew :firebase-firestore:connectedCheck
+```
+
+### Run on Firebase Test Lab
+
+You can also test on Firebase Test Lab, which allow you to run the integration
+tests on devices hosted in Google data center.
+
+See [here](../README.md#running-integration-tests-on-firebase-test-lab) for
+instructions of how to setup Firebase Test Lab for your project.
+
+Run:
+```bash
+./gradlew :firebase-firestore:deviceCheck
+```
+
+## Code Formatting
+
+Run below to format Java code:
+```bash
+./gradlew :firebase-firestore:googleJavaFormat
+```
+
+See [here](../README.md#code-formatting) if you want to be able to format code
+from within Android Studio.
+
+## Build Local Jar of Firestore SDK
+
+```bash
+./gradlew -PprojectsToPublish="firebase-firestore" publishReleasingLibrariesToMavenLocal
+```
+
+Developers may then take a dependency on these locally published versions by adding
+the `mavenLocal()` repository to your [repositories
+block](https://docs.gradle.org/current/userguide/declaring_repositories.html) in
+your app module's build.gradle.
+
+## Misc
+After importing the project into Android Studio and building successfully
+for the first time, Android Studio will delete the run configuration xml files
+in `./idea/runConfigurations`. Undo these changes with the command:
+
+```
+$ git checkout .idea/runConfigurations
+```
diff --git a/firebase-firestore-sdk34/firebase-firestore-sdk34.gradle b/firebase-firestore-sdk34/firebase-firestore-sdk34.gradle
new file mode 100755
index 00000000000..0f3f086fc1b
--- /dev/null
+++ b/firebase-firestore-sdk34/firebase-firestore-sdk34.gradle
@@ -0,0 +1,184 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+plugins {
+ id 'firebase-library'
+ id 'com.google.protobuf'
+}
+
+firebaseLibrary {
+ libraryGroup "firestore"
+ publishSources = true
+ testLab {
+ enabled = true
+ timeout = '30m'
+ }
+}
+
+protobuf {
+ // Configure the protoc executable
+ protoc {
+ // Download from repositories
+ artifact = "com.google.protobuf:protoc:$protocVersion"
+ }
+ plugins {
+ grpc {
+ artifact = "io.grpc:protoc-gen-grpc-java:$grpcVersion"
+ }
+ }
+ generateProtoTasks {
+ all().each { task ->
+ task.builtins {
+ java { option 'lite' }
+ }
+ task.plugins {
+ grpc {
+ option 'lite'
+ }
+ }
+ }
+ }
+}
+
+android {
+ adbOptions {
+ timeOutInMs 60 * 1000
+ }
+
+ namespace "com.google.firebase.firestore.sdk34"
+ compileSdkVersion 34
+ defaultConfig {
+ targetSdkVersion 34
+ minSdkVersion 19
+ versionName version
+ multiDexEnabled true
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles 'proguard.txt'
+
+ // Acceptable values are: 'emulator', 'qa', 'nightly', and 'prod'.
+ def targetBackend = findProperty("targetBackend") ?: "emulator"
+ buildConfigField("String", "TARGET_BACKEND", "\"$targetBackend\"")
+
+ def targetDatabaseId = findProperty('targetDatabaseId') ?: "(default)"
+ buildConfigField("String", "TARGET_DATABASE_ID", "\"$targetDatabaseId\"")
+
+ def localProps = new Properties()
+
+ try {
+ file("local.properties").withInputStream { localProps.load(it) }
+ } catch (FileNotFoundException e) {
+ }
+ }
+
+ sourceSets {
+ main {
+ proto {
+ srcDir 'src/proto'
+ }
+ }
+ test {
+ java {
+ srcDir 'src/testUtil/java'
+ srcDir 'src/roboUtil/java'
+ }
+ }
+ androidTest {
+ java {
+ srcDir 'src/testUtil/java'
+ }
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_17
+ targetCompatibility JavaVersion.VERSION_17
+ }
+ testOptions.unitTests.includeAndroidResources = true
+
+}
+
+tasks.withType(Test) {
+ maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1
+}
+
+googleJavaFormat {
+ exclude 'src/testUtil/java/com/google/firebase/firestore/testutil/Assert.java'
+ exclude 'src/testUtil/java/com/google/firebase/firestore/testutil/ThrowingRunnable.java'
+}
+
+dependencies {
+/*
+ implementation 'com.google.firebase:firebase-annotations:16.2.0'
+ implementation 'com.google.firebase:firebase-common:20.3.1'
+ implementation project(':protolite-well-known-types')
+ implementation 'com.google.firebase:firebase-database-collection:18.0.1'
+ implementation 'com.google.firebase:firebase-components:17.1.0'
+ implementation 'com.google.firebase:firebase-appcheck-interop:17.0.0'
+
+ //To provide @Generated annotations
+ compileOnly 'javax.annotation:jsr250-api:1.0'
+
+ javadocClasspath 'com.google.auto.value:auto-value-annotations:1.6.6'
+
+ implementation 'androidx.annotation:annotation:1.1.0'
+ implementation "io.grpc:grpc-stub:$grpcVersion"
+ implementation "io.grpc:grpc-protobuf-lite:$grpcVersion"
+ implementation "io.grpc:grpc-okhttp:$grpcVersion"
+ implementation "io.grpc:grpc-android:$grpcVersion"
+ implementation 'com.google.android.gms:play-services-basement:18.1.0'
+ implementation 'com.google.android.gms:play-services-tasks:18.0.1'
+ implementation 'com.google.android.gms:play-services-base:18.0.1'
+
+ implementation('com.google.firebase:firebase-auth-interop:19.0.2') {
+ exclude group: "com.google.firebase", module: "firebase-common"
+ }
+
+ compileOnly 'com.google.auto.value:auto-value-annotations:1.6.6'
+ androidTestAnnotationProcessor 'com.google.auto.value:auto-value:1.6.5'
+ annotationProcessor 'com.google.auto.value:auto-value:1.6.5'
+*/
+
+ //implementation project(':firebase-firestore')
+ testImplementation project(':firebase-firestore-sdk34')
+ testImplementation 'junit:junit:4.13.2'
+ testImplementation "androidx.test:core:$androidxTestCoreVersion"
+ testImplementation "org.hamcrest:hamcrest-junit:2.0.0.0"
+ testImplementation 'org.mockito:mockito-core:2.25.0'
+ testImplementation "org.robolectric:robolectric:4.10.3"
+ 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'
+ testImplementation project(path: ':firebase-firestore')
+ testImplementation libs.google.gson
+
+ androidTestImplementation 'junit:junit:4.13.2'
+ androidTestImplementation("com.google.truth:truth:$googleTruthVersion") {
+ exclude group: "org.codehaus.mojo", module: "animal-sniffer-annotations"
+ }
+ androidTestImplementation 'org.mockito:mockito-core:2.25.0'
+ androidTestImplementation 'org.mockito:mockito-android:2.25.0'
+ androidTestImplementation 'com.fasterxml.jackson.core:jackson-databind:2.9.8'
+ androidTestImplementation "androidx.annotation:annotation:1.1.0"
+ androidTestImplementation 'androidx.test:runner:1.5.2'
+ androidTestImplementation 'androidx.test:rules:1.5.0'
+ androidTestImplementation "androidx.test.ext:junit:$androidxTestJUnitVersion"
+}
+
+gradle.projectsEvaluated {
+ tasks.withType(JavaCompile) {
+ // TODO(wuandy): Also add "-Xlint:unchecked". But currently that
+ // enables 100+ warnings due to our generated source code.
+ // TODO(wuandy): Re-enable error on warnings once errorprone issues are fixed.
+ options.compilerArgs << "-Xlint:deprecation" // << "-Werror"
+ }
+}
diff --git a/firebase-firestore-sdk34/gradle.properties b/firebase-firestore-sdk34/gradle.properties
new file mode 100755
index 00000000000..398412fefb4
--- /dev/null
+++ b/firebase-firestore-sdk34/gradle.properties
@@ -0,0 +1,2 @@
+version=24.8.0-SNAPSHOT
+latestReleasedVersion=24.7.1
diff --git a/firebase-firestore-sdk34/lint.xml b/firebase-firestore-sdk34/lint.xml
new file mode 100755
index 00000000000..5cdbff248f9
--- /dev/null
+++ b/firebase-firestore-sdk34/lint.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/firebase-firestore-sdk34/proguard.txt b/firebase-firestore-sdk34/proguard.txt
new file mode 100755
index 00000000000..b76f1281567
--- /dev/null
+++ b/firebase-firestore-sdk34/proguard.txt
@@ -0,0 +1,18 @@
+# Needed for DNS resolution. Present in OpenJDK, but not Android
+-dontwarn javax.naming.**
+
+# Don't warn about checkerframework
+#
+# Guava uses the checkerframework and the annotations
+# can safely be ignored at runtime.
+-dontwarn org.checkerframework.**
+
+# Guava warnings:
+-dontwarn java.lang.ClassValue
+-dontwarn com.google.j2objc.annotations.Weak
+-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
+-dontwarn javax.lang.model.element.Modifier
+
+# Okhttp warnings.
+-dontwarn okio.**
+-dontwarn com.google.j2objc.annotations.**
diff --git a/firebase-firestore-sdk34/src/main/java/com/google/firebase/firestore/sdk34/DocumentId.java b/firebase-firestore-sdk34/src/main/java/com/google/firebase/firestore/sdk34/DocumentId.java
new file mode 100755
index 00000000000..5985d6beec6
--- /dev/null
+++ b/firebase-firestore-sdk34/src/main/java/com/google/firebase/firestore/sdk34/DocumentId.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.firebase.firestore.sdk34;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation used to mark a record component to be automatically populated with the document's ID
+ * when the record is created from a firebase Firestore document (for example, via
+ * DocumentSnapshot#toObject).
+ *
+ *
+ * Any of the following will throw a runtime exception:
+ * - This annotation is applied to a property of a type other than String or DocumentReference.
+ *
- This annotation is applied to a property with a name that conflicts with a read document
+ * component. For example, if a record has a component `firstName` annotated by @DocumentId,
+ * and there is a property from the document named `firstName` as well, an exception is thrown
+ * when you try to read the document into the record via DocumentSnapshot#toObject or
+ * DocumentReference#get.
+ *
-
+ *
+ *
+ * When using a record to write to a document (via DocumentReference#set or WriteBatch#set), the
+ * property annotated by @DocumentId is ignored, which allows writing the record back to any
+ * document, even if it's not the origin of the record.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.RECORD_COMPONENT)
+public @interface DocumentId {}
diff --git a/firebase-firestore-sdk34/src/main/java/com/google/firebase/firestore/sdk34/PropertyName.java b/firebase-firestore-sdk34/src/main/java/com/google/firebase/firestore/sdk34/PropertyName.java
new file mode 100755
index 00000000000..553c692dbdf
--- /dev/null
+++ b/firebase-firestore-sdk34/src/main/java/com/google/firebase/firestore/sdk34/PropertyName.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2017 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.firebase.firestore.sdk34;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Marks a component to be renamed when serialized. */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.RECORD_COMPONENT)
+public @interface PropertyName {
+
+ String value();
+}
diff --git a/firebase-firestore-sdk34/src/main/java/com/google/firebase/firestore/sdk34/ServerTimestamp.java b/firebase-firestore-sdk34/src/main/java/com/google/firebase/firestore/sdk34/ServerTimestamp.java
new file mode 100755
index 00000000000..d91103e0921
--- /dev/null
+++ b/firebase-firestore-sdk34/src/main/java/com/google/firebase/firestore/sdk34/ServerTimestamp.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.firebase.firestore.sdk34;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation used to mark a timestamp component as being populated via Server Timestamps. If a
+ * record being written contains null for a @ServerTimestamp annotated component, it will be
+ * replaced with a server-generated timestamp.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.RECORD_COMPONENT)
+public @interface ServerTimestamp {}
diff --git a/firebase-firestore-sdk34/src/roboUtil/java/com/google/firebase/firestore/TestUtil.java b/firebase-firestore-sdk34/src/roboUtil/java/com/google/firebase/firestore/TestUtil.java
new file mode 100755
index 00000000000..da70dab4a8c
--- /dev/null
+++ b/firebase-firestore-sdk34/src/roboUtil/java/com/google/firebase/firestore/TestUtil.java
@@ -0,0 +1,126 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.firebase.firestore;
+
+import com.google.firebase.firestore.model.DocumentKey;
+import com.google.firebase.firestore.model.ResourcePath;
+
+import static org.mockito.Mockito.mock;
+
+public class TestUtil {
+
+ private static final FirebaseFirestore FIRESTORE = mock(FirebaseFirestore.class);
+
+ public static FirebaseFirestore firestore() {
+ return FIRESTORE;
+ }
+
+ public static CollectionReference collectionReference(String path) {
+ return new CollectionReference(ResourcePath.fromString(path), FIRESTORE);
+ }
+
+ public static DocumentReference documentReference(String path) {
+ return new DocumentReference(key(path), FIRESTORE);
+ }
+
+ public static DocumentKey key(String key) {
+ return DocumentKey.fromPathString(key);
+ }
+
+ /*
+ public static DocumentSnapshot documentSnapshot(
+ String path, Map data, boolean isFromCache) {
+ if (data == null) {
+ return DocumentSnapshot.fromNoDocument(FIRESTORE, key(path), isFromCache);
+ } else {
+ return DocumentSnapshot.fromDocument(
+ FIRESTORE, doc(path, 1L, data), isFromCache, */
+/*hasPendingWrites=*/ /*
+ false);
+ }
+ }
+ */
+
+ /*
+ public static Query query(String path) {
+ return new Query(com.google.firebase.firestore.testutil.TestUtil.query(path), FIRESTORE);
+ }
+
+ /**
+ * A convenience method for creating a particular query snapshot for tests.
+ *
+ * @param path To be used in constructing the query.
+ * @param oldDocs Provides the prior set of documents in the QuerySnapshot. Each entry maps to a
+ * document, with the key being the document id, and the value being the document contents.
+ * @param docsToAdd Specifies data to be added into the query snapshot as of now. Each entry maps
+ * to a document, with the key being the document id, and the value being the document
+ * contents.
+ * @param isFromCache Whether the query snapshot is cache result.
+ * @return A query snapshot that consists of both sets of documents.
+ * /
+ public static QuerySnapshot querySnapshot(
+ String path,
+ Map oldDocs,
+ Map docsToAdd,
+ boolean hasPendingWrites,
+ boolean isFromCache,
+ boolean hasCachedResults) {
+ DocumentSet oldDocuments = docSet(Document.KEY_COMPARATOR);
+ ImmutableSortedSet mutatedKeys = DocumentKey.emptyKeySet();
+ for (Map.Entry pair : oldDocs.entrySet()) {
+ String docKey = path + "/" + pair.getKey();
+ MutableDocument doc = doc(docKey, 1L, pair.getValue());
+ if (hasPendingWrites) {
+ doc.setHasCommittedMutations();
+ mutatedKeys = mutatedKeys.insert(key(docKey));
+ }
+ oldDocuments = oldDocuments.add(doc);
+ }
+ DocumentSet newDocuments = docSet(Document.KEY_COMPARATOR);
+ List documentChanges = new ArrayList<>();
+ for (Map.Entry pair : docsToAdd.entrySet()) {
+ String docKey = path + "/" + pair.getKey();
+ MutableDocument docToAdd = doc(docKey, 1L, pair.getValue());
+ if (hasPendingWrites) {
+ docToAdd.setHasCommittedMutations();
+ mutatedKeys = mutatedKeys.insert(key(docKey));
+ }
+ newDocuments = newDocuments.add(docToAdd);
+ documentChanges.add(DocumentViewChange.create(Type.ADDED, docToAdd));
+ }
+ ViewSnapshot viewSnapshot =
+ new ViewSnapshot(
+ com.google.firebase.firestore.testutil.TestUtil.query(path),
+ newDocuments,
+ oldDocuments,
+ documentChanges,
+ isFromCache,
+ mutatedKeys,
+ /* didSyncStateChange= * / true,
+ /* excludesMetadataChanges= * / false,
+ hasCachedResults);
+ return new QuerySnapshot(query(path), viewSnapshot, FIRESTORE);
+ }
+
+ public static T waitFor(Task task) {
+ if (!task.isComplete()) {
+ Robolectric.flushBackgroundThreadScheduler();
+ }
+ Assert.assertTrue(
+ "Expected task to be completed after background thread flush", task.isComplete());
+ return task.getResult();
+ }
+ */
+}
diff --git a/firebase-firestore-sdk34/src/test/AndroidManifest.xml b/firebase-firestore-sdk34/src/test/AndroidManifest.xml
new file mode 100755
index 00000000000..26e8ce7a35c
--- /dev/null
+++ b/firebase-firestore-sdk34/src/test/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/firebase-firestore-sdk34/src/test/java/com/google/firebase/firestore/sdk34/LocalFirestoreHelper.java b/firebase-firestore-sdk34/src/test/java/com/google/firebase/firestore/sdk34/LocalFirestoreHelper.java
new file mode 100755
index 00000000000..b43a29512d8
--- /dev/null
+++ b/firebase-firestore-sdk34/src/test/java/com/google/firebase/firestore/sdk34/LocalFirestoreHelper.java
@@ -0,0 +1,406 @@
+/*
+ * Copyright 2017 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.firebase.firestore.sdk34;
+
+import java.math.BigInteger;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.Nullable;
+
+import org.mockito.stubbing.Answer;
+
+import com.google.common.reflect.TypeToken;
+import com.google.firebase.Timestamp;
+import com.google.firebase.firestore.Blob;
+import com.google.firebase.firestore.GeoPoint;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.firestore.v1.ArrayValue;
+import com.google.firestore.v1.BatchGetDocumentsResponse;
+import com.google.firestore.v1.CommitRequest;
+import com.google.firestore.v1.CommitResponse;
+import com.google.firestore.v1.DocumentMask;
+import com.google.firestore.v1.DocumentTransform.FieldTransform;
+import com.google.firestore.v1.MapValue;
+import com.google.firestore.v1.Value;
+import com.google.firestore.v1.Write;
+import com.google.gson.Gson;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+
+public final class LocalFirestoreHelper {
+
+ /*
+ public static final String DATABASE_NAME;
+ public static final String DOCUMENT_PATH;
+ public static final String DOCUMENT_NAME;
+ public static final String DOCUMENT_ROOT;
+
+ public static final SingleComponent SINGLE_COMPONENT_OBJECT;
+ public static final Map SINGLE_COMPONENT_PROTO;
+
+ public static final NestedRecord NESTED_RECORD_OBJECT;
+
+ public static final ServerTimestamp SERVER_TIMESTAMP_OBJECT;
+ public static final Map SERVER_TIMESTAMP_PROTO;
+
+ public static final AllSupportedTypes ALL_SUPPORTED_TYPES_OBJECT;
+ public static final Map ALL_SUPPORTED_TYPES_PROTO;
+
+ public static final Date DATE;
+ public static final Timestamp TIMESTAMP;
+ public static final GeoPoint GEO_POINT;
+ public static final Blob BLOB;
+
+
+ public record SingleComponent(
+
+ String foo
+ ){}
+
+ public record NestedRecord(
+ SingleComponent first,
+ AllSupportedTypes second
+ ){}
+*/
+
+ public record ServerTimestamp (
+
+ @com.google.firebase.firestore.sdk34.ServerTimestamp Date foo,
+ Inner inner
+
+ ){
+ record Inner (
+
+ @com.google.firebase.firestore.sdk34.ServerTimestamp Date bar
+ ){}
+ }
+
+ public record InvalidRecord (
+ BigInteger bigIntegerValue,
+ Byte byteValue,
+ Short shortValue
+ ){}
+
+ public static Map map(K key, V value, Object... moreKeysAndValues) {
+ Map map = new HashMap<>();
+ map.put(key, value);
+
+ for (var i = 0; i < moreKeysAndValues.length; i += 2) {
+ map.put((K) moreKeysAndValues[i], (V) moreKeysAndValues[i + 1]);
+ }
+
+ return map;
+ }
+
+ /*
+ public static Answer getAllResponse(
+ final Map... fields) {
+ var responses = new BatchGetDocumentsResponse[fields.length];
+
+ for (var i = 0; i < fields.length; ++i) {
+ var name = DOCUMENT_NAME;
+ if (fields.length > 1) {
+ name += i + 1;
+ }
+ var response = BatchGetDocumentsResponse.newBuilder();
+ response
+ .getFoundBuilder()
+ .setCreateTime(com.google.protobuf.Timestamp.newBuilder().setSeconds(1).setNanos(2));
+ response
+ .getFoundBuilder()
+ .setUpdateTime(com.google.protobuf.Timestamp.newBuilder().setSeconds(3).setNanos(4));
+ response.setReadTime(com.google.protobuf.Timestamp.newBuilder().setSeconds(5).setNanos(6));
+ response.getFoundBuilder().setName(name).putAllFields(fields[i]);
+ responses[i] = response.build();
+ }
+
+ return streamingResponse(responses, null);
+ }
+
+ /** Returns a stream of responses followed by an optional exception. * /
+ public static Answer streamingResponse(
+ final T[] response, @Nullable final Throwable throwable) {
+ return invocation -> {
+ var args = invocation.getArguments();
+ var observer = (ResponseObserver) args[1];
+ observer.onStart(mock(StreamController.class));
+ for (var resp : response) {
+ observer.onResponse(resp);
+ }
+ if (throwable != null) {
+ observer.onError(throwable);
+ }
+ observer.onComplete();
+ return null;
+ };
+ }
+
+ public static ApiFuture commitResponse(int adds, int deletes) {
+ var commitResponse = CommitResponse.newBuilder();
+ commitResponse.getCommitTimeBuilder().setSeconds(0).setNanos(0);
+ for (var i = 0; i < adds; ++i) {
+ commitResponse.addWriteResultsBuilder().getUpdateTimeBuilder().setSeconds(i).setNanos(i);
+ }
+ for (var i = 0; i < deletes; ++i) {
+ commitResponse.addWriteResultsBuilder();
+ }
+ return ApiFutures.immediateFuture(commitResponse.build());
+ }
+
+ public static FieldTransform serverTimestamp() {
+ return FieldTransform.newBuilder()
+ .setSetToServerValue(FieldTransform.ServerValue.REQUEST_TIME)
+ .build();
+ }
+
+ public static List transform(
+ String fieldPath, FieldTransform fieldTransform, Object... fieldPathOrTransform) {
+
+ List transforms = new ArrayList<>();
+ var transformBuilder = FieldTransform.newBuilder();
+ transformBuilder.setFieldPath(fieldPath).mergeFrom(fieldTransform);
+
+ transforms.add(transformBuilder.build());
+
+ for (var i = 0; i < fieldPathOrTransform.length; i += 2) {
+ var path = (String) fieldPathOrTransform[i];
+ var transform = (FieldTransform) fieldPathOrTransform[i + 1];
+ transforms.add(FieldTransform.newBuilder().setFieldPath(path).mergeFrom(transform).build());
+ }
+ return transforms;
+ }
+
+ public static Write create(Map fields, String docPath) {
+ var write = Write.newBuilder();
+ var document = write.getUpdateBuilder();
+ document.setName(DOCUMENT_ROOT + docPath);
+ document.putAllFields(fields);
+ write.getCurrentDocumentBuilder().setExists(false);
+ return write.build();
+ }
+
+ public static Write create(Map fields) {
+ return create(fields, DOCUMENT_PATH);
+ }
+
+ public static Write set(Map fields) {
+ return set(fields, null, DOCUMENT_PATH);
+ }
+
+ public static Write set(Map fields, @Nullable List fieldMap) {
+ return set(fields, fieldMap, DOCUMENT_PATH);
+ }
+
+ public static Write set(
+ Map fields, @Nullable List fieldMap, String docPath) {
+ var write = Write.newBuilder();
+ var document = write.getUpdateBuilder();
+ document.setName(DOCUMENT_ROOT + docPath);
+ document.putAllFields(fields);
+
+ if (fieldMap != null) {
+ write.getUpdateMaskBuilder().addAllFieldPaths(fieldMap);
+ }
+
+ return write.build();
+ }
+
+ public static CommitRequest commit(@Nullable String transactionId, Write... writes) {
+ var commitRequest = CommitRequest.newBuilder();
+ commitRequest.setDatabase(DATABASE_NAME);
+ commitRequest.addAllWrites(Arrays.asList(writes));
+
+ if (transactionId != null) {
+ commitRequest.setTransaction(ByteString.copyFromUtf8(transactionId));
+ }
+
+ return commitRequest.build();
+ }
+
+ public static CommitRequest commit(Write... writes) {
+ return commit(null, writes);
+ }
+
+ public static CommitRequest commit(Write write, List transforms) {
+ return commit((String) null, write.toBuilder().addAllUpdateTransforms(transforms).build());
+ }
+
+ public static void assertCommitEquals(CommitRequest expected, CommitRequest actual) {
+ assertEquals(sortCommit(expected), sortCommit(actual));
+ }
+
+ private static CommitRequest sortCommit(CommitRequest commit) {
+ var builder = commit.toBuilder();
+
+ for (var writes : builder.getWritesBuilderList()) {
+ if (writes.hasUpdateMask()) {
+ var updateMask = new ArrayList<>(writes.getUpdateMask().getFieldPathsList());
+ Collections.sort(updateMask);
+ writes.setUpdateMask(DocumentMask.newBuilder().addAllFieldPaths(updateMask));
+ }
+
+ if (!writes.getUpdateTransformsList().isEmpty()) {
+ var transformList = new ArrayList<>(writes.getUpdateTransformsList());
+ transformList.sort(Comparator.comparing(FieldTransform::getFieldPath));
+ writes.clearUpdateTransforms().addAllUpdateTransforms(transformList);
+ }
+ }
+
+ return builder.build();
+ }
+
+ public record AllSupportedTypes (
+
+ String foo,
+ Double doubleValue,
+ long longValue,
+ double nanValue,
+ double infValue,
+ double negInfValue,
+ boolean trueValue,
+ boolean falseValue,
+ SingleComponent objectValue,
+ Date dateValue,
+ Timestamp timestampValue,
+ List arrayValue,
+ String nullValue,
+ Blob bytesValue,
+ GeoPoint geoPointValue,
+ Map model
+ ){}
+
+ static {
+ try {
+ DATE = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.S z").parse("1985-03-18 08:20:00.123 CET");
+ } catch (ParseException e) {
+ throw new RuntimeException("Failed to parse date", e);
+ }
+
+ TIMESTAMP =
+ Timestamp.ofTimeSecondsAndNanos(
+ TimeUnit.MILLISECONDS.toSeconds(DATE.getTime()),
+ 123000); // Firestore truncates to microsecond precision.
+ GEO_POINT = new GeoPoint(50.1430847, -122.9477780);
+ BLOB = Blob.fromBytes(new byte[] {1, 2, 3});
+
+ DATABASE_NAME = "projects/test-project/databases/(default)";
+ DOCUMENT_PATH = "coll/doc";
+ DOCUMENT_NAME = DATABASE_NAME + "/documents/" + DOCUMENT_PATH;
+ DOCUMENT_ROOT = DATABASE_NAME + "/documents/";
+
+ SINGLE_COMPONENT_OBJECT = new SingleComponent("bar");
+ SINGLE_COMPONENT_PROTO = map("foo", Value.newBuilder().setStringValue("bar").build());
+
+ SERVER_TIMESTAMP_PROTO = Collections.emptyMap();
+ SERVER_TIMESTAMP_OBJECT = new ServerTimestamp(null, new ServerTimestamp.Inner(null));
+
+ ALL_SUPPORTED_TYPES_OBJECT = new AllSupportedTypes("bar", 0.0, 0L, Double.NaN, Double.POSITIVE_INFINITY,
+ Double.NEGATIVE_INFINITY, true, false,
+ new SingleComponent("bar"), DATE,
+ TIMESTAMP, ImmutableList.of("foo"), null, BLOB, GEO_POINT,
+ ImmutableMap.of("foo", SINGLE_COMPONENT_OBJECT.foo()));
+ ALL_SUPPORTED_TYPES_PROTO =
+ ImmutableMap.builder()
+ .put("foo", Value.newBuilder().setStringValue("bar").build())
+ .put("doubleValue", Value.newBuilder().setDoubleValue(0.0).build())
+ .put("longValue", Value.newBuilder().setIntegerValue(0L).build())
+ .put("nanValue", Value.newBuilder().setDoubleValue(Double.NaN).build())
+ .put("infValue", Value.newBuilder().setDoubleValue(Double.POSITIVE_INFINITY).build())
+ .put("negInfValue", Value.newBuilder().setDoubleValue(Double.NEGATIVE_INFINITY).build())
+ .put("trueValue", Value.newBuilder().setBooleanValue(true).build())
+ .put("falseValue", Value.newBuilder().setBooleanValue(false).build())
+ .put(
+ "objectValue",
+ Value.newBuilder()
+ .setMapValue(MapValue.newBuilder().putAllFields(SINGLE_COMPONENT_PROTO))
+ .build())
+ .put(
+ "dateValue",
+ Value.newBuilder()
+ .setTimestampValue(
+ com.google.protobuf.Timestamp.newBuilder()
+ .setSeconds(479978400)
+ .setNanos(123000000)) // Dates only support millisecond precision.
+ .build())
+ .put(
+ "timestampValue",
+ Value.newBuilder()
+ .setTimestampValue(
+ com.google.protobuf.Timestamp.newBuilder()
+ .setSeconds(479978400)
+ .setNanos(123000)) // Timestamps supports microsecond precision.
+ .build())
+ .put(
+ "arrayValue",
+ Value.newBuilder()
+ .setArrayValue(
+ ArrayValue.newBuilder().addValues(Value.newBuilder().setStringValue("foo")))
+ .build())
+ .put("nullValue", Value.newBuilder().setNullValue(NullValue.NULL_VALUE).build())
+ .put("bytesValue", Value.newBuilder().setBytesValue(BLOB.toByteString()).build())
+ .put(
+ "geoPointValue",
+ Value.newBuilder()
+ .setGeoPointValue(
+ LatLng.newBuilder().setLatitude(50.1430847).setLongitude(-122.9477780))
+ .build())
+ .put(
+ "model",
+ Value.newBuilder()
+ .setMapValue(MapValue.newBuilder().putAllFields(SINGLE_COMPONENT_PROTO))
+ .build())
+ .build();
+ SINGLE_WRITE_COMMIT_RESPONSE = commitResponse(/* adds= * / 1, /* deletes= * / 0);
+
+ FIELD_TRANSFORM_COMMIT_RESPONSE = commitResponse(/* adds= * / 2, /* deletes= * / 0);
+
+ NESTED_RECORD_OBJECT = new NestedRecord(SINGLE_COMPONENT_OBJECT, ALL_SUPPORTED_TYPES_OBJECT);
+ }
+ */
+
+ @SuppressWarnings("unchecked")
+ public static Map mapAnyType(Object... entries) {
+ Map res = new HashMap<>();
+ for (var i = 0; i < entries.length; i += 2) {
+ res.put((String) entries[i], (T) entries[i + 1]);
+ }
+ return res;
+ }
+
+ private static Map fromJsonString(String json) {
+ var type = new TypeToken