diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/Pipeline.kt b/firebase-firestore/src/main/java/com/google/firebase/firestore/Pipeline.kt index ee675db87d4..1954e04cc16 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/Pipeline.kt +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/Pipeline.kt @@ -17,7 +17,10 @@ package com.google.firebase.firestore import com.google.android.gms.tasks.Task import com.google.android.gms.tasks.TaskCompletionSource import com.google.firebase.Timestamp +import com.google.firebase.firestore.core.Canonicalizable +import com.google.firebase.firestore.model.Document import com.google.firebase.firestore.model.DocumentKey +import com.google.firebase.firestore.model.MutableDocument import com.google.firebase.firestore.model.Values import com.google.firebase.firestore.pipeline.AddFieldsStage import com.google.firebase.firestore.pipeline.AggregateFunction @@ -29,6 +32,7 @@ import com.google.firebase.firestore.pipeline.CollectionSource import com.google.firebase.firestore.pipeline.DatabaseSource import com.google.firebase.firestore.pipeline.DistinctStage import com.google.firebase.firestore.pipeline.DocumentsSource +import com.google.firebase.firestore.pipeline.EvaluationContext import com.google.firebase.firestore.pipeline.Expr import com.google.firebase.firestore.pipeline.Expr.Companion.field import com.google.firebase.firestore.pipeline.ExprWithAlias @@ -51,6 +55,7 @@ import com.google.firebase.firestore.pipeline.Stage import com.google.firebase.firestore.pipeline.UnionStage import com.google.firebase.firestore.pipeline.UnnestStage import com.google.firebase.firestore.pipeline.WhereStage +import com.google.firebase.firestore.util.Assert.fail import com.google.firestore.v1.ExecutePipelineRequest import com.google.firestore.v1.StructuredPipeline import com.google.firestore.v1.Value @@ -759,7 +764,7 @@ internal constructor( firestore: FirebaseFirestore, userDataReader: UserDataReader, stages: List> -) : AbstractPipeline(firestore, userDataReader, stages) { +) : AbstractPipeline(firestore, userDataReader, stages), Canonicalizable { internal constructor( firestore: FirebaseFirestore, userDataReader: UserDataReader, @@ -786,31 +791,107 @@ internal constructor( fun where(condition: BooleanExpr): RealtimePipeline = append(WhereStage(condition)) - internal fun rewriteStages(): RealtimePipeline { + internal val rewrittenStages: List> by lazy { var hasOrder = false - return with( - buildList { - for (stage in stages) when (stage) { - // Stages whose semantics depend on ordering - is LimitStage, - is OffsetStage -> { - if (!hasOrder) { - hasOrder = true - add(SortStage.BY_DOCUMENT_ID) - } - add(stage) - } - is SortStage -> { + buildList { + for (stage in stages) when (stage) { + // Stages whose semantics depend on ordering + is LimitStage, + is OffsetStage -> { + if (!hasOrder) { hasOrder = true - add(stage.withStableOrdering()) + add(SortStage.BY_DOCUMENT_ID) } - else -> add(stage) + add(stage) } - if (!hasOrder) { - add(SortStage.BY_DOCUMENT_ID) + is SortStage -> { + hasOrder = true + add(stage.withStableOrdering()) } + else -> add(stage) } - ) + if (!hasOrder) { + add(SortStage.BY_DOCUMENT_ID) + } + } + } + + override fun canonicalId(): String { + return rewrittenStages.joinToString("|") { stage -> (stage as Canonicalizable).canonicalId() } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is RealtimePipeline) return false + return stages == other.stages + } + + override fun hashCode(): Int { + return stages.hashCode() + } + + internal fun evaluate(inputs: List): List { + val context = EvaluationContext(this) + return rewrittenStages.fold(inputs) { documents, stage -> stage.evaluate(context, documents) } + } + + internal fun matchesAllDocuments(): Boolean { + for (stage in rewrittenStages) { + // Check for LimitStage + if (stage.name == "limit") { + return false + } + + // Check for Where stage + if (stage is WhereStage) { + // Check if it's the special 'exists(__name__)' case + val funcExpr = stage.condition as? FunctionExpr + if (funcExpr?.name == "exists" && funcExpr.params.size == 1) { + val fieldExpr = funcExpr.params[0] as? Field + if (fieldExpr?.fieldPath?.isKeyField == true) { + continue // This specific 'exists(__name__)' filter doesn't count + } + } + return false + } + // TODO(pipeline) : Add checks for other filtering stages like Aggregate, + // Distinct, FindNearest once they are implemented. + } + return true + } + + internal fun hasLimit(): Boolean { + for (stage in rewrittenStages) { + if (stage.name == "limit") { + return true + } + // TODO(pipeline): need to check for other stages that could have a limit, + // like findNearest + } + return false + } + + internal fun matches(doc: Document): Boolean { + val result = evaluate(listOf(doc as MutableDocument)) + return result.isNotEmpty() + } + + private fun evaluateContext(): EvaluationContext { + return EvaluationContext(this) + } + + internal fun comparator(): Comparator = + getLastEffectiveSortStage().comparator(evaluateContext()) + + private fun getLastEffectiveSortStage(): SortStage { + for (stage in rewrittenStages.asReversed()) { + if (stage is SortStage) { + return stage + } + // TODO(pipeline): Consider stages that might invalidate ordering later, + // like fineNearest + } + throw fail("RealtimePipeline must contain at least one Sort stage (ensured by RewriteStages).") } } diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/core/Canonicalizable.kt b/firebase-firestore/src/main/java/com/google/firebase/firestore/core/Canonicalizable.kt new file mode 100644 index 00000000000..9b463b43a5c --- /dev/null +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/core/Canonicalizable.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2025 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.core + +/** An internal interface for classes that can be canonicalized to a string representation. */ +internal interface Canonicalizable { + fun canonicalId(): String +} diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/core/PipelineUtil.kt b/firebase-firestore/src/main/java/com/google/firebase/firestore/core/PipelineUtil.kt index b23863d41a1..095f10822ed 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/core/PipelineUtil.kt +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/core/PipelineUtil.kt @@ -24,285 +24,109 @@ import com.google.firebase.firestore.pipeline.CollectionGroupSource import com.google.firebase.firestore.pipeline.CollectionSource import com.google.firebase.firestore.pipeline.DatabaseSource import com.google.firebase.firestore.pipeline.DocumentsSource -import com.google.firebase.firestore.pipeline.Expr -import com.google.firebase.firestore.pipeline.Field -import com.google.firebase.firestore.pipeline.FunctionExpr +import com.google.firebase.firestore.pipeline.InternalOptions import com.google.firebase.firestore.pipeline.LimitStage -import com.google.firebase.firestore.pipeline.Ordering -import com.google.firebase.firestore.pipeline.SortStage -import com.google.firebase.firestore.pipeline.Stage -import com.google.firebase.firestore.pipeline.WhereStage -import com.google.firebase.firestore.util.Assert.fail -import com.google.firestore.v1.Value - -private fun runPipeline(pipeline: RealtimePipeline, input: List): List { - // This is a placeholder implementation. The actual pipeline execution logic is required. - // For now, returning an empty list to ensure compilation. - // A proper implementation would execute each stage of the pipeline on the input documents. - return emptyList() -} - -// Anonymous namespace for canonicalization helpers -private fun canonifyConstant(constant: Expr.Constant): String { - return Values.canonicalId(constant.value) -} - -private fun canonifyExpr(expr: Expr): String { - return when (expr) { - is Field -> "fld(${expr.fieldPath.canonicalString()})" - is Expr.Constant -> "cst(${canonifyConstant(expr)})" - is FunctionExpr -> { - val paramStrings = expr.params.map { paramPtr -> canonifyExpr(paramPtr) } - "fn(${expr.name}[${paramStrings.joinToString(",")}])" - } - else -> throw fail("Canonify a unrecognized expr") - } -} - -private fun canonifySortOrderings(orders: List): String { - return orders - .map { order -> - val direction = if (order.dir == Ordering.Direction.ASCENDING) "asc" else "desc" - "${canonifyExpr(order.expr)}$direction" - } - .joinToString(",") -} - -private fun canonifyStage(stage: Stage<*>): String { - return when (stage) { - is CollectionSource -> "${stage.name}(${stage.path})" - is CollectionGroupSource -> "${stage.name}(${stage.collectionId})" - is DocumentsSource -> { - val sortedDocuments = stage.documents.sorted() - "${stage.name}(${sortedDocuments.joinToString(",")})" - } - is WhereStage -> "${stage.name}(${canonifyExpr(stage.expr)})" - is SortStage -> "${stage.name}(${canonifySortOrderings(stage.orders)})" - is LimitStage -> "${stage.name}(${stage.limit})" - else -> throw fail("Trying to canonify an unrecognized stage type ${stage.name}") - } -} - -// Canonicalizes a RealtimePipeline by canonicalizing its stages. -private fun canonifyPipeline(pipeline: RealtimePipeline): String { - return pipeline.rewriteStages().stages.map { stage -> canonifyStage(stage) }.joinToString("|") -} +import com.google.firebase.firestore.util.Assert.hardAssert /** A class that wraps either a Query or a RealtimePipeline. */ -class QueryOrPipeline -private constructor( - private val query: Query?, - private val pipeline: RealtimePipeline?, -) { - constructor(query: Query) : this(query, null) - constructor(pipeline: RealtimePipeline) : this(null, pipeline) +sealed class QueryOrPipeline { + data class QueryWrapper(val query: Query) : QueryOrPipeline() + data class PipelineWrapper(val pipeline: RealtimePipeline) : QueryOrPipeline() val isQuery: Boolean - get() = query != null + get() = this is QueryWrapper val isPipeline: Boolean - get() = pipeline != null + get() = this is PipelineWrapper fun query(): Query { - return query!! + return (this as QueryWrapper).query } fun pipeline(): RealtimePipeline { - return pipeline!! - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is QueryOrPipeline) return false - if (isPipeline != other.isPipeline) return false - - return if (isPipeline) { - canonifyPipeline(pipeline()) == canonifyPipeline(other.pipeline()) - } else { - query() == other.query() - } - } - - override fun hashCode(): Int { - return if (isPipeline) { - canonifyPipeline(pipeline()).hashCode() - } else { - query().hashCode() - } + return (this as PipelineWrapper).pipeline } fun canonicalId(): String { - return if (isPipeline) { - canonifyPipeline(pipeline()) - } else { - query().canonicalId() + return when (this) { + is PipelineWrapper -> pipeline.canonicalId() + is QueryWrapper -> query.canonicalId } } override fun toString(): String { - return if (isPipeline) { - canonicalId() - } else { - query().toString() + return when (this) { + is PipelineWrapper -> pipeline.canonicalId() + is QueryWrapper -> query.toString() } } fun toTargetOrPipeline(): TargetOrPipeline { - return if (isPipeline) { - TargetOrPipeline(pipeline()) - } else { - TargetOrPipeline(query().toTarget()) + return when (this) { + is PipelineWrapper -> TargetOrPipeline.PipelineWrapper(pipeline) + is QueryWrapper -> TargetOrPipeline.TargetWrapper(query.toTarget()) } } fun matchesAllDocuments(): Boolean { - if (isPipeline) { - for (stage in pipeline().rewrittenStages) { - // Check for LimitStage - if (stage.name == "limit") { - return false - } - - // Check for Where stage - if (stage is Where) { - // Check if it's the special 'exists(__name__)' case - val funcExpr = stage.expr as? FunctionExpr - if (funcExpr?.name == "exists" && funcExpr.params.size == 1) { - val fieldExpr = funcExpr.params[0] as? Field - if (fieldExpr?.fieldPath?.isKeyFieldPath == true) { - continue // This specific 'exists(__name__)' filter doesn't count - } - } - return false // Any other Where stage means it filters documents - } - // TODO(pipeline) : Add checks for other filtering stages like Aggregate, - // Distinct, FindNearest once they are implemented. - } - return true // No filtering stages found (besides allowed ones) + return when (this) { + is PipelineWrapper -> pipeline.matchesAllDocuments() + is QueryWrapper -> query.matchesAllDocuments() } - - return query().matchesAllDocuments() } fun hasLimit(): Boolean { - if (isPipeline) { - for (stage in pipeline().rewrittenStages) { - // Check for LimitStage - if (stage.name == "limit") { - return true - } - // TODO(pipeline): need to check for other stages that could have a limit, - // like findNearest - } - return false + return when (this) { + is PipelineWrapper -> pipeline.hasLimit() + is QueryWrapper -> query.hasLimit() } - - return query().hasLimit() } fun matches(doc: Document): Boolean { - if (isPipeline) { - val result = runPipeline(pipeline(), listOf(doc)) - return result.isNotEmpty() + return when (this) { + is PipelineWrapper -> pipeline.matches(doc) + is QueryWrapper -> query.matches(doc) } - - return query().matches(doc) } - fun comparator(): DocumentComparator { - if (isPipeline) { - // Capture pipeline by reference. Orderings captured by value inside lambda. - val p = pipeline() - val orderings = getLastEffectiveSortOrderings(p) - return DocumentComparator { d1, d2 -> - val context = p.evaluateContext - - for (ordering in orderings) { - val expr = ordering.expr - // Evaluate expression for both documents using expr->Evaluate - // (assuming this method exists) Pass const references to documents. - val leftValue = expr.toEvaluable().evaluate(context, d1) - val rightValue = expr.toEvaluable().evaluate(context, d2) - - // Compare results, using MinValue for error - val comparison = - Values.compare( - if (leftValue.isErrorOrUnset) Value.getDefaultInstance() else leftValue.value!!, - if (rightValue.isErrorOrUnset) Value.getDefaultInstance() else rightValue.value!!, - ) - - if (comparison != 0) { - return@DocumentComparator if (ordering.direction == Ordering.Direction.ASCENDING) { - comparison - } else { - -comparison - } - } - } - 0 - } + fun comparator(): Comparator { + return when (this) { + is PipelineWrapper -> pipeline.comparator() + is QueryWrapper -> query.comparator() } - - return query().comparator() } } /** A class that wraps either a Target or a RealtimePipeline. */ -class TargetOrPipeline -private constructor( - private val target: Target?, - private val pipeline: RealtimePipeline?, -) { - constructor(target: Target) : this(target, null) - constructor(pipeline: RealtimePipeline) : this(null, pipeline) +sealed class TargetOrPipeline { + data class TargetWrapper(val target: Target) : TargetOrPipeline() + data class PipelineWrapper(val pipeline: RealtimePipeline) : TargetOrPipeline() val isTarget: Boolean - get() = target != null + get() = this is TargetWrapper val isPipeline: Boolean - get() = pipeline != null + get() = this is PipelineWrapper fun target(): Target { - return target!! + return (this as TargetWrapper).target } fun pipeline(): RealtimePipeline { - return pipeline!! - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is TargetOrPipeline) return false - if (isPipeline != other.isPipeline) return false - - return if (isPipeline) { - canonifyPipeline(pipeline()) == canonifyPipeline(other.pipeline()) - } else { - target() == other.target() - } - } - - override fun hashCode(): Int { - return if (isPipeline) { - canonifyPipeline(pipeline()).hashCode() - } else { - target().hashCode() - } + return (this as PipelineWrapper).pipeline } fun canonicalId(): String { - return if (isPipeline) { - canonifyPipeline(pipeline()) - } else { - target().canonicalId() + return when (this) { + is PipelineWrapper -> pipeline.canonicalId() + is TargetWrapper -> target.canonicalId } } override fun toString(): String { - return if (isPipeline) { - canonicalId() - } else { - target().toString() + return when (this) { + is PipelineWrapper -> pipeline.canonicalId() + is TargetWrapper -> target.toString() } } } @@ -339,7 +163,7 @@ fun getPipelineFlavor(pipeline: RealtimePipeline): PipelineFlavor { // Determines the source type of the given pipeline based on its first stage. fun getPipelineSourceType(pipeline: RealtimePipeline): PipelineSourceType { - HardAssert.hardAssert( + hardAssert( !pipeline.stages.isEmpty(), "Pipeline must have at least one stage to determine its source.", ) @@ -356,7 +180,7 @@ fun getPipelineSourceType(pipeline: RealtimePipeline): PipelineSourceType { // group. fun getPipelineCollectionGroup(pipeline: RealtimePipeline): String? { if (getPipelineSourceType(pipeline) == PipelineSourceType.COLLECTION_GROUP) { - HardAssert.hardAssert( + hardAssert( !pipeline.stages.isEmpty(), "Pipeline source is CollectionGroup but stages are empty.", ) @@ -371,7 +195,7 @@ fun getPipelineCollectionGroup(pipeline: RealtimePipeline): String? { // Retrieves the collection path if the pipeline's source is a collection. fun getPipelineCollection(pipeline: RealtimePipeline): String? { if (getPipelineSourceType(pipeline) == PipelineSourceType.COLLECTION) { - HardAssert.hardAssert( + hardAssert( !pipeline.stages.isEmpty(), "Pipeline source is Collection but stages are empty.", ) @@ -384,9 +208,9 @@ fun getPipelineCollection(pipeline: RealtimePipeline): String? { } // Retrieves the document pathes if the pipeline's source is a document source. -fun getPipelineDocuments(pipeline: RealtimePipeline): List? { +fun getPipelineDocuments(pipeline: RealtimePipeline): Array? { if (getPipelineSourceType(pipeline) == PipelineSourceType.DOCUMENTS) { - HardAssert.hardAssert( + hardAssert( !pipeline.stages.isEmpty(), "Pipeline source is Documents but stages are empty.", ) @@ -407,7 +231,7 @@ fun asCollectionPipelineAtPath( val newStages = pipeline.stages.map { stagePtr -> if (stagePtr is CollectionGroupSource) { - CollectionSource(path.canonicalString()) + CollectionSource(path.canonicalString(), pipeline.firestore, InternalOptions.EMPTY) } else { stagePtr } @@ -416,12 +240,13 @@ fun asCollectionPipelineAtPath( // Construct a new RealtimePipeline with the (potentially) modified stages // and the original user_data_reader. return RealtimePipeline( + pipeline.firestore, + pipeline.userDataReader, newStages, - Serializer(pipeline.evaluateContext.serializer.databaseId), ) } -fun getLastEffectiveLimit(pipeline: RealtimePipeline): Long? { +fun getLastEffectiveLimit(pipeline: RealtimePipeline): Int? { for (stagePtr in pipeline.rewrittenStages.asReversed()) { // Check if the stage is a LimitStage if (stagePtr is LimitStage) { diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/pipeline/expressions.kt b/firebase-firestore/src/main/java/com/google/firebase/firestore/pipeline/expressions.kt index 4bf7274dcae..756c25c4309 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/pipeline/expressions.kt +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/pipeline/expressions.kt @@ -22,6 +22,7 @@ import com.google.firebase.firestore.GeoPoint import com.google.firebase.firestore.Pipeline import com.google.firebase.firestore.UserDataReader import com.google.firebase.firestore.VectorValue +import com.google.firebase.firestore.core.Canonicalizable import com.google.firebase.firestore.model.DocumentKey import com.google.firebase.firestore.model.FieldPath as ModelFieldPath import com.google.firebase.firestore.model.FieldPath.CREATE_TIME_PATH @@ -29,6 +30,7 @@ import com.google.firebase.firestore.model.FieldPath.KEY_PATH import com.google.firebase.firestore.model.FieldPath.UPDATE_TIME_PATH import com.google.firebase.firestore.model.MutableDocument import com.google.firebase.firestore.model.Values +import com.google.firebase.firestore.model.Values.canonicalId import com.google.firebase.firestore.model.Values.encodeValue import com.google.firebase.firestore.pipeline.Expr.Companion.field import com.google.firebase.firestore.util.CustomClassMapper @@ -49,15 +51,26 @@ import java.util.Date * The [Expr] class provides a fluent API for building expressions. You can chain together method * calls to create complex expressions. */ -abstract class Expr internal constructor() { +abstract class Expr internal constructor() : Canonicalizable { internal class Constant(val value: Value) : Expr() { override fun toProto(userDataReader: UserDataReader): Value = value - override fun evaluateContext(context: EvaluationContext) = { _: MutableDocument -> + override fun evaluateFunction(context: EvaluationContext) = { _: MutableDocument -> EvaluateResultValue(value) } override fun toString(): String { - return "Constant(value=$value)" + return canonicalId() + } + override fun canonicalId() = "cst(${canonicalId(value)})" + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Constant) return false + return value == other.value + } + + override fun hashCode(): Int { + return value.hashCode() } } @@ -218,19 +231,7 @@ abstract class Expr internal constructor() { */ @JvmStatic fun constant(ref: DocumentReference): Expr { - return object : Expr() { - override fun toProto(userDataReader: UserDataReader): Value { - userDataReader.validateDocumentReference(ref, ::IllegalArgumentException) - return encodeValue(ref) - } - - override fun evaluateContext( - context: EvaluationContext - ): (input: MutableDocument) -> EvaluateResult { - val result = EvaluateResultValue(toProto(context.pipeline.userDataReader)) - return { _ -> result } - } - } + return Constant(encodeValue(ref)) } /** @@ -4109,7 +4110,7 @@ abstract class Expr internal constructor() { internal abstract fun toProto(userDataReader: UserDataReader): Value - internal abstract fun evaluateContext(context: EvaluationContext): EvaluateDocument + internal abstract fun evaluateFunction(context: EvaluationContext): EvaluateDocument } /** Expressions that have an alias are [Selectable] */ @@ -4133,7 +4134,22 @@ abstract class Selectable : Expr() { class ExprWithAlias internal constructor(override val alias: String, override val expr: Expr) : Selectable() { override fun toProto(userDataReader: UserDataReader): Value = expr.toProto(userDataReader) - override fun evaluateContext(context: EvaluationContext) = expr.evaluateContext(context) + override fun evaluateFunction(context: EvaluationContext) = expr.evaluateFunction(context) + override fun canonicalId() = expr.canonicalId() + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is ExprWithAlias) return false + if (alias != other.alias) return false + if (expr != other.expr) return false + return true + } + + override fun hashCode(): Int { + var result = alias.hashCode() + result = 31 * result + expr.hashCode() + return result + } } /** @@ -4168,7 +4184,7 @@ class Field internal constructor(internal val fieldPath: ModelFieldPath) : Selec internal fun toProto(): Value = Value.newBuilder().setFieldReferenceValue(fieldPath.canonicalString()).build() - override fun evaluateContext(context: EvaluationContext) = + override fun evaluateFunction(context: EvaluationContext) = block@{ input: MutableDocument -> EvaluateResultValue( when (fieldPath) { @@ -4180,6 +4196,18 @@ class Field internal constructor(internal val fieldPath: ModelFieldPath) : Selec } ) } + + override fun canonicalId(): String = "fld(${fieldPath.canonicalString()})" + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Field) return false + return fieldPath == other.fieldPath + } + + override fun hashCode(): Int { + return fieldPath.hashCode() + } } /** @@ -4247,8 +4275,29 @@ internal constructor( return Value.newBuilder().setFunctionValue(builder).build() } - final override fun evaluateContext(context: EvaluationContext): EvaluateDocument = - function(params.map { expr -> expr.evaluateContext(context) }) + final override fun evaluateFunction(context: EvaluationContext): EvaluateDocument = + function(params.map { expr -> expr.evaluateFunction(context) }) + + override fun canonicalId(): String { + val paramStrings = params.map { paramPtr -> paramPtr.canonicalId() } + return "fn(${name}[${paramStrings.joinToString(",")}])" + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is FunctionExpr) return false + if (name != other.name) return false + if (!params.contentEquals(other.params)) return false + if (options != other.options) return false + return true + } + + override fun hashCode(): Int { + var result = name.hashCode() + result = 31 * result + params.contentHashCode() + result = 31 * result + options.hashCode() + return result + } } /** A class that represents a filter condition. */ @@ -4352,7 +4401,26 @@ internal constructor(name: String, function: EvaluateFunction, params: Array>(internal val name: String, internal val options: InternalOptions) { internal fun toProtoStage(userDataReader: UserDataReader): Pipeline.Stage { @@ -98,9 +95,9 @@ sealed class Stage>(internal val name: String, internal val options internal open fun evaluate( context: EvaluationContext, - inputs: Flow - ): Flow { - throw NotImplementedError("Stage does not support offline evaluation") + inputs: List + ): List { + throw NotImplementedError("Stage $name does not support offline evaluation") } } @@ -186,15 +183,45 @@ internal constructor(options: InternalOptions = InternalOptions.EMPTY) : Stage("database", options) { override fun self(options: InternalOptions) = DatabaseSource(options) override fun args(userDataReader: UserDataReader): Sequence = emptySequence() + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is DatabaseSource) return false + return options == other.options + } + + override fun hashCode(): Int { + return options.hashCode() + } } class CollectionSource internal constructor( - private val path: String, + val path: String, // We validate [firestore.databaseId] when adding to pipeline. internal val firestore: FirebaseFirestore?, options: InternalOptions -) : Stage("collection", options) { +) : Stage("collection", options), Canonicalizable { + override fun canonicalId(): String { + return "${name}(${path})" + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is CollectionSource) return false + if (path != other.path) return false + if (firestore != other.firestore) return false + if (options != other.options) return false + return true + } + + override fun hashCode(): Int { + var result = path.hashCode() + result = 31 * result + (firestore?.hashCode() ?: 0) + result = 31 * result + options.hashCode() + return result + } + override fun self(options: InternalOptions): CollectionSource = CollectionSource(path, firestore, options) override fun args(userDataReader: UserDataReader): Sequence = @@ -231,8 +258,8 @@ internal constructor( override fun evaluate( context: EvaluationContext, - inputs: Flow - ): Flow { + inputs: List + ): List { return inputs.filter { input -> input.isFoundDocument && input.key.collectionPath.canonicalString() == path } @@ -240,15 +267,33 @@ internal constructor( } class CollectionGroupSource -private constructor(private val collectionId: String, options: InternalOptions) : - Stage("collection_group", options) { +private constructor(val collectionId: String, options: InternalOptions) : + Stage("collection_group", options), Canonicalizable { + override fun canonicalId(): String { + return "${name}(${collectionId})" + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is CollectionGroupSource) return false + if (collectionId != other.collectionId) return false + if (options != other.options) return false + return true + } + + override fun hashCode(): Int { + var result = collectionId.hashCode() + result = 31 * result + options.hashCode() + return result + } + override fun self(options: InternalOptions) = CollectionGroupSource(collectionId, options) override fun args(userDataReader: UserDataReader): Sequence = sequenceOf(Value.newBuilder().setReferenceValue("").build(), encodeValue(collectionId)) override fun evaluate( context: EvaluationContext, - inputs: Flow - ): Flow { + inputs: List + ): List { return inputs.filter { input -> input.isFoundDocument && input.key.collectionGroup == collectionId } @@ -277,9 +322,28 @@ private constructor(private val collectionId: String, options: InternalOptions) internal class DocumentsSource @JvmOverloads internal constructor( - private val documents: Array, + val documents: Array, options: InternalOptions = InternalOptions.EMPTY -) : Stage("documents", options) { +) : Stage("documents", options), Canonicalizable { + override fun canonicalId(): String { + val sortedDocuments = documents.sorted() + return "${name}(${sortedDocuments.joinToString(",")})" + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is DocumentsSource) return false + if (!documents.contentEquals(other.documents)) return false + if (options != other.options) return false + return true + } + + override fun hashCode(): Int { + var result = documents.contentHashCode() + result = 31 * result + options.hashCode() + return result + } + internal constructor(document: String) : this(arrayOf(document)) override fun self(options: InternalOptions) = DocumentsSource(documents, options) override fun args(userDataReader: UserDataReader): Sequence = @@ -302,6 +366,20 @@ internal constructor( override fun self(options: InternalOptions) = AddFieldsStage(fields, options) override fun args(userDataReader: UserDataReader): Sequence = sequenceOf(encodeValue(fields.associate { it.alias to it.toProto(userDataReader) })) + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is AddFieldsStage) return false + if (!fields.contentEquals(other.fields)) return false + if (options != other.options) return false + return true + } + + override fun hashCode(): Int { + var result = fields.contentHashCode() + result = 31 * result + options.hashCode() + return result + } } /** @@ -384,22 +462,56 @@ internal constructor( encodeValue(accumulators.mapValues { entry -> entry.value.toProto(userDataReader) }), encodeValue(groups.mapValues { entry -> entry.value.toProto(userDataReader) }) ) + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is AggregateStage) return false + if (accumulators != other.accumulators) return false + if (groups != other.groups) return false + if (options != other.options) return false + return true + } + + override fun hashCode(): Int { + var result = accumulators.hashCode() + result = 31 * result + groups.hashCode() + result = 31 * result + options.hashCode() + return result + } } internal class WhereStage internal constructor( - private val condition: BooleanExpr, + internal val condition: BooleanExpr, options: InternalOptions = InternalOptions.EMPTY -) : Stage("where", options) { +) : Stage("where", options), Canonicalizable { + override fun canonicalId(): String { + return "${name}(${condition.canonicalId()})" + } + override fun self(options: InternalOptions) = WhereStage(condition, options) override fun args(userDataReader: UserDataReader): Sequence = sequenceOf(condition.toProto(userDataReader)) + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is WhereStage) return false + if (condition != other.condition) return false + if (options != other.options) return false + return true + } + + override fun hashCode(): Int { + var result = condition.hashCode() + result = 31 * result + options.hashCode() + return result + } + override fun evaluate( context: EvaluationContext, - inputs: Flow - ): Flow { - val conditionFunction = condition.evaluateContext(context) + inputs: List + ): List { + val conditionFunction = condition.evaluateFunction(context) return inputs.filter { input -> conditionFunction(input).value?.booleanValue ?: false } } } @@ -497,6 +609,24 @@ internal constructor( distanceMeasure.proto ) + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is FindNearestStage) return false + if (property != other.property) return false + if (vector != other.vector) return false + if (distanceMeasure != other.distanceMeasure) return false + if (options != other.options) return false + return true + } + + override fun hashCode(): Int { + var result = property.hashCode() + result = 31 * result + vector.hashCode() + result = 31 * result + distanceMeasure.hashCode() + result = 31 * result + options.hashCode() + return result + } + /** * Specifies the upper bound of documents to return. * @@ -525,29 +655,38 @@ internal constructor( } internal class LimitStage -internal constructor(private val limit: Int, options: InternalOptions = InternalOptions.EMPTY) : - Stage("limit", options) { +internal constructor(val limit: Int, options: InternalOptions = InternalOptions.EMPTY) : + Stage("limit", options), Canonicalizable { + override fun canonicalId(): String { + return "${name}(${limit})" + } + override fun self(options: InternalOptions) = LimitStage(limit, options) override fun evaluate( context: EvaluationContext, - inputs: Flow - ): Flow = + inputs: List + ): List = when { limit > 0 -> inputs.take(limit) - limit < 0 -> - flow { - val limitLast = -limit - val buffer = ArrayDeque(limitLast) - inputs.collect { doc -> - if (buffer.size == limitLast) buffer.removeFirst() - buffer.add(doc) - } - buffer.forEach { emit(it) } - } - else -> emptyFlow() + limit < 0 -> inputs.takeLast(limit) + else -> listOf() } override fun args(userDataReader: UserDataReader): Sequence = sequenceOf(encodeValue(limit)) + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is LimitStage) return false + if (limit != other.limit) return false + if (options != other.options) return false + return true + } + + override fun hashCode(): Int { + var result = limit + result = 31 * result + options.hashCode() + return result + } } internal class OffsetStage @@ -556,23 +695,20 @@ internal constructor(private val offset: Int, options: InternalOptions = Interna override fun self(options: InternalOptions) = OffsetStage(offset, options) override fun args(userDataReader: UserDataReader): Sequence = sequenceOf(encodeValue(offset)) - override fun evaluate( - context: EvaluationContext, - inputs: Flow - ): Flow = - when { - offset > 0 -> inputs.drop(offset) - offset < 0 -> - flow { - val offsetLast = -offset - val buffer = ArrayDeque(offsetLast) - inputs.collect { doc -> - if (buffer.size == offsetLast) emit(buffer.removeFirst()) - buffer.add(doc) - } - } - else -> inputs - } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is OffsetStage) return false + if (offset != other.offset) return false + if (options != other.options) return false + return true + } + + override fun hashCode(): Int { + var result = offset + result = 31 * result + options.hashCode() + return result + } } internal class SelectStage @@ -593,13 +729,61 @@ private constructor(internal val fields: Array, options: Interna override fun self(options: InternalOptions) = SelectStage(fields, options) override fun args(userDataReader: UserDataReader): Sequence = sequenceOf(encodeValue(fields.associate { it.alias to it.toProto(userDataReader) })) + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is SelectStage) return false + if (!fields.contentEquals(other.fields)) return false + if (options != other.options) return false + return true + } + + override fun hashCode(): Int { + var result = fields.contentHashCode() + result = 31 * result + options.hashCode() + return result + } } +private fun comparatorFromOrderings( + context: EvaluationContext, + orderings: Array +): Comparator = + java.util.Comparator { d1, d2 -> + for (ordering in orderings) { + val expr = ordering.expr + // Evaluate expression for both documents using expr->Evaluate + // (assuming this method exists) Pass const references to documents. + val leftValue = expr.evaluateFunction(context)(d1 as MutableDocument) + val rightValue = expr.evaluateFunction(context)(d2 as MutableDocument) + + // Compare results, using MinValue for error + val comparison = + Values.compare( + if (leftValue.isError || leftValue.isUnset) Values.NULL_VALUE else leftValue.value!!, + if (rightValue.isError || rightValue.isUnset) Values.NULL_VALUE else rightValue.value!!, + ) + + if (comparison != 0) { + return@Comparator if (ordering.dir == Ordering.Direction.ASCENDING) { + comparison + } else { + -comparison + } + } + } + return@Comparator 0 + } + internal class SortStage internal constructor( - private val orders: Array, + val orders: Array, options: InternalOptions = InternalOptions.EMPTY -) : Stage("sort", options) { +) : Stage("sort", options), Canonicalizable { + override fun canonicalId(): String { + return "${name}(${orders.joinToString(",") { it.canonicalId() }})" + } + companion object { internal val BY_DOCUMENT_ID = SortStage(arrayOf(Field.DOCUMENT_ID.ascending())) } @@ -608,43 +792,32 @@ internal constructor( override fun args(userDataReader: UserDataReader): Sequence = orders.asSequence().map { it.toProto(userDataReader) } + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is SortStage) return false + if (!orders.contentEquals(other.orders)) return false + if (options != other.options) return false + return true + } + + override fun hashCode(): Int { + var result = orders.contentHashCode() + result = 31 * result + options.hashCode() + return result + } + override fun evaluate( context: EvaluationContext, - inputs: Flow - ): Flow { + inputs: List + ): List { val evaluates: Array = - orders.map { it.expr.evaluateContext(context) }.toTypedArray() - val directions: Array = orders.map { it.dir }.toTypedArray() - return flow { - inputs - // For each document, lazily evaluate order expression values. - .map { doc -> - val orderValues = - evaluates - .map { lazy(LazyThreadSafetyMode.PUBLICATION) { it(doc).value ?: Values.MIN_VALUE } } - .toTypedArray>() - Pair(doc, orderValues) - } - .toList() - .sortedWith( - Comparator { px, py -> - val x = px.second - val y = py.second - directions.forEachIndexed { i, dir -> - val r = - when (dir) { - Ordering.Direction.ASCENDING -> Values.compare(x[i].value, y[i].value) - Ordering.Direction.DESCENDING -> Values.compare(y[i].value, x[i].value) - } - if (r != 0) return@Comparator r - } - 0 - } - ) - .forEach { p -> emit(p.first) } - } + orders.map { it.expr.evaluateFunction(context) }.toTypedArray() + return inputs.sortedWith(comparator(context)) } + internal fun comparator(context: EvaluationContext): Comparator = + comparatorFromOrderings(context, orders) + internal fun withStableOrdering(): SortStage { val position = orders.indexOfFirst { (it.expr as? Field)?.alias == KEY_FIELD_NAME } return if (position < 0) { @@ -664,6 +837,20 @@ internal constructor( override fun self(options: InternalOptions) = DistinctStage(groups, options) override fun args(userDataReader: UserDataReader): Sequence = sequenceOf(encodeValue(groups.associate { it.alias to it.toProto(userDataReader) })) + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is DistinctStage) return false + if (!groups.contentEquals(other.groups)) return false + if (options != other.options) return false + return true + } + + override fun hashCode(): Int { + var result = groups.contentHashCode() + result = 31 * result + options.hashCode() + return result + } } internal class RemoveFieldsStage @@ -682,6 +869,20 @@ internal constructor( override fun self(options: InternalOptions) = RemoveFieldsStage(fields, options) override fun args(userDataReader: UserDataReader): Sequence = fields.asSequence().map(Field::toProto) + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is RemoveFieldsStage) return false + if (!fields.contentEquals(other.fields)) return false + if (options != other.options) return false + return true + } + + override fun hashCode(): Int { + var result = fields.contentHashCode() + result = 31 * result + options.hashCode() + return result + } } internal class ReplaceStage @@ -701,6 +902,22 @@ internal constructor( override fun self(options: InternalOptions) = ReplaceStage(mapValue, mode, options) override fun args(userDataReader: UserDataReader): Sequence = sequenceOf(mapValue.toProto(userDataReader), mode.proto) + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is ReplaceStage) return false + if (mapValue != other.mapValue) return false + if (mode != other.mode) return false + if (options != other.options) return false + return true + } + + override fun hashCode(): Int { + var result = mapValue.hashCode() + result = 31 * result + mode.hashCode() + result = 31 * result + options.hashCode() + return result + } } /** @@ -753,6 +970,22 @@ private constructor( } override fun args(userDataReader: UserDataReader): Sequence = sequenceOf(encodeValue(size), mode.proto) + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is SampleStage) return false + if (size != other.size) return false + if (mode != other.mode) return false + if (options != other.options) return false + return true + } + + override fun hashCode(): Int { + var result = size.hashCode() + result = 31 * result + mode.hashCode() + result = 31 * result + options.hashCode() + return result + } } internal class UnionStage @@ -763,6 +996,20 @@ internal constructor( override fun self(options: InternalOptions) = UnionStage(other, options) override fun args(userDataReader: UserDataReader): Sequence = sequenceOf(Value.newBuilder().setPipelineValue(other.toPipelineProto()).build()) + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is UnionStage) return false + if (this.other != other.other) return false + if (options != other.options) return false + return true + } + + override fun hashCode(): Int { + var result = other.hashCode() + result = 31 * result + options.hashCode() + return result + } } /** @@ -811,6 +1058,20 @@ internal constructor( override fun args(userDataReader: UserDataReader): Sequence = sequenceOf(encodeValue(selectable.alias), selectable.toProto(userDataReader)) + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is UnnestStage) return false + if (selectable != other.selectable) return false + if (options != other.options) return false + return true + } + + override fun hashCode(): Int { + var result = selectable.hashCode() + result = 31 * result + options.hashCode() + return result + } + /** * Adds index field to emitted documents * diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/CanonifyEqTest.kt b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/CanonifyEqTest.kt new file mode 100644 index 00000000000..a3149651dde --- /dev/null +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/CanonifyEqTest.kt @@ -0,0 +1,92 @@ +// Copyright 2025 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.pipeline + +import com.google.common.truth.Truth.assertThat +import com.google.firebase.firestore.RealtimePipelineSource +import com.google.firebase.firestore.TestUtil +import com.google.firebase.firestore.pipeline.Expr.Companion.field +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +internal class CanonifyEqTest { + + private val db = TestUtil.firestore() + + @Test + fun `canonify simple where`() { + val pipeline = RealtimePipelineSource(db).collection("test").where(field("foo").eq(42L)) + assertThat(pipeline.canonicalId()) + .isEqualTo("collection(test)|where(fn(eq[fld(foo),cst(42)]))|sort(fld(__name__)asc)") + } + + @Test + fun `canonify multiple stages`() { + val pipeline = + RealtimePipelineSource(db) + .collection("test") + .where(field("foo").eq("42L")) + .limit(10) + .sort(field("bar").descending()) + assertThat(pipeline.canonicalId()) + .isEqualTo( + "collection(test)|where(fn(eq[fld(foo),cst(42L)]))|sort(fld(__name__)asc)|limit(10)|sort(fld(bar)desc,fld(__name__)asc)" + ) + } + + @Test + fun `canonify collection group source`() { + val pipeline = RealtimePipelineSource(db).collectionGroup("cities") + assertThat(pipeline.canonicalId()).isEqualTo("collection_group(cities)|sort(fld(__name__)asc)") + } + + @Test + fun `eq returns true for identical pipelines`() { + val p1 = RealtimePipelineSource(db).collection("test").where(field("foo").eq(42L)) + val p2 = RealtimePipelineSource(db).collection("test").where(field("foo").eq(42L)) + assertThat(p1.equals(p2)).isTrue() + } + + @Test + fun `eq returns false for different stages`() { + val p1 = RealtimePipelineSource(db).collection("test").where(field("foo").eq(42L)) + val p2 = RealtimePipelineSource(db).collection("test").limit(10) + assertThat(p1.equals(p2)).isFalse() + } + + @Test + fun `eq returns false for different params in stage`() { + val p1 = RealtimePipelineSource(db).collection("test").where(field("foo").eq(42L)) + val p2 = RealtimePipelineSource(db).collection("test").where(field("bar").eq(42L)) + assertThat(p1.equals(p2)).isFalse() + } + + @Test + fun `eq returns false for different stage order`() { + val p1 = RealtimePipelineSource(db).collection("test").where(field("foo").eq(42L)).limit(10) + val p2 = RealtimePipelineSource(db).collection("test").limit(10).where(field("foo").eq(42L)) + assertThat(p1.equals(p2)).isFalse() + } + + @Test + fun `eq returns false for same canonicalId but different types`() { + val p1 = RealtimePipelineSource(db).collection("test").where(field("foo").eq("42")) + val p2 = RealtimePipelineSource(db).collection("test").where(field("foo").eq(42)) + assertThat(p1.canonicalId()).isEqualTo(p2.canonicalId()) + assertThat(p1.equals(p2)).isFalse() + } +} diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/CollectionGroupTests.kt b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/CollectionGroupTests.kt index e41c9e0dfcd..06a84777375 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/CollectionGroupTests.kt +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/CollectionGroupTests.kt @@ -27,7 +27,6 @@ import com.google.firebase.firestore.pipeline.Expr.Companion.gt import com.google.firebase.firestore.pipeline.Expr.Companion.neq import com.google.firebase.firestore.runPipeline import com.google.firebase.firestore.testutil.TestUtilKtx.doc -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.toList import kotlinx.coroutines.runBlocking import org.junit.Test @@ -43,7 +42,7 @@ internal class CollectionGroupTests { fun `returns no result from empty db`(): Unit = runBlocking { val pipeline = RealtimePipelineSource(db).collectionGroup("users") val documents = emptyList() - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -52,7 +51,7 @@ internal class CollectionGroupTests { val pipeline = RealtimePipelineSource(db).collectionGroup("users") val doc1 = doc("users/bob", 1000, mapOf("score" to 90L, "rank" to 1L)) val documents = listOf(doc1) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1) } @@ -63,7 +62,7 @@ internal class CollectionGroupTests { val doc2 = doc("users/alice", 1000, mapOf("score" to 50L, "rank" to 3L)) val doc3 = doc("users/charlie", 1000, mapOf("score" to 97L, "rank" to 2L)) val documents = listOf(doc1, doc2, doc3) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() // Expected order by key: alice, bob, charlie assertThat(result).containsExactly(doc2, doc1, doc3).inOrder() } @@ -80,7 +79,7 @@ internal class CollectionGroupTests { val documents = listOf(doc1, doc2, doc3, doc4, doc5, doc6) val expectedDocs = listOf(doc3, doc1, doc5) // Expected order by key: alice, bob, charlie (from 'users' only) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(expectedDocs).inOrder() } @@ -99,7 +98,7 @@ internal class CollectionGroupTests { val documents = listOf(doc1, doc2, doc3, doc4, doc5, doc6, doc7) val expectedDocs = listOf(doc1, doc2, doc3, doc4, doc5, doc6) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(expectedDocs).inOrder() } @@ -128,7 +127,7 @@ internal class CollectionGroupTests { // users/bob/games/5 // users/charlie/games/4 val expectedDocs = listOf(doc2, doc6, doc1, doc3, doc5, doc4) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(expectedDocs).inOrder() } @@ -151,7 +150,7 @@ internal class CollectionGroupTests { val documents = listOf(doc1, doc2, doc3, doc4, doc5, doc6, doc7) val expectedDocs = listOf(doc2, doc6, doc1, doc3, doc5, doc4) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(expectedDocs).inOrder() } @@ -176,7 +175,7 @@ internal class CollectionGroupTests { val documents = listOf(doc1, doc2, doc3, doc4, doc5) // Expected: bob(profiles), bob(users), charlie(users), diane(users) - sorted by key val expectedDocs = listOf(doc5, doc1, doc3, doc4) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(expectedDocs).inOrder() } @@ -193,7 +192,7 @@ internal class CollectionGroupTests { val documents = listOf(doc1, doc2, doc3, doc4) // Expected: bob(profiles), bob(users), charlie(users) - sorted by key val expectedDocs = listOf(doc4, doc1, doc3) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(expectedDocs).inOrder() } @@ -210,7 +209,7 @@ internal class CollectionGroupTests { val documents = listOf(doc1, doc2, doc3, doc4) // Expected: bob(profiles), bob(users), charlie(users) - sorted by key val expectedDocs = listOf(doc4, doc1, doc3) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(expectedDocs).inOrder() } @@ -240,7 +239,7 @@ internal class CollectionGroupTests { val documents = listOf(doc1, doc2, doc3, doc4) // Expected: bob(profiles), bob(users), charlie(users) - sorted by key val expectedDocs = listOf(doc4, doc1, doc3) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(expectedDocs).inOrder() } @@ -258,7 +257,7 @@ internal class CollectionGroupTests { // Expected: charlie(97), bob(profiles, 90), bob(users, 90), alice(50) // Tie is broken by document key (ascending), where "profiles/admin/users/bob" (doc4) // comes before "users/bob" (doc1). - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3, doc4, doc1, doc2).inOrder() } @@ -277,7 +276,7 @@ internal class CollectionGroupTests { // So, charlie (doc3) with missing 'score' comes after alice (doc2) with score 50. // Order for scores: 90, 90, 50, missing. val expectedDocs = listOf(doc4, doc1, doc2, doc3) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() // Tie for 'score' is broken by document key (ascending), where "profiles/admin/users/bob" // (doc4) // comes before "users/bob" (doc1). Documents with missing 'score' (doc3) sort after @@ -304,7 +303,7 @@ internal class CollectionGroupTests { // users/bob (doc1) // users/charlie (doc3) val expectedDocs = listOf(doc4, doc2, doc1, doc3) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(expectedDocs).inOrder() } @@ -326,7 +325,7 @@ internal class CollectionGroupTests { // profiles/admin/users/bob (doc4) // users/alice (doc2) val expectedDocs = listOf(doc4, doc2) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(expectedDocs).inOrder() } } diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/CollectionTests.kt b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/CollectionTests.kt index a542d860b4e..12d133335a1 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/CollectionTests.kt +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/CollectionTests.kt @@ -25,7 +25,6 @@ import com.google.firebase.firestore.pipeline.Expr.Companion.eqAny import com.google.firebase.firestore.pipeline.Expr.Companion.field import com.google.firebase.firestore.runPipeline import com.google.firebase.firestore.testutil.TestUtilKtx.doc -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.toList import kotlinx.coroutines.runBlocking import org.junit.Test @@ -41,7 +40,7 @@ internal class CollectionTests { fun `empty database returns no results`(): Unit = runBlocking { val pipeline = RealtimePipelineSource(db).collection("/users") val inputDocs = emptyList() - val result = runPipeline(pipeline, flowOf(*inputDocs.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*inputDocs.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -51,7 +50,7 @@ internal class CollectionTests { val doc1 = doc("users/alice/games/doc1", 1000, mapOf("title" to "minecraft")) val doc2 = doc("users/charlie/games/doc1", 1000, mapOf("title" to "halo")) val inputDocs = listOf(doc1, doc2) - val result = runPipeline(pipeline, flowOf(*inputDocs.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*inputDocs.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -61,7 +60,7 @@ internal class CollectionTests { val doc1 = doc("users/bob/addresses/doc1", 1000, mapOf("city" to "New York")) val doc2 = doc("users/bob/inventories/doc1", 1000, mapOf("item_id" to 42L)) val inputDocs = listOf(doc1, doc2) - val result = runPipeline(pipeline, flowOf(*inputDocs.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*inputDocs.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -71,7 +70,7 @@ internal class CollectionTests { val doc1 = doc("games/42", 1000, mapOf("title" to "minecraft")) val doc2 = doc("users/bob", 1000, mapOf("score" to 90L, "rank" to 1L)) val inputDocs = listOf(doc1, doc2) - val result = runPipeline(pipeline, flowOf(*inputDocs.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*inputDocs.toTypedArray())).toList() assertThat(result).containsExactly(doc2) } @@ -82,7 +81,7 @@ internal class CollectionTests { val doc2 = doc("users/bob/games/doc1", 1000, mapOf("title" to "minecraft")) val doc3 = doc("users/alice/games/doc1", 1000, mapOf("title" to "halo")) val inputDocs = listOf(doc1, doc2, doc3) - val result = runPipeline(pipeline, flowOf(*inputDocs.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*inputDocs.toTypedArray())).toList() assertThat(result).containsExactly(doc2) } @@ -95,7 +94,7 @@ internal class CollectionTests { val doc4 = doc("games/doc1", 1000, mapOf("title" to "minecraft")) val inputDocs = listOf(doc1, doc2, doc3, doc4) // Firestore backend sorts by document key as a tie-breaker. - val result = runPipeline(pipeline, flowOf(*inputDocs.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*inputDocs.toTypedArray())).toList() assertThat(result).containsExactly(doc2, doc1, doc3) } @@ -109,7 +108,7 @@ internal class CollectionTests { val doc3 = doc("users/charlie", 1000, mapOf("score" to 97L, "rank" to 2L)) val doc4 = doc("games/doc1", 1000, mapOf("title" to "minecraft")) val inputDocs = listOf(doc1, doc2, doc3, doc4) - val result = runPipeline(pipeline, flowOf(*inputDocs.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*inputDocs.toTypedArray())).toList() assertThat(result).containsExactly(doc2, doc1, doc3) } @@ -120,7 +119,7 @@ internal class CollectionTests { val doc2 = doc("users/bob/games/minecraft", 1000, mapOf("title" to "minecraft")) val doc3 = doc("users/bob/games/minecraft/players/player1", 1000, mapOf("location" to "sf")) val inputDocs = listOf(doc1, doc2, doc3) - val result = runPipeline(pipeline, flowOf(*inputDocs.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*inputDocs.toTypedArray())).toList() assertThat(result).containsExactly(doc1) } @@ -134,7 +133,7 @@ internal class CollectionTests { val doc5 = doc("users/charlie", 1000, mapOf("score" to 97L, "rank" to 2L)) val doc6 = doc("users-other/charlie", 1000, mapOf("score" to 97L, "rank" to 2L)) val inputDocs = listOf(doc1, doc2, doc3, doc4, doc5, doc6) - val result = runPipeline(pipeline, flowOf(*inputDocs.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*inputDocs.toTypedArray())).toList() assertThat(result).containsExactly(doc3, doc1, doc5) } @@ -149,7 +148,7 @@ internal class CollectionTests { val doc6 = doc("users/alice/games/doc1", 1000, mapOf("score" to 30L)) val inputDocs = listOf(doc1, doc2, doc3, doc4, doc5, doc6) // Expected order based on key for user bob's games - val result = runPipeline(pipeline, flowOf(*inputDocs.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*inputDocs.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc3, doc5).inOrder() } @@ -167,7 +166,7 @@ internal class CollectionTests { val doc3 = doc("users/charlie", 1000, mapOf("score" to 97L)) val doc4 = doc("users/diane", 1000, mapOf("score" to 97L)) val inputDocs = listOf(doc1, doc2, doc3, doc4) - val result = runPipeline(pipeline, flowOf(*inputDocs.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*inputDocs.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc3, doc4) } @@ -179,7 +178,7 @@ internal class CollectionTests { val doc2 = doc("users/alice", 1000, mapOf("score" to 50L)) val doc3 = doc("users/charlie", 1000, mapOf("score" to 97L)) val inputDocs = listOf(doc1, doc2, doc3) - val result = runPipeline(pipeline, flowOf(*inputDocs.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*inputDocs.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc3) } @@ -191,7 +190,7 @@ internal class CollectionTests { val doc2 = doc("users/alice", 1000, mapOf("score" to 50L)) val doc3 = doc("users/charlie", 1000, mapOf("score" to 97L)) val inputDocs = listOf(doc1, doc2, doc3) - val result = runPipeline(pipeline, flowOf(*inputDocs.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*inputDocs.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc3) } @@ -212,7 +211,7 @@ internal class CollectionTests { mapOf("score" to 97L, "rounds" to listOf("round2", "round3", "round4")) ) val inputDocs = listOf(doc1, doc2, doc3) - val result = runPipeline(pipeline, flowOf(*inputDocs.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*inputDocs.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc3) } @@ -226,7 +225,7 @@ internal class CollectionTests { val doc2 = doc("users/alice", 1000, mapOf("score" to 50L)) val doc3 = doc("users/charlie", 1000, mapOf("score" to 97L)) val inputDocs = listOf(doc1, doc2, doc3) - val result = runPipeline(pipeline, flowOf(*inputDocs.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*inputDocs.toTypedArray())).toList() assertThat(result).containsExactly(doc3, doc1, doc2).inOrder() } @@ -241,7 +240,7 @@ internal class CollectionTests { val doc2 = doc("users/alice", 1000, mapOf("score" to 50L)) val doc3 = doc("users/charlie", 1000, mapOf("score" to 97L)) val inputDocs = listOf(doc1, doc2, doc3) - val result = runPipeline(pipeline, flowOf(*inputDocs.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*inputDocs.toTypedArray())).toList() assertThat(result).containsExactly(doc2, doc1, doc3).inOrder() } @@ -259,7 +258,7 @@ internal class CollectionTests { val doc2 = doc("users/alice", 1000, mapOf("score" to 50L)) val doc3 = doc("users/charlie", 1000, mapOf("score" to 97L)) val inputDocs = listOf(doc1, doc2, doc3) - val result = runPipeline(pipeline, flowOf(*inputDocs.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*inputDocs.toTypedArray())).toList() assertThat(result).containsExactly(doc2, doc1).inOrder() } @@ -278,7 +277,7 @@ internal class CollectionTests { val doc4 = doc("users/bob/inventories/a", 1000, mapOf("type" to "sword")) val doc5 = doc("users/alice/games/c", 1000, mapOf("title" to "skyrim")) val inputDocs = listOf(doc1, doc2, doc3, doc4, doc5) - val result = runPipeline(pipeline, flowOf(*inputDocs.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*inputDocs.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc2, doc3).inOrder() } @@ -295,7 +294,7 @@ internal class CollectionTests { val doc4 = doc("users/bob/inventories/a", 1000, mapOf("type" to "sword")) val doc5 = doc("users/alice/games/c", 1000, mapOf("title" to "skyrim")) val inputDocs = listOf(doc1, doc2, doc3, doc4, doc5) - val result = runPipeline(pipeline, flowOf(*inputDocs.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*inputDocs.toTypedArray())).toList() assertThat(result).containsExactly(doc3, doc2, doc1).inOrder() } } diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/ComplexTests.kt b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/ComplexTests.kt index f645df42b37..5ced6607be3 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/ComplexTests.kt +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/ComplexTests.kt @@ -27,7 +27,6 @@ import com.google.firebase.firestore.pipeline.Expr.Companion.field import com.google.firebase.firestore.pipeline.Expr.Companion.or import com.google.firebase.firestore.runPipeline import com.google.firebase.firestore.testutil.TestUtilKtx.doc -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.toList import kotlinx.coroutines.runBlocking import org.junit.Test @@ -69,7 +68,7 @@ internal class ComplexTests { pipeline = pipeline.where(field("field_$i").gt(0L)) } - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(documents) } @@ -87,7 +86,7 @@ internal class ComplexTests { val pipeline = RealtimePipelineSource(db).collection(collectionId).where(field("field_1").eqAny(values)) - val result = runPipeline(pipeline, flowOf(*allDocuments.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*allDocuments.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(documentsSource) } @@ -109,7 +108,7 @@ internal class ComplexTests { .collection(collectionId) .where(and(conditions.first(), *conditions.drop(1).toTypedArray())) - val result = runPipeline(pipeline, flowOf(*allDocuments.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*allDocuments.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(documentsSource) } @@ -127,7 +126,7 @@ internal class ComplexTests { val pipeline = RealtimePipelineSource(db).collection(collectionId).where(field("field_1").notEqAny(values)) - val result = runPipeline(pipeline, flowOf(*allDocuments.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*allDocuments.toTypedArray())).toList() assertThat(result).containsExactly(matchingDoc) } @@ -169,7 +168,7 @@ internal class ComplexTests { .collection(collectionId) .where(or(conditions.first(), *conditions.drop(1).toTypedArray())) - val result = runPipeline(pipeline, flowOf(*allDocuments.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*allDocuments.toTypedArray())).toList() // matchingDoc: field_1=3001 (not in values) -> true. Other fields are in values. So OR is true. // documentsSource: All fields have values from 1 to 1000. All are IN `values`. So notEqAny is // false for all fields. OR is false. @@ -195,7 +194,7 @@ internal class ComplexTests { .collection(collectionId) .where(field("field_1").arrayContainsAny(valuesToSearch)) - val result = runPipeline(pipeline, flowOf(*allDocuments.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*allDocuments.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(documentsSource) } @@ -227,7 +226,7 @@ internal class ComplexTests { .collection(collectionId) .where(or(conditions.first(), *conditions.drop(1).toTypedArray())) - val result = runPipeline(pipeline, flowOf(*allDocuments.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*allDocuments.toTypedArray())).toList() // documentsSource: each field_i has a list like [some_value_between_1_and_1000]. // Since valuesToSearch is [1..3000], arrayContainsAny will be true for each field. So OR is // true. @@ -256,7 +255,7 @@ internal class ComplexTests { // Since all field values are the same, sort order is determined by document ID. val expectedDocs = documents.sortedBy { it.key } - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(expectedDocs).inOrder() } @@ -277,7 +276,7 @@ internal class ComplexTests { val pipeline = RealtimePipelineSource(db).collection(collectionId).where(addExpr.gt(0L)) // 31 > 0L is true - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(documents) } @@ -305,7 +304,7 @@ internal class ComplexTests { .collection(collectionId) .where(or(orConditions.first(), *orConditions.drop(1).toTypedArray())) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() // Every document will have at least one field_i <= maxValueInDb (actually all fields are) assertThat(result).containsExactlyElementsIn(documents) } @@ -333,7 +332,7 @@ internal class ComplexTests { and(andConditions2.first(), *andConditions2.drop(1).toTypedArray()) ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() // All seeded values are > 0 and < Long.MAX_VALUE, so all documents match. assertThat(result).containsExactlyElementsIn(documents) } diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/DisjunctiveTests.kt b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/DisjunctiveTests.kt index 6385485f26b..afd433399f2 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/DisjunctiveTests.kt +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/DisjunctiveTests.kt @@ -27,7 +27,6 @@ import com.google.firebase.firestore.pipeline.Expr.Companion.not import com.google.firebase.firestore.pipeline.Expr.Companion.or import com.google.firebase.firestore.runPipeline import com.google.firebase.firestore.testutil.TestUtilKtx.doc -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.toList import kotlinx.coroutines.runBlocking import org.junit.Test @@ -64,7 +63,7 @@ internal class DisjunctiveTests { ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc2, doc3, doc4, doc5)) } @@ -96,7 +95,7 @@ internal class DisjunctiveTests { ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc2, doc4, doc5)) } @@ -126,7 +125,7 @@ internal class DisjunctiveTests { ) .where(field("age").eqAny(array(constant(10.0), constant(25.0)))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc2, doc4, doc5)) } @@ -149,7 +148,7 @@ internal class DisjunctiveTests { ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc2, doc4, doc5)) } @@ -170,7 +169,7 @@ internal class DisjunctiveTests { .eqAny(array(constant("alice"), constant("bob"), constant("diane"), constant("eric"))) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc4)) } @@ -192,7 +191,7 @@ internal class DisjunctiveTests { ) .sort(field("age").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc4, doc5, doc2, doc1).inOrder() } @@ -214,7 +213,7 @@ internal class DisjunctiveTests { ) .sort(field("name").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc2, doc4, doc5).inOrder() } @@ -247,7 +246,7 @@ internal class DisjunctiveTests { ) .sort(field("name").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc4, doc5).inOrder() } @@ -270,7 +269,7 @@ internal class DisjunctiveTests { ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc5) } @@ -291,7 +290,7 @@ internal class DisjunctiveTests { ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -318,7 +317,7 @@ internal class DisjunctiveTests { ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc2)) } @@ -345,7 +344,7 @@ internal class DisjunctiveTests { ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc2, doc3, doc4)) } @@ -373,7 +372,7 @@ internal class DisjunctiveTests { ) .sort(field("age").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc2, doc1).inOrder() } @@ -399,7 +398,7 @@ internal class DisjunctiveTests { ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc2, doc4)) } @@ -423,7 +422,7 @@ internal class DisjunctiveTests { ) .sort(field("name").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc2, doc3, doc4).inOrder() } @@ -440,7 +439,7 @@ internal class DisjunctiveTests { .where(field("age").eqAny(array(constant(10.0)))) .sort(field("age").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() // Order of doc2 and doc3 is by key after sorting by constant age assertThat(result).containsExactly(doc2, doc3).inOrder() } @@ -474,7 +473,7 @@ internal class DisjunctiveTests { ) .sort(field("name").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc4, doc5).inOrder() } @@ -507,7 +506,7 @@ internal class DisjunctiveTests { ) .sort(field("age").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc4, doc5).inOrder() // Sorted by key after age } @@ -530,7 +529,7 @@ internal class DisjunctiveTests { ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc2, doc3)) } @@ -557,7 +556,7 @@ internal class DisjunctiveTests { ) .sort(field("age").ascending()) // C++ test sorts by age (inequality field) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc2, doc1, doc3).inOrder() } @@ -573,7 +572,7 @@ internal class DisjunctiveTests { .collection("/users") .where(field("name").eqAny(array(Expr.nullValue(), constant("alice")))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1) // Nulls are not matched by IN } @@ -590,7 +589,7 @@ internal class DisjunctiveTests { .collection("/users") .where(Expr.arrayContains(field("field"), Expr.nullValue())) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() // arrayContains does not match null } @@ -607,7 +606,7 @@ internal class DisjunctiveTests { .collection("/users") .where(field("field").arrayContainsAny(array(Expr.nullValue(), constant("foo")))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3) // arrayContainsAny does not match null } @@ -623,7 +622,7 @@ internal class DisjunctiveTests { .collection("/users") .where(field("age").eqAny(array(Expr.nullValue()))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() // Nulls are not matched by IN } @@ -641,7 +640,7 @@ internal class DisjunctiveTests { .collection("/users") .where(field("groups").arrayContainsAny(array(constant(1L), constant(5L)))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc2, doc4, doc5)) } @@ -693,7 +692,7 @@ internal class DisjunctiveTests { ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc4)) } @@ -720,7 +719,7 @@ internal class DisjunctiveTests { field("groups").lt(array(constant(3L), constant(4L), constant(5L))) ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc2, doc4)) } @@ -742,7 +741,7 @@ internal class DisjunctiveTests { field("name").eqAny(array(constant("alice"), constant("bob"))) ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc2)) } @@ -759,7 +758,7 @@ internal class DisjunctiveTests { .collection("/users") .where(or(field("name").eq(constant("bob")), field("age").eq(constant(10.0)))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc2, doc4)) } @@ -782,7 +781,7 @@ internal class DisjunctiveTests { field("age").eq(constant(100.0)) ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc2, doc3, doc4)) } @@ -800,7 +799,7 @@ internal class DisjunctiveTests { .where(or(field("name").eq(constant("bob")), field("age").eq(constant(10.0)))) .where(or(field("name").eq(constant("diane")), field("age").eq(constant(100.0)))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc4) // (name=bob OR age=10) AND (name=diane OR age=100) } @@ -821,7 +820,7 @@ internal class DisjunctiveTests { and(field("name").eq(constant("diane")), field("age").eq(constant(10.0))) ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc2, doc4)) } @@ -842,7 +841,7 @@ internal class DisjunctiveTests { field("age").lt(constant(80.0)) ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc2, doc4)) } @@ -863,7 +862,7 @@ internal class DisjunctiveTests { or(field("name").eq(constant("diane")), field("age").eq(constant(100.0))) ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc4) } @@ -884,7 +883,7 @@ internal class DisjunctiveTests { or(field("name").eq(constant("diane")), field("age").eq(constant(100.0))) ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc2, doc3, doc4)) } @@ -905,7 +904,7 @@ internal class DisjunctiveTests { and(field("age").eq(constant(10.0)), field("age").gt(constant(20.0))) ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc2) } @@ -923,7 +922,7 @@ internal class DisjunctiveTests { .where(or(field("name").eq(constant("diane")), field("age").gt(constant(20.0)))) .sort(field("age").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc4, doc2, doc1, doc3).inOrder() } @@ -941,7 +940,7 @@ internal class DisjunctiveTests { .where(or(field("age").lt(constant(20.0)), field("age").gt(constant(50.0)))) .sort(field("age").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc4, doc1, doc3).inOrder() } @@ -959,7 +958,7 @@ internal class DisjunctiveTests { .where(or(field("age").lt(constant(20.0)), field("age").gt(constant(50.0)))) .sort(field("name").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc3, doc4).inOrder() } @@ -983,7 +982,7 @@ internal class DisjunctiveTests { .where(or(field("age").lt(constant(80.0)), field("height").gt(constant(160.0)))) .sort(field("age").ascending(), field("height").descending(), field("name").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc4, doc2, doc1, doc5).inOrder() } @@ -1001,7 +1000,7 @@ internal class DisjunctiveTests { .where(or(field("name").eq(constant("diane")), field("age").gt(constant(20.0)))) .sort(field("age").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3, doc4, doc2, doc1).inOrder() } @@ -1020,7 +1019,7 @@ internal class DisjunctiveTests { .sort(field("age").ascending()) .limit(2) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc4, doc2).inOrder() } @@ -1039,7 +1038,7 @@ internal class DisjunctiveTests { .collection("/users") .where(or(field("a").eq(constant(1L)), isNull(field("a")))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() // C++ test expects 1.0 to match 1L in this context. // isNull matches explicit nulls. assertThat(result).containsExactlyElementsIn(listOf(doc1, doc2, doc3, doc4)) @@ -1060,7 +1059,7 @@ internal class DisjunctiveTests { .collection("/users") .where(or(field("b").eq(constant(1L)), isNull(field("a")))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc3, doc4)) } @@ -1079,7 +1078,7 @@ internal class DisjunctiveTests { .collection("/users") .where(or(field("a").gt(constant(1L)), not(isNull(field("a"))))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() // a > 1L (none) OR a IS NOT NULL (doc1, doc2, doc3, doc5) assertThat(result).containsExactlyElementsIn(listOf(doc1, doc2, doc3, doc5)) } @@ -1099,7 +1098,7 @@ internal class DisjunctiveTests { .collection("/users") .where(or(field("b").eq(constant(1L)), not(isNull(field("a"))))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() // b == 1L (doc3) OR a IS NOT NULL (doc1, doc2, doc3, doc5) assertThat(result).containsExactlyElementsIn(listOf(doc1, doc2, doc3, doc5)) } @@ -1116,7 +1115,7 @@ internal class DisjunctiveTests { .collection("/users") .where(or(isNull(field("a")), isNan(field("a")))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc2)) } @@ -1135,7 +1134,7 @@ internal class DisjunctiveTests { .collection("/users") .where(or(isNull(field("a")), isNan(field("b")))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc5)) } @@ -1153,7 +1152,7 @@ internal class DisjunctiveTests { .collection("/users") .where(field("name").notEqAny(array(constant("alice"), constant("bob")))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc3, doc4, doc5)) } @@ -1176,7 +1175,7 @@ internal class DisjunctiveTests { ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3) } @@ -1199,7 +1198,7 @@ internal class DisjunctiveTests { ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc3, doc4, doc5)) } @@ -1217,7 +1216,7 @@ internal class DisjunctiveTests { .collectionGroup("users") .where(field("name").notEqAny(array(constant("alice"), constant("bob"), constant("diane")))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3) } @@ -1236,7 +1235,7 @@ internal class DisjunctiveTests { .where(field("name").notEqAny(array(constant("alice"), constant("diane")))) .sort(field("age").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc5, doc2, doc3).inOrder() } @@ -1259,7 +1258,7 @@ internal class DisjunctiveTests { ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc4, doc5)) } @@ -1282,7 +1281,7 @@ internal class DisjunctiveTests { ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc5) } @@ -1306,7 +1305,7 @@ internal class DisjunctiveTests { ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc2) } @@ -1330,7 +1329,7 @@ internal class DisjunctiveTests { ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc3, doc4)) } @@ -1355,7 +1354,7 @@ internal class DisjunctiveTests { ) .sort(field("age").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc2, doc3).inOrder() } @@ -1378,7 +1377,7 @@ internal class DisjunctiveTests { ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc4, doc5)) } @@ -1397,7 +1396,7 @@ internal class DisjunctiveTests { .where(field("name").notEqAny(array(constant("alice"), constant("bob")))) .sort(field("name").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3, doc4, doc5).inOrder() } @@ -1414,7 +1413,7 @@ internal class DisjunctiveTests { .where(field("age").notEqAny(array(constant(100.0)))) .sort(field("age").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc2, doc3).inOrder() // Sorted by key after age } @@ -1438,7 +1437,7 @@ internal class DisjunctiveTests { ) .sort(field("name").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc4, doc5).inOrder() } @@ -1462,7 +1461,7 @@ internal class DisjunctiveTests { ) .sort(field("age").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc4, doc5).inOrder() // Sorted by key after age } @@ -1486,7 +1485,7 @@ internal class DisjunctiveTests { ) .sort(field("age").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc2, doc1).inOrder() } @@ -1510,7 +1509,7 @@ internal class DisjunctiveTests { ) .sort(field("age").ascending()) // C++ test sorts by age (inequality field) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc2, doc3).inOrder() } @@ -1543,7 +1542,7 @@ internal class DisjunctiveTests { ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc2, doc3, doc4, doc5)) } @@ -1561,7 +1560,7 @@ internal class DisjunctiveTests { field("score").eqAny(array(constant(50L), constant(97L), constant(97L), constant(97L))) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc2, doc3)) } @@ -1577,7 +1576,7 @@ internal class DisjunctiveTests { .collection("/users") .where(field("score").notEqAny(array(constant(50L), constant(50L)))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc3)) } @@ -1596,7 +1595,7 @@ internal class DisjunctiveTests { .arrayContainsAny(array(constant(1L), constant(2L), constant(2L), constant(2L))) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1) } @@ -1615,7 +1614,7 @@ internal class DisjunctiveTests { array(constant(1L), constant(2L), constant(2L), constant(2L), constant(3L)) ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() // The C++ test `EXPECT_THAT(RunPipeline(pipeline, documents), ElementsAre(doc1, doc2));` // indicates an ordered check. Aligning with this. assertThat(result).containsExactly(doc1, doc2).inOrder() diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/ErrorHandlingTests.kt b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/ErrorHandlingTests.kt index 660519494cd..bc2cb16ef2e 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/ErrorHandlingTests.kt +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/ErrorHandlingTests.kt @@ -26,7 +26,6 @@ import com.google.firebase.firestore.pipeline.Expr.Companion.or import com.google.firebase.firestore.pipeline.Expr.Companion.xor import com.google.firebase.firestore.runPipeline import com.google.firebase.firestore.testutil.TestUtilKtx.doc -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.toList import kotlinx.coroutines.runBlocking import org.junit.Test @@ -59,7 +58,7 @@ internal class ErrorHandlingTests { ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() // In Firestore, comparisons between different types are generally false. // The OR evaluates to true if *any* of the fields 'a', 'b', or 'c' is the // boolean value `true`. All documents have at least one field that is boolean @@ -89,7 +88,7 @@ internal class ErrorHandlingTests { ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() // AND requires all conditions to be true. Type mismatches evaluate EqExpr to // false. Only doc7 has a=true, b=true, AND c=true. assertThat(result).containsExactly(doc7) @@ -117,7 +116,7 @@ internal class ErrorHandlingTests { ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() // XOR is true if an odd number of inputs are true. // Assuming type mismatches evaluate EqExpr to false: // doc1: F xor T xor F = T @@ -141,7 +140,7 @@ internal class ErrorHandlingTests { // resulting in a condition `field("a") == false`. val pipeline = RealtimePipelineSource(db).collection("k").where(eq(field("a"), constant(false))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() // Only doc1 has a == false. assertThat(result).containsExactly(doc1) } @@ -158,7 +157,7 @@ internal class ErrorHandlingTests { .collection("k") .where(eq(divide(constant("100"), constant("50")), constant(2L))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() // Division of string constants should cause an evaluation error, // leading to no documents matching. assertThat(result).isEmpty() diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/InequalityTests.kt b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/InequalityTests.kt index 84750c981b0..2759129d182 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/InequalityTests.kt +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/InequalityTests.kt @@ -26,7 +26,6 @@ import com.google.firebase.firestore.pipeline.Expr.Companion.not import com.google.firebase.firestore.pipeline.Expr.Companion.or import com.google.firebase.firestore.runPipeline import com.google.firebase.firestore.testutil.TestUtilKtx.doc -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.toList import kotlinx.coroutines.runBlocking import org.junit.Test @@ -47,7 +46,7 @@ internal class InequalityTests { val pipeline = RealtimePipelineSource(db).collection("users").where(field("score").gt(90L)) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3) } @@ -60,7 +59,7 @@ internal class InequalityTests { val pipeline = RealtimePipelineSource(db).collection("users").where(field("score").gte(90L)) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc3)) } @@ -73,7 +72,7 @@ internal class InequalityTests { val pipeline = RealtimePipelineSource(db).collection("users").where(field("score").lt(90L)) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc2) } @@ -86,7 +85,7 @@ internal class InequalityTests { val pipeline = RealtimePipelineSource(db).collection("users").where(field("score").lte(90L)) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc2)) } @@ -99,7 +98,7 @@ internal class InequalityTests { val pipeline = RealtimePipelineSource(db).collection("users").where(field("score").neq(90L)) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc2, doc3)) } @@ -117,7 +116,7 @@ internal class InequalityTests { val pipeline = RealtimePipelineSource(db).collection("users").where(field("score").neq(90L)) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc2, doc3, doc4, doc5, doc6, doc7, doc8)) } @@ -135,7 +134,7 @@ internal class InequalityTests { val pipeline = RealtimePipelineSource(db).collection("users").where(field("score").gt(42L)) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc2) } @@ -156,7 +155,7 @@ internal class InequalityTests { val pipeline = RealtimePipelineSource(db).collection("users").where(not(field("score").gt(90L))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc3, doc4, doc5, doc6, doc7, doc8)) } @@ -173,7 +172,7 @@ internal class InequalityTests { .collection("users") .where(and(field("rank").eq(2L), field("score").gt(80L))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1) } @@ -189,7 +188,7 @@ internal class InequalityTests { .collection("users") .where(and(field("score").eq(90L), field("score").gt(80L))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1) } @@ -206,7 +205,7 @@ internal class InequalityTests { .where(field("score").gte(90L)) .sort(field("score").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc3).inOrder() } @@ -223,7 +222,7 @@ internal class InequalityTests { .where(field("score").gte(90L)) .sort(field("rank").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3, doc1).inOrder() } @@ -239,7 +238,7 @@ internal class InequalityTests { .collection("users") .where(or(field("score").gt(90L), field("score").lt(60L))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc2, doc3)) } @@ -261,7 +260,7 @@ internal class InequalityTests { .collection("users") .where(or(field("score").gt(80L), field("rank").lt(2L))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc3)) } @@ -282,7 +281,7 @@ internal class InequalityTests { .collection("users") .where(and(field("score").gt(80L), field("score").eqAny(listOf(50L, 80L, 97L)))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3) } @@ -308,7 +307,7 @@ internal class InequalityTests { .collection("users") .where(and(field("rank").lt(3L), field("score").eqAny(listOf(50L, 80L, 97L)))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3) } @@ -327,7 +326,7 @@ internal class InequalityTests { .collection("users") .where(and(field("score").gt(80L), field("score").notEqAny(listOf(90L, 95L)))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc4) } @@ -350,7 +349,7 @@ internal class InequalityTests { .collection("users") .where(field("score").notEqAny(listOf("foo", 90L, false))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result) .containsExactlyElementsIn(listOf(doc3, doc4, doc5, doc6, doc7, doc8, doc9, doc10)) } @@ -373,7 +372,7 @@ internal class InequalityTests { .collection("users") .where(and(field("rank").lt(3L), field("score").notEqAny(listOf(90L, 95L)))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3) } @@ -393,7 +392,7 @@ internal class InequalityTests { .where(and(field("rank").eq(2L), field("score").gt(80L))) .sort(field("rank").ascending(), field("score").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc4).inOrder() } @@ -422,7 +421,7 @@ internal class InequalityTests { .where(and(field("rank").eqAny(listOf(2L, 3L, 4L)), field("score").gt(80L))) .sort(field("rank").ascending(), field("score").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc4, doc1).inOrder() } @@ -453,7 +452,7 @@ internal class InequalityTests { .collection("users") .where(and(field("scores").lte(array(90L, 90L, 90L)), field("rounds").gt(array(1L, 2L)))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1) } @@ -491,7 +490,7 @@ internal class InequalityTests { // In Kotlin, arrayContains is the equivalent of C++ ArrayContainsExpr for a single element. // For multiple elements, it would be arrayContainsAny. - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1) } @@ -510,7 +509,7 @@ internal class InequalityTests { .sort(field("rank").ascending()) .limit(2) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3, doc4).inOrder() } @@ -526,7 +525,7 @@ internal class InequalityTests { .collection("users") .where(and(field("score").gt(90L), field("score").lt(100L))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3) } @@ -547,7 +546,7 @@ internal class InequalityTests { .collection("users") .where(and(field("score").gt(90L), field("rank").lt(2L))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3) } @@ -569,7 +568,7 @@ internal class InequalityTests { .collection("users") .where(and(field("score").gt(80L), field("rank").lt(3L))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc3)) } @@ -592,7 +591,7 @@ internal class InequalityTests { .collection("users") .where(and(field("score").gt(40L), field("rank").lt(4L))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc2, doc3)) } @@ -608,7 +607,7 @@ internal class InequalityTests { .collection("users") .where(and(field("score").lt(90L), field("rank").gt(3L))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -637,7 +636,7 @@ internal class InequalityTests { ) ) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1) } @@ -654,7 +653,7 @@ internal class InequalityTests { .where(and(field("rank").lt(3L), field("score").gt(80L))) .sort(field("rank").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3, doc1).inOrder() } @@ -671,7 +670,7 @@ internal class InequalityTests { .where(and(field("rank").lt(3L), field("score").gt(80L))) .sort(field("rank").descending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc3).inOrder() } @@ -688,7 +687,7 @@ internal class InequalityTests { .where(and(field("rank").lt(3L), field("score").gt(80L))) .sort(field("rank").ascending(), field("score").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3, doc1).inOrder() } @@ -705,7 +704,7 @@ internal class InequalityTests { .where(and(field("rank").lt(3L), field("score").gt(80L))) .sort(field("rank").descending(), field("score").descending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc3).inOrder() } @@ -722,7 +721,7 @@ internal class InequalityTests { .where(and(field("rank").lt(3L), field("score").gt(80L))) .sort(field("score").descending(), field("rank").descending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3, doc1).inOrder() } } diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/LimitTests.kt b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/LimitTests.kt index 7f383630f56..a71fe045ebc 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/LimitTests.kt +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/LimitTests.kt @@ -20,7 +20,6 @@ import com.google.firebase.firestore.TestUtil import com.google.firebase.firestore.model.MutableDocument import com.google.firebase.firestore.runPipeline import com.google.firebase.firestore.testutil.TestUtilKtx.doc -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.toList import kotlinx.coroutines.runBlocking import org.junit.Test @@ -45,7 +44,7 @@ internal class LimitTests { val documents = createDocs() val pipeline = RealtimePipelineSource(db).collection("k").limit(0) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -54,7 +53,7 @@ internal class LimitTests { val documents = createDocs() val pipeline = RealtimePipelineSource(db).collection("k").limit(0).limit(0).limit(0) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -63,7 +62,7 @@ internal class LimitTests { val documents = createDocs() val pipeline = RealtimePipelineSource(db).collection("k").limit(1) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).hasSize(1) } @@ -72,7 +71,7 @@ internal class LimitTests { val documents = createDocs() val pipeline = RealtimePipelineSource(db).collection("k").limit(1).limit(1).limit(1) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).hasSize(1) } @@ -81,7 +80,7 @@ internal class LimitTests { val documents = createDocs() val pipeline = RealtimePipelineSource(db).collection("k").limit(2) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).hasSize(2) } @@ -90,7 +89,7 @@ internal class LimitTests { val documents = createDocs() val pipeline = RealtimePipelineSource(db).collection("k").limit(2).limit(2).limit(2) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).hasSize(2) } @@ -99,7 +98,7 @@ internal class LimitTests { val documents = createDocs() val pipeline = RealtimePipelineSource(db).collection("k").limit(3) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).hasSize(3) } @@ -108,7 +107,7 @@ internal class LimitTests { val documents = createDocs() val pipeline = RealtimePipelineSource(db).collection("k").limit(3).limit(3).limit(3) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).hasSize(3) } @@ -117,7 +116,7 @@ internal class LimitTests { val documents = createDocs() val pipeline = RealtimePipelineSource(db).collection("k").limit(4) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).hasSize(4) } @@ -126,7 +125,7 @@ internal class LimitTests { val documents = createDocs() val pipeline = RealtimePipelineSource(db).collection("k").limit(4).limit(4).limit(4) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).hasSize(4) } @@ -135,7 +134,7 @@ internal class LimitTests { val documents = createDocs() // Only 4 docs created val pipeline = RealtimePipelineSource(db).collection("k").limit(5) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).hasSize(4) // Limited by actual doc count } @@ -144,7 +143,7 @@ internal class LimitTests { val documents = createDocs() // Only 4 docs created val pipeline = RealtimePipelineSource(db).collection("k").limit(5).limit(5).limit(5) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).hasSize(4) // Limited by actual doc count } @@ -153,7 +152,7 @@ internal class LimitTests { val documents = createDocs() val pipeline = RealtimePipelineSource(db).collection("k").limit(Int.MAX_VALUE) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).hasSize(4) } @@ -167,7 +166,7 @@ internal class LimitTests { .limit(Int.MAX_VALUE) .limit(Int.MAX_VALUE) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).hasSize(4) } } diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/NestedPropertiesTests.kt b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/NestedPropertiesTests.kt index 9a6460c72cd..b9a226db48a 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/NestedPropertiesTests.kt +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/NestedPropertiesTests.kt @@ -26,7 +26,6 @@ import com.google.firebase.firestore.pipeline.Expr.Companion.map import com.google.firebase.firestore.pipeline.Expr.Companion.not import com.google.firebase.firestore.runPipeline import com.google.firebase.firestore.testutil.TestUtilKtx.doc -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.toList import kotlinx.coroutines.runBlocking import org.junit.Test @@ -126,7 +125,7 @@ internal class NestedPropertiesTests { .collection("/users") .where(field("a.b.c.d.e.f.g.h.i.j.k").eq(constant(42L))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1) } @@ -219,7 +218,7 @@ internal class NestedPropertiesTests { .where(field("a.b.c.d.e.f.g.h.i.j.k").gte(constant(0L))) .sort(field(PublicFieldPath.documentId()).ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc3).inOrder() } @@ -254,7 +253,7 @@ internal class NestedPropertiesTests { .collection("/users") .where(field("address.street").eq(constant("76"))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc2) } @@ -290,7 +289,7 @@ internal class NestedPropertiesTests { .where(field("address.city").eq(constant("San Francisco"))) .where(field("address.zip").gt(constant(90000L))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1) } @@ -329,7 +328,7 @@ internal class NestedPropertiesTests { ) .where(field("address.zip").gt(constant(90000L))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1) } @@ -366,7 +365,7 @@ internal class NestedPropertiesTests { .where(field("address.city").eq(constant("San Francisco"))) .where(field("address.zip").gt(constant(90000L))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1) } @@ -400,25 +399,25 @@ internal class NestedPropertiesTests { RealtimePipelineSource(db) .collection("/users") .where(field("address.zip").gt(constant(90000L))) - assertThat(runPipeline(pipeline1, flowOf(*documents.toTypedArray())).toList()) + assertThat(runPipeline(pipeline1, listOf(*documents.toTypedArray())).toList()) .containsExactly(doc1, doc3) val pipeline2 = RealtimePipelineSource(db) .collection("/users") .where(field("address.zip").lt(constant(90000L))) - assertThat(runPipeline(pipeline2, flowOf(*documents.toTypedArray())).toList()) + assertThat(runPipeline(pipeline2, listOf(*documents.toTypedArray())).toList()) .containsExactly(doc2) val pipeline3 = RealtimePipelineSource(db).collection("/users").where(field("address.zip").lt(constant(0L))) - assertThat(runPipeline(pipeline3, flowOf(*documents.toTypedArray())).toList()).isEmpty() + assertThat(runPipeline(pipeline3, listOf(*documents.toTypedArray())).toList()).isEmpty() val pipeline4 = RealtimePipelineSource(db) .collection("/users") .where(field("address.zip").neq(constant(10011L))) - assertThat(runPipeline(pipeline4, flowOf(*documents.toTypedArray())).toList()) + assertThat(runPipeline(pipeline4, listOf(*documents.toTypedArray())).toList()) .containsExactly(doc1, doc3) } @@ -451,7 +450,7 @@ internal class NestedPropertiesTests { val pipeline = RealtimePipelineSource(db).collection("/users").where(exists(field("address.street"))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc2) } @@ -484,7 +483,7 @@ internal class NestedPropertiesTests { val pipeline = RealtimePipelineSource(db).collection("/users").where(not(exists(field("address.street")))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc3, doc4).inOrder() } @@ -519,7 +518,7 @@ internal class NestedPropertiesTests { val pipeline = RealtimePipelineSource(db).collection("/users").where(isNull(field("address.street"))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1) } @@ -554,7 +553,7 @@ internal class NestedPropertiesTests { val pipeline = RealtimePipelineSource(db).collection("/users").where(not(isNull(field("address.street")))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc2) } @@ -593,7 +592,7 @@ internal class NestedPropertiesTests { .where(exists(field("address.street"))) .sort(field("address.street").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc2).inOrder() } @@ -629,7 +628,7 @@ internal class NestedPropertiesTests { val pipeline = RealtimePipelineSource(db).collection("/users").sort(field("address.street").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() // Missing fields sort first, then by key (c < d). Then existing fields by value ("41" < "76"). assertThat(result).containsExactly(doc3, doc4, doc1, doc2).inOrder() } @@ -646,7 +645,7 @@ internal class NestedPropertiesTests { .collection("/users") .where(field("address.city").eq(constant("San Francisco"))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc2) } @@ -662,7 +661,7 @@ internal class NestedPropertiesTests { .collection("/users") .where(field(PublicFieldPath.of("address.city")).eq(constant("San Francisco"))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1) } } diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/NullSemanticsTests.kt b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/NullSemanticsTests.kt index b29940ff9f8..1b7be77b606 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/NullSemanticsTests.kt +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/NullSemanticsTests.kt @@ -42,7 +42,6 @@ import com.google.firebase.firestore.pipeline.Expr.Companion.or import com.google.firebase.firestore.pipeline.Expr.Companion.xor import com.google.firebase.firestore.runPipeline import com.google.firebase.firestore.testutil.TestUtilKtx.doc -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.toList import kotlinx.coroutines.runBlocking import org.junit.Test @@ -70,7 +69,7 @@ internal class NullSemanticsTests { val pipeline = RealtimePipelineSource(db).collection("users").where(isNull(field("score"))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1) } @@ -88,7 +87,7 @@ internal class NullSemanticsTests { val pipeline = RealtimePipelineSource(db).collection("users").where(isNotNull(field("score"))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc2, doc3, doc4, doc5, doc6)) } @@ -105,7 +104,7 @@ internal class NullSemanticsTests { .collection("users") .where(and(isNull(field("score")), isNotNull(field("score")))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -122,7 +121,7 @@ internal class NullSemanticsTests { .collection("users") .where(eq(field("score"), nullValue())) // Equality filters never match null or missing - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -140,7 +139,7 @@ internal class NullSemanticsTests { .collection("users") .where(eq(field("score"), field("rank"))) // Equality filters never match null - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -157,7 +156,7 @@ internal class NullSemanticsTests { val pipeline = RealtimePipelineSource(db).collection("users").where(eq(field("score.bonus"), nullValue())) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -176,7 +175,7 @@ internal class NullSemanticsTests { .collection("users") .where(and(eq(field("score.bonus"), nullValue()), eq(field("rank"), nullValue()))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -190,7 +189,7 @@ internal class NullSemanticsTests { val pipeline = RealtimePipelineSource(db).collection("k").where(eq(field("foo"), array(nullValue()))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -207,7 +206,7 @@ internal class NullSemanticsTests { .collection("k") .where(eq(field("foo"), array(constant(1.0), nullValue()))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -223,7 +222,7 @@ internal class NullSemanticsTests { .collection("k") .where(eq(field("foo"), array(nullValue(), constant(Double.NaN)))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -239,7 +238,7 @@ internal class NullSemanticsTests { .collection("k") .where(eq(field("foo"), map(mapOf("a" to nullValue())))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -256,7 +255,7 @@ internal class NullSemanticsTests { .collection("k") .where(eq(field("foo"), map(mapOf("a" to constant(1.0), "b" to nullValue())))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -272,7 +271,7 @@ internal class NullSemanticsTests { .collection("k") .where(eq(field("foo"), map(mapOf("a" to nullValue(), "b" to constant(Double.NaN))))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -292,7 +291,7 @@ internal class NullSemanticsTests { .collection("k") .where(eq(field("foo"), map(mapOf("a" to array(nullValue()))))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -313,7 +312,7 @@ internal class NullSemanticsTests { .collection("k") .where(eq(field("foo"), map(mapOf("a" to array(constant(1.0), nullValue()))))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -333,7 +332,7 @@ internal class NullSemanticsTests { .collection("k") .where(eq(field("foo"), map(mapOf("a" to array(nullValue(), constant(Double.NaN)))))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -348,7 +347,7 @@ internal class NullSemanticsTests { .collection("users") .where(and(eq(field("score"), constant(42L)), eq(field("rank"), nullValue()))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -364,7 +363,7 @@ internal class NullSemanticsTests { .collection("users") .where(eqAny(field("score"), array(nullValue()))) // IN filters never match null - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -382,7 +381,7 @@ internal class NullSemanticsTests { .collection("users") .where(eqAny(field("score"), array(nullValue(), constant(100L)))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc4) } @@ -404,7 +403,7 @@ internal class NullSemanticsTests { .collection("users") .where(arrayContains(field("score"), nullValue())) // arrayContains does not match null - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -428,7 +427,7 @@ internal class NullSemanticsTests { arrayContainsAny(field("score"), array(nullValue())) ) // arrayContainsAny does not match null - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -450,7 +449,7 @@ internal class NullSemanticsTests { .collection("users") .where(arrayContainsAny(field("score"), array(nullValue(), constant("foo")))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc6) } @@ -474,7 +473,7 @@ internal class NullSemanticsTests { arrayContainsAll(field("score"), array(nullValue())) ) // arrayContainsAll does not match null - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -498,7 +497,7 @@ internal class NullSemanticsTests { arrayContainsAll(field("score"), array(nullValue(), constant(42L))) ) // arrayContainsAll does not match null - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -515,7 +514,7 @@ internal class NullSemanticsTests { .collection("users") .where(neq(field("score"), nullValue())) // != null is not supported - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -533,7 +532,7 @@ internal class NullSemanticsTests { .collection("users") .where(neq(field("score"), field("rank"))) // != null is not supported - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -549,7 +548,7 @@ internal class NullSemanticsTests { .collection("k") .where(neq(field("foo"), array(nullValue()))) // != [null] is not supported - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc2, doc3)) } @@ -568,7 +567,7 @@ internal class NullSemanticsTests { neq(field("foo"), array(constant(1.0), nullValue())) ) // != [1.0, null] is not supported - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1) } @@ -586,7 +585,7 @@ internal class NullSemanticsTests { neq(field("foo"), array(nullValue(), constant(Double.NaN))) ) // != [null, NaN] is not supported - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc3)) } @@ -602,7 +601,7 @@ internal class NullSemanticsTests { .collection("k") .where(neq(field("foo"), map(mapOf("a" to nullValue())))) // != {a:null} is not supported - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc2, doc3)) } @@ -621,7 +620,7 @@ internal class NullSemanticsTests { neq(field("foo"), map(mapOf("a" to constant(1.0), "b" to nullValue()))) ) // != {a:1.0,b:null} not supported - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1) } @@ -639,7 +638,7 @@ internal class NullSemanticsTests { neq(field("foo"), map(mapOf("a" to nullValue(), "b" to constant(Double.NaN)))) ) // != {a:null,b:NaN} not supported - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc3)) } @@ -654,7 +653,7 @@ internal class NullSemanticsTests { .collection("users") .where(notEqAny(field("score"), array(nullValue()))) // NOT IN [null] is not supported - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -672,7 +671,7 @@ internal class NullSemanticsTests { .collection("users") .where(gt(field("score"), nullValue())) // > null is not supported - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -690,7 +689,7 @@ internal class NullSemanticsTests { .collection("users") .where(gte(field("score"), nullValue())) // >= null is not supported - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -708,7 +707,7 @@ internal class NullSemanticsTests { .collection("users") .where(lt(field("score"), nullValue())) // < null is not supported - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -726,7 +725,7 @@ internal class NullSemanticsTests { .collection("users") .where(lte(field("score"), nullValue())) // <= null is not supported - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -743,7 +742,7 @@ internal class NullSemanticsTests { .collection("k") .where(and(eq(field("a"), constant(true)), eq(field("b"), constant(true)))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc4) } @@ -774,7 +773,7 @@ internal class NullSemanticsTests { // (missing AND null) -> error // (null AND missing) -> error // (missing AND missing) -> error - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc3, doc6)) } @@ -835,7 +834,7 @@ internal class NullSemanticsTests { .collection("k") .where(isError(and(eq(field("a"), constant(true)), eq(field("b"), constant(true))))) // This happens if either a or b is missing. - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc2, doc5, doc8)) } @@ -851,7 +850,7 @@ internal class NullSemanticsTests { .collection("k") .where(or(eq(field("a"), constant(true)), eq(field("b"), constant(true)))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1) } @@ -879,7 +878,7 @@ internal class NullSemanticsTests { // (null OR true) -> true // (missing OR false) -> error // (false OR missing) -> error - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc4, doc7)) } @@ -939,7 +938,7 @@ internal class NullSemanticsTests { .collection("k") .where(isError(or(eq(field("a"), constant(true)), eq(field("b"), constant(true))))) // This happens if either a or b is missing. - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc2, doc5, doc8)) } @@ -957,7 +956,7 @@ internal class NullSemanticsTests { .collection("k") .where(xor(eq(field("a"), constant(true)), eq(field("b"), constant(true)))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc4) } @@ -985,7 +984,7 @@ internal class NullSemanticsTests { // (null XOR null) -> null (doc1) // (missing XOR true) -> error // (true XOR missing) -> error - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc3, doc4, doc6, doc7)) } @@ -1046,7 +1045,7 @@ internal class NullSemanticsTests { .collection("k") .where(isError(xor(eq(field("a"), constant(true)), eq(field("b"), constant(true))))) // This happens if either a or b is missing. - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc2, doc5, doc8)) } @@ -1061,7 +1060,7 @@ internal class NullSemanticsTests { RealtimePipelineSource(db).collection("k").where(not(eq(field("a"), constant(true)))) // Based on C++ test's interpretation of TS behavior for NOT - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc2) } @@ -1077,7 +1076,7 @@ internal class NullSemanticsTests { // NOT(null_operand) -> null. So ISNULL(null) -> true. // NOT(true) -> false. ISNULL(false) -> false. // NOT(false) -> true. ISNULL(true) -> false. - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3) } @@ -1098,7 +1097,7 @@ internal class NullSemanticsTests { val pipeline = RealtimePipelineSource(db).collection("k").where(isError(not(eq(field("a"), constant(true))))) // This happens if a is missing. - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc4) } @@ -1120,7 +1119,7 @@ internal class NullSemanticsTests { val pipeline = RealtimePipelineSource(db).collection("k").sort(field("foo").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result) .containsExactly(doc0, doc1, doc2, doc3, doc4, doc5, doc6, doc7, doc8) .inOrder() @@ -1141,7 +1140,7 @@ internal class NullSemanticsTests { val pipeline = RealtimePipelineSource(db).collection("k").sort(field("foo").descending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result) .containsExactly(doc8, doc7, doc6, doc5, doc4, doc3, doc2, doc1, doc0) .inOrder() @@ -1162,7 +1161,7 @@ internal class NullSemanticsTests { val pipeline = RealtimePipelineSource(db).collection("k").sort(field("foo").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result) .containsExactly(doc0, doc1, doc2, doc3, doc4, doc5, doc6, doc7, doc8) .inOrder() @@ -1183,7 +1182,7 @@ internal class NullSemanticsTests { val pipeline = RealtimePipelineSource(db).collection("k").sort(field("foo").descending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result) .containsExactly(doc8, doc7, doc6, doc5, doc4, doc3, doc2, doc1, doc0) .inOrder() diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/NumberSemanticsTests.kt b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/NumberSemanticsTests.kt index 2216d9ccf83..46d9834026e 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/NumberSemanticsTests.kt +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/NumberSemanticsTests.kt @@ -25,7 +25,6 @@ import com.google.firebase.firestore.pipeline.Expr.Companion.field import com.google.firebase.firestore.pipeline.Expr.Companion.notEqAny import com.google.firebase.firestore.runPipeline import com.google.firebase.firestore.testutil.TestUtilKtx.doc -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.toList import kotlinx.coroutines.runBlocking import org.junit.Test @@ -48,7 +47,7 @@ internal class NumberSemanticsTests { val pipeline = RealtimePipelineSource(db).collection("users").where(field("score").eq(constant(-0.0))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc3, doc4)) } @@ -65,7 +64,7 @@ internal class NumberSemanticsTests { .collection("users") .where(field("score").eq(constant(0L))) // Firestore -0LL is 0L - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc3, doc4)) } @@ -80,7 +79,7 @@ internal class NumberSemanticsTests { val pipeline = RealtimePipelineSource(db).collection("users").where(field("score").eq(constant(0.0))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc3, doc4)) } @@ -95,7 +94,7 @@ internal class NumberSemanticsTests { val pipeline = RealtimePipelineSource(db).collection("users").where(field("score").eq(constant(0L))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc3, doc4)) } @@ -109,7 +108,7 @@ internal class NumberSemanticsTests { val pipeline = RealtimePipelineSource(db).collection("users").where(field("age").eq(constant(Double.NaN))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -123,7 +122,7 @@ internal class NumberSemanticsTests { val pipeline = RealtimePipelineSource(db).collection("users").where(field("age").lt(constant(Double.NaN))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -137,7 +136,7 @@ internal class NumberSemanticsTests { val pipeline = RealtimePipelineSource(db).collection("users").where(field("age").lte(constant(Double.NaN))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -151,7 +150,7 @@ internal class NumberSemanticsTests { val pipeline = RealtimePipelineSource(db).collection("users").where(field("age").gte(constant(Double.NaN))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -165,7 +164,7 @@ internal class NumberSemanticsTests { val pipeline = RealtimePipelineSource(db).collection("users").where(field("age").gt(constant(Double.NaN))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -179,7 +178,7 @@ internal class NumberSemanticsTests { val pipeline = RealtimePipelineSource(db).collection("users").where(field("age").neq(constant(Double.NaN))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc2, doc3)) } @@ -195,7 +194,7 @@ internal class NumberSemanticsTests { .collection("users") .where(field("name").eqAny(array(Double.NaN, "alice"))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1) } @@ -209,7 +208,7 @@ internal class NumberSemanticsTests { val pipeline = RealtimePipelineSource(db).collection("users").where(field("age").eqAny(array(Double.NaN))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -230,7 +229,7 @@ internal class NumberSemanticsTests { .collection("users") .where(arrayContains(field("age"), constant(Double.NaN))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -246,7 +245,7 @@ internal class NumberSemanticsTests { .collection("k") .where(arrayContainsAny(field("field"), array(Double.NaN, "foo"))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3) } @@ -262,7 +261,7 @@ internal class NumberSemanticsTests { .collection("users") .where(notEqAny(field("age"), array(Double.NaN, 42L))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc2, doc3)) } @@ -278,7 +277,7 @@ internal class NumberSemanticsTests { .collection("users") .where(notEqAny(field("age"), array(Double.NaN))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc1, doc2, doc3)) } @@ -291,7 +290,7 @@ internal class NumberSemanticsTests { val pipeline = RealtimePipelineSource(db).collection("k").where(field("foo").eq(array(Double.NaN))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } } diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/PipelineTests.kt b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/PipelineTests.kt index 86184a3827f..01c1d3e2667 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/PipelineTests.kt +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/PipelineTests.kt @@ -21,7 +21,6 @@ import com.google.firebase.firestore.model.MutableDocument import com.google.firebase.firestore.pipeline.Expr.Companion.field import com.google.firebase.firestore.runPipeline import com.google.firebase.firestore.testutil.TestUtilKtx.doc -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.toList import kotlinx.coroutines.runBlocking import org.junit.Test @@ -40,7 +39,7 @@ internal class PipelineTests { val doc2: MutableDocument = doc("foo/2", 0, mapOf("bar" to "43")) val doc3: MutableDocument = doc("xxx/1", 0, mapOf("bar" to 42)) - val list = runPipeline(pipeline, flowOf(doc1, doc2, doc3)).toList() + val list = runPipeline(pipeline, listOf(doc1, doc2, doc3)).toList() assertThat(list).hasSize(1) } diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/SortTests.kt b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/SortTests.kt index e4d65da4408..2e49b282d53 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/SortTests.kt +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/SortTests.kt @@ -26,7 +26,6 @@ import com.google.firebase.firestore.pipeline.Expr.Companion.field import com.google.firebase.firestore.pipeline.Expr.Companion.not import com.google.firebase.firestore.runPipeline import com.google.firebase.firestore.testutil.TestUtilKtx.doc -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.toList import kotlinx.coroutines.runBlocking import org.junit.Test @@ -43,7 +42,7 @@ internal class SortTests { val documents = emptyList() val pipeline = RealtimePipelineSource(db).collection("users").sort(field("age").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -52,7 +51,7 @@ internal class SortTests { val documents = emptyList() val pipeline = RealtimePipelineSource(db).collection("users").sort(field("age").descending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -62,7 +61,7 @@ internal class SortTests { val documents = listOf(doc1) val pipeline = RealtimePipelineSource(db).collection("users").sort(field("age").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1) } @@ -76,7 +75,7 @@ internal class SortTests { .where(exists(field("age"))) .sort(field("age").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1) } @@ -90,7 +89,7 @@ internal class SortTests { .where(not(exists(field("age")))) .sort(field("age").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -104,7 +103,7 @@ internal class SortTests { .where(field("age").eq(10L)) .sort(field("age").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1) } @@ -114,7 +113,7 @@ internal class SortTests { val documents = listOf(doc1) val pipeline = RealtimePipelineSource(db).collection("users").sort(field("age").descending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1) } @@ -128,7 +127,7 @@ internal class SortTests { .where(exists(field("age"))) .sort(field("age").descending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1) } @@ -142,7 +141,7 @@ internal class SortTests { .where(field("age").eq(10L)) .sort(field("age").descending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1) } @@ -162,7 +161,7 @@ internal class SortTests { // So expected order: doc3, doc1, doc2, doc4, doc5 (if 'd' < 'e') or doc3, doc1, doc2, doc5, // doc4 (if 'e' < 'd') // Since the C++ test uses UnorderedElementsAre, we'll use containsExactlyElementsIn. - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactlyElementsIn(listOf(doc3, doc1, doc2, doc4, doc5)).inOrder() // Actually, the local pipeline implementation might not guarantee tie-breaking by key unless // explicitly added. @@ -191,7 +190,7 @@ internal class SortTests { .where(exists(field("age"))) .sort(field("age").descending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3, doc1, doc2, doc4, doc5).inOrder() } @@ -209,7 +208,7 @@ internal class SortTests { .collection("users") .where(field("age").gt(0.0)) .sort(field("age").descending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3, doc1, doc2, doc4, doc5).inOrder() } @@ -227,7 +226,7 @@ internal class SortTests { .collection("users") .sort(field("age").descending(), field("name").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() // age desc: 100(c), 75.5(a), 25(b), 10(d), 10(e) // name asc for 10: diane(d), eric(e) // Expected: c, a, b, d, e @@ -250,7 +249,7 @@ internal class SortTests { .where(exists(field("name"))) .sort(field("age").descending(), field("name").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3, doc1, doc2, doc4, doc5).inOrder() } @@ -272,7 +271,7 @@ internal class SortTests { // Filtered: doc4, doc5 // Sort by missing age (no op), then missing name (no op), then by key ascending. // d < e - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc4, doc5).inOrder() } @@ -292,7 +291,7 @@ internal class SortTests { .where(field("name").regexMatch(".*")) // Implicit exists name .sort(field("age").descending(), field("name").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3, doc1, doc2, doc4, doc5).inOrder() } @@ -311,7 +310,7 @@ internal class SortTests { .where(exists(field("name"))) .sort(field("age").descending(), field("name").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3, doc1, doc2, doc4, doc5).inOrder() } @@ -330,7 +329,7 @@ internal class SortTests { .where(not(exists(field("name")))) // Only doc2 matches .sort(field("age").descending(), field("name").descending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc2) } @@ -350,7 +349,7 @@ internal class SortTests { .where(not(exists(field("name")))) // Only doc2 matches .sort(field("name").descending(), field("age").descending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc2) } @@ -369,7 +368,7 @@ internal class SortTests { .where(field("name").regexMatch(".*")) .sort(field("age").descending(), field("name").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3, doc1, doc2, doc4, doc5).inOrder() } @@ -388,7 +387,7 @@ internal class SortTests { // Sorting by a missing field results in undefined order relative to each other, // but documents are secondarily sorted by key. // Since it's descending for not_age (all are null essentially), key order will be ascending. - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc2, doc3, doc4, doc5).inOrder() } @@ -402,7 +401,7 @@ internal class SortTests { .where(exists(field("not_age"))) .sort(field("not_age").descending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -420,7 +419,7 @@ internal class SortTests { // Missing fields sort first in ascending order, then by key. b < d // Then existing fields sorted by value: e (10.0) < a (75.5) < c (100.0) // Expected: doc2, doc4, doc5, doc1, doc3 - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc2, doc4, doc5, doc1, doc3).inOrder() } @@ -440,7 +439,7 @@ internal class SortTests { .sort(field("age").ascending()) // Sort remaining: doc5 (10.0), doc1 (75.5), doc3 (100.0) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc5, doc1, doc3).inOrder() } @@ -460,7 +459,7 @@ internal class SortTests { .sort(field("age").ascending()) // Sort by non-existent field, then key // Sort remaining by key: doc2, doc4 - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc2, doc4).inOrder() } @@ -480,7 +479,7 @@ internal class SortTests { .limit(2) // Expected: doc4, doc5 - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc4, doc5).inOrder() } @@ -500,7 +499,7 @@ internal class SortTests { .sort(field("age").ascending()) // Sort: e (10), b (25), a (75.5), c (100) .limit(2) // Limit 2: e, b - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc5, doc2).inOrder() } @@ -520,7 +519,7 @@ internal class SortTests { .sort(field("age").ascending()) // Sort by missing field -> key order: d, e .limit(2) // Limit 2: d, e - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc4, doc5).inOrder() } @@ -531,7 +530,7 @@ internal class SortTests { val pipeline = RealtimePipelineSource(db).collection("users").sort(field("age").ascending()).limit(0) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -551,7 +550,7 @@ internal class SortTests { .limit(1) // Limits to doc1 (key "users/a" is first by default key order) .sort(field("age").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1) } @@ -571,7 +570,7 @@ internal class SortTests { .limit(1) // Limits to doc1 (key "users/a") .sort(field("age").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1) } @@ -591,7 +590,7 @@ internal class SortTests { .limit(1) // Limits to doc4 (key "users/d") .sort(field("age").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc4) } @@ -611,7 +610,7 @@ internal class SortTests { .where(not(exists(field("age")))) // Filter out a, b (both have age) .sort(field("age").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -622,7 +621,7 @@ internal class SortTests { val pipeline = RealtimePipelineSource(db).collectionGroup("users").limit(0).sort(field("age").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -647,7 +646,7 @@ internal class SortTests { // doc5: 20+10 = 30 // doc1: 10+10 = 20 // Expected: doc3, doc4, doc2, doc5, doc1 - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3, doc4, doc2, doc5, doc1).inOrder() } @@ -673,7 +672,7 @@ internal class SortTests { // doc5: 20+10 = 30 // doc1: 10+10 = 20 // Expected: doc3, doc2, doc5, doc1 - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3, doc2, doc5, doc1).inOrder() } @@ -696,7 +695,7 @@ internal class SortTests { // Sort by (age+10) desc where age is missing. This means they are treated as null for the // expression. // Then tie-broken by key: d, e - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc4, doc5).inOrder() } @@ -722,7 +721,7 @@ internal class SortTests { // However, if these are separate stages, the last one would indeed re-sort the entire output of // the previous. // Let's assume the Kotlin pipeline behaves this way for separate .orderBy() calls. - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc2, doc1, doc3).inOrder() } @@ -740,7 +739,7 @@ internal class SortTests { .sort(field("age").ascending()) // Sort by age: 2(30), 1(40), 3(50) .sort(field(PublicFieldPath.documentId()).ascending()) // Sort by key: 1, 2, 3 - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc2, doc3).inOrder() } @@ -765,7 +764,7 @@ internal class SortTests { .sort(field(PublicFieldPath.documentId()).ascending()) .sort(field("age").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc2, doc1, doc3).inOrder() } @@ -783,7 +782,7 @@ internal class SortTests { .sort(field("age").ascending()) .sort(field(PublicFieldPath.documentId()).ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc2, doc3).inOrder() } } diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/UnicodeTests.kt b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/UnicodeTests.kt index 3668abd96ee..aa5cf98a092 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/UnicodeTests.kt +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/UnicodeTests.kt @@ -22,7 +22,6 @@ import com.google.firebase.firestore.pipeline.Expr.Companion.constant import com.google.firebase.firestore.pipeline.Expr.Companion.field import com.google.firebase.firestore.runPipeline import com.google.firebase.firestore.testutil.TestUtilKtx.doc -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.toList import kotlinx.coroutines.runBlocking import org.junit.Test @@ -43,7 +42,7 @@ internal class UnicodeTests { val documents = listOf(doc1, doc2, doc3) val pipeline = RealtimePipelineSource(db).collection("/🐵").sort(field("Ł").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc2, doc3).inOrder() } @@ -66,7 +65,7 @@ internal class UnicodeTests { ) .sort(field("str").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc2, doc1).inOrder() } @@ -79,7 +78,7 @@ internal class UnicodeTests { val documents = listOf(doc1, doc2, doc3) val pipeline = RealtimePipelineSource(db).collection("users").sort(field("foo").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3, doc2, doc1).inOrder() } @@ -92,7 +91,7 @@ internal class UnicodeTests { val documents = listOf(doc1, doc2, doc3) val pipeline = RealtimePipelineSource(db).collection("users").sort(field("map").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc3, doc2).inOrder() } @@ -105,7 +104,7 @@ internal class UnicodeTests { val documents = listOf(doc1, doc2, doc3) val pipeline = RealtimePipelineSource(db).collection("users").sort(field("map").ascending()) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc3, doc2).inOrder() } } diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/WhereTests.kt b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/WhereTests.kt index 065b1dc1738..6fe3196a061 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/WhereTests.kt +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/WhereTests.kt @@ -29,7 +29,6 @@ import com.google.firebase.firestore.pipeline.Expr.Companion.or import com.google.firebase.firestore.pipeline.Expr.Companion.xor import com.google.firebase.firestore.runPipeline import com.google.firebase.firestore.testutil.TestUtilKtx.doc -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.toList import kotlinx.coroutines.runBlocking import org.junit.Test @@ -47,7 +46,7 @@ internal class WhereTests { val pipeline = RealtimePipelineSource(TestUtil.firestore()).collection("users").where(field("age").gte(10L)) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).isEmpty() } @@ -66,7 +65,7 @@ internal class WhereTests { .where(and(field("age").gte(10.0), field("age").gte(20.0))) // age >= 10.0 AND age >= 20.0 => age >= 20.0 // Matches: doc1 (75.5), doc2 (25.0), doc3 (100.0) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc2, doc3) } @@ -85,8 +84,8 @@ internal class WhereTests { .collection("users") .where(constant(25.0).eq(field("age"))) - val result1 = runPipeline(pipeline1, flowOf(*documents.toTypedArray())).toList() - val result2 = runPipeline(pipeline2, flowOf(*documents.toTypedArray())).toList() + val result1 = runPipeline(pipeline1, listOf(*documents.toTypedArray())).toList() + val result2 = runPipeline(pipeline2, listOf(*documents.toTypedArray())).toList() assertThat(result1).containsExactly(doc2) assertThat(result1).isEqualTo(result2) @@ -109,8 +108,8 @@ internal class WhereTests { .collection("users") .where(and(field("age").lt(70.0), field("age").gt(10.0))) - val result1 = runPipeline(pipeline1, flowOf(*documents.toTypedArray())).toList() - val result2 = runPipeline(pipeline2, flowOf(*documents.toTypedArray())).toList() + val result1 = runPipeline(pipeline1, listOf(*documents.toTypedArray())).toList() + val result2 = runPipeline(pipeline2, listOf(*documents.toTypedArray())).toList() assertThat(result1).containsExactly(doc2) assertThat(result1).isEqualTo(result2) @@ -132,8 +131,8 @@ internal class WhereTests { RealtimePipelineSource(TestUtil.firestore()) .collection("users") .where(or(field("age").gt(80.0), field("age").lt(10.0))) - val result1 = runPipeline(pipeline1, flowOf(*documents.toTypedArray())).toList() - val result2 = runPipeline(pipeline2, flowOf(*documents.toTypedArray())).toList() + val result1 = runPipeline(pipeline1, listOf(*documents.toTypedArray())).toList() + val result2 = runPipeline(pipeline2, listOf(*documents.toTypedArray())).toList() assertThat(result1).containsExactly(doc3) assertThat(result1).isEqualTo(result2) @@ -158,8 +157,8 @@ internal class WhereTests { .collection("users") .where(eqAny(field("name"), array(values))) - val result1 = runPipeline(pipeline1, flowOf(*documents.toTypedArray())).toList() - val result2 = runPipeline(pipeline2, flowOf(*documents.toTypedArray())).toList() + val result1 = runPipeline(pipeline1, listOf(*documents.toTypedArray())).toList() + val result2 = runPipeline(pipeline2, listOf(*documents.toTypedArray())).toList() assertThat(result1).containsExactly(doc1) assertThat(result1).isEqualTo(result2) @@ -182,7 +181,7 @@ internal class WhereTests { // age >= 10.0 THEN age >= 20.0 => age >= 20.0 // Matches: doc1 (75.5), doc2 (25.0), doc3 (100.0) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc2, doc3) } @@ -201,7 +200,7 @@ internal class WhereTests { .where(field("age").eq(75L)) .where(field("height").eq(55L)) // 55L will also match 55.0 - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3) } @@ -226,7 +225,7 @@ internal class WhereTests { // doc3: 75 > 50 (T) AND 55.0 < 75 (T) -> True // doc4: 41 > 50 (F) // doc5: 75 > 50 (T) AND 80 < 75 (F) -> False - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc3) } @@ -253,7 +252,7 @@ internal class WhereTests { // doc3: charlie (yes), baker (yes) -> Match // doc4: diane (yes), miller (yes) -> Match // doc5: eric (no), davis (no) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3, doc4) } @@ -304,7 +303,7 @@ internal class WhereTests { // doc3: 75==75 (T), 50>45 (T), baker ends er (T) -> True // doc4: 75==75 (T), 50>45 (T), miller ends er (T) -> True // doc5: 80==75 (F) -> False - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3, doc4) } @@ -320,7 +319,7 @@ internal class WhereTests { val pipeline = RealtimePipelineSource(TestUtil.firestore()).collection("users").where(exists(field("name"))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc2, doc3) } @@ -338,7 +337,7 @@ internal class WhereTests { .collection("users") .where(not(exists(field("name")))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc4, doc5) } @@ -356,7 +355,7 @@ internal class WhereTests { .collection("users") .where(not(not(exists(field("name"))))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc2, doc3) } @@ -373,7 +372,7 @@ internal class WhereTests { RealtimePipelineSource(TestUtil.firestore()) .collection("users") .where(and(exists(field("name")), exists(field("age")))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc2) } @@ -390,7 +389,7 @@ internal class WhereTests { RealtimePipelineSource(TestUtil.firestore()) .collection("users") .where(or(exists(field("name")), exists(field("age")))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc2, doc3, doc4) } @@ -407,7 +406,7 @@ internal class WhereTests { RealtimePipelineSource(TestUtil.firestore()) .collection("users") .where(not(and(exists(field("name")), exists(field("age"))))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3, doc4, doc5) } @@ -424,7 +423,7 @@ internal class WhereTests { RealtimePipelineSource(TestUtil.firestore()) .collection("users") .where(not(or(exists(field("name")), exists(field("age"))))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc5) } @@ -444,7 +443,7 @@ internal class WhereTests { // NOT ( (name exists AND NOT age exists) OR (NOT name exists AND age exists) ) // = (name exists AND age exists) OR (NOT name exists AND NOT age exists) // Matches: doc1, doc2, doc5 - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc2, doc5) } @@ -461,7 +460,7 @@ internal class WhereTests { RealtimePipelineSource(TestUtil.firestore()) .collection("users") .where(and(not(exists(field("name"))), not(exists(field("age"))))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc5) } @@ -478,7 +477,7 @@ internal class WhereTests { RealtimePipelineSource(TestUtil.firestore()) .collection("users") .where(or(not(exists(field("name"))), not(exists(field("age"))))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3, doc4, doc5) } @@ -498,7 +497,7 @@ internal class WhereTests { // (NOT name exists AND NOT (NOT age exists)) OR (NOT (NOT name exists) AND NOT age exists) // (NOT name exists AND age exists) OR (name exists AND NOT age exists) // Matches: doc3, doc4 - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc3, doc4) } @@ -515,7 +514,7 @@ internal class WhereTests { RealtimePipelineSource(TestUtil.firestore()) .collection("users") .where(and(not(exists(field("name"))), exists(field("age")))) - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc4) } @@ -534,7 +533,7 @@ internal class WhereTests { .where(or(not(exists(field("name"))), exists(field("age")))) // (NOT name exists) OR (age exists) // Matches: doc1, doc2, doc4, doc5 - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc2, doc4, doc5) } @@ -553,7 +552,7 @@ internal class WhereTests { .where(xor(not(exists(field("name"))), exists(field("age")))) // (NOT name exists AND NOT age exists) OR (name exists AND age exists) // Matches: doc1, doc2, doc5 - val result = runPipeline(pipeline, flowOf(*documents.toTypedArray())).toList() + val result = runPipeline(pipeline, listOf(*documents.toTypedArray())).toList() assertThat(result).containsExactly(doc1, doc2, doc5) } @@ -572,7 +571,7 @@ internal class WhereTests { RealtimePipelineSource(TestUtil.firestore()) .collection("users") .where(and(equalityArgument1, equalityArgument2)) - val resultAnd1 = runPipeline(pipelineAnd1, flowOf(*documents.toTypedArray())).toList() + val resultAnd1 = runPipeline(pipelineAnd1, listOf(*documents.toTypedArray())).toList() assertThat(resultAnd1).containsExactly(doc2) // Combined AND (reversed order) @@ -580,7 +579,7 @@ internal class WhereTests { RealtimePipelineSource(TestUtil.firestore()) .collection("users") .where(and(equalityArgument2, equalityArgument1)) - val resultAnd2 = runPipeline(pipelineAnd2, flowOf(*documents.toTypedArray())).toList() + val resultAnd2 = runPipeline(pipelineAnd2, listOf(*documents.toTypedArray())).toList() assertThat(resultAnd2).containsExactly(doc2) // Separate Stages @@ -589,7 +588,7 @@ internal class WhereTests { .collection("users") .where(equalityArgument1) .where(equalityArgument2) - val resultSep1 = runPipeline(pipelineSep1, flowOf(*documents.toTypedArray())).toList() + val resultSep1 = runPipeline(pipelineSep1, listOf(*documents.toTypedArray())).toList() assertThat(resultSep1).containsExactly(doc2) // Separate Stages (reversed order) @@ -598,7 +597,7 @@ internal class WhereTests { .collection("users") .where(equalityArgument2) .where(equalityArgument1) - val resultSep2 = runPipeline(pipelineSep2, flowOf(*documents.toTypedArray())).toList() + val resultSep2 = runPipeline(pipelineSep2, listOf(*documents.toTypedArray())).toList() assertThat(resultSep2).containsExactly(doc2) } } diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/testUtil.kt b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/testUtil.kt index 4af161a2600..2b1ef9062ba 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/testUtil.kt +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/pipeline/testUtil.kt @@ -31,7 +31,7 @@ internal val EVALUATION_CONTEXT: EvaluationContext = internal fun evaluate(expr: Expr): EvaluateResult = evaluate(expr, EMPTY_DOC) internal fun evaluate(expr: Expr, doc: MutableDocument): EvaluateResult { - val function = expr.evaluateContext(EVALUATION_CONTEXT) + val function = expr.evaluateFunction(EVALUATION_CONTEXT) return function(doc) } diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/testUtil.kt b/firebase-firestore/src/test/java/com/google/firebase/firestore/testUtil.kt index 15c4daafadc..27c689035f3 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/testUtil.kt +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/testUtil.kt @@ -15,16 +15,8 @@ package com.google.firebase.firestore import com.google.firebase.firestore.model.MutableDocument -import com.google.firebase.firestore.pipeline.EvaluationContext -import kotlinx.coroutines.flow.Flow internal fun runPipeline( pipeline: RealtimePipeline, - input: Flow -): Flow { - val rewrittenPipeline = pipeline.rewriteStages() - val context = EvaluationContext(rewrittenPipeline) - return rewrittenPipeline.stages.fold(input) { documentFlow, stage -> - stage.evaluate(context, documentFlow) - } -} + input: List +): List = pipeline.evaluate(input)