Skip to content

Test large documents. #15028

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Firestore/Example/Firestore.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
03AEB9E07A605AE1B5827548 /* field_index_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = BF76A8DA34B5B67B4DD74666 /* field_index_test.cc */; };
043C7B3DECB94F69F28BB798 /* Validation_BloomFilterTest_MD5_5000_01_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = 57F8EE51B5EFC9FAB185B66C /* Validation_BloomFilterTest_MD5_5000_01_bloom_filter_proto.json */; };
0455FC6E2A281BD755FD933A /* precondition_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA5520A36E1F00BCEB75 /* precondition_test.cc */; };
047F6D7CC8DA617E4113D648 /* LargeDocTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA8A97046A25F4B8AE21D542 /* LargeDocTests.swift */; };
04887E378B39FB86A8A5B52B /* leveldb_local_store_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5FF903AEFA7A3284660FA4C5 /* leveldb_local_store_test.cc */; };
048A55EED3241ABC28752F86 /* memory_mutation_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 74FBEFA4FE4B12C435011763 /* memory_mutation_queue_test.cc */; };
04D7D9DB95E66FECF2C0A412 /* bundle_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F7FC06E0A47D393DE1759AE1 /* bundle_cache_test.cc */; };
Expand Down Expand Up @@ -796,6 +797,7 @@
6A4F6B42C628D55CCE0C311F /* FIRQueryTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E069202154D500B64F25 /* FIRQueryTests.mm */; };
6A94393D83EB338DFAF6A0D2 /* pretty_printing_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB323F9553050F4F6490F9FF /* pretty_printing_test.cc */; };
6ABB82D43C0728EB095947AF /* geo_point_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB7BAB332012B519001E0872 /* geo_point_test.cc */; };
6AC95F65EE2E7A7DB27F227D /* LargeDocTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA8A97046A25F4B8AE21D542 /* LargeDocTests.swift */; };
6AED40FF444F0ACFE3AE96E3 /* target_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B5C37696557C81A6C2B7271A /* target_cache_test.cc */; };
6AF739DDA9D33DF756DE7CDE /* autoid_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54740A521FC913E500713A1A /* autoid_test.cc */; };
6B8E8B6C9EFDB3F1F91628A0 /* Validation_BloomFilterTest_MD5_5000_01_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = 57F8EE51B5EFC9FAB185B66C /* Validation_BloomFilterTest_MD5_5000_01_bloom_filter_proto.json */; };
Expand Down Expand Up @@ -1425,6 +1427,7 @@
D94A1862B8FB778225DB54A1 /* filesystem_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F51859B394D01C0C507282F1 /* filesystem_test.cc */; };
D98430EA4FAA357D855FA50F /* orderby_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A21F315EE100DD57A1 /* orderby_spec_test.json */; };
D98A0B6007E271E32299C79D /* FIRGeoPointTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E048202154AA00B64F25 /* FIRGeoPointTests.mm */; };
D9B76AABA2000B5B33E7011B /* LargeDocTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA8A97046A25F4B8AE21D542 /* LargeDocTests.swift */; };
D9DA467E7903412DC6AECDE4 /* grpc_connection_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D9649021544D4F00EB9CFB /* grpc_connection_test.cc */; };
D9EF7FC0E3F8646B272B427E /* FSTAPIHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04E202154AA00B64F25 /* FSTAPIHelpers.mm */; };
DA1D665B12AA1062DCDEA6BD /* async_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB467B208E9A8200554BA2 /* async_queue_test.cc */; };
Expand Down Expand Up @@ -2078,6 +2081,7 @@
C8582DFD74E8060C7072104B /* Validation_BloomFilterTest_MD5_5000_0001_membership_test_result.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_5000_0001_membership_test_result.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_5000_0001_membership_test_result.json; sourceTree = "<group>"; };
C8FB22BCB9F454DA44BA80C8 /* Validation_BloomFilterTest_MD5_50000_01_membership_test_result.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_50000_01_membership_test_result.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_50000_01_membership_test_result.json; sourceTree = "<group>"; };
C939D1789E38C09F9A0C1157 /* Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json; sourceTree = "<group>"; };
CA8A97046A25F4B8AE21D542 /* LargeDocTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = LargeDocTests.swift; sourceTree = "<group>"; };
CB7B2D4691C380DE3EB59038 /* lru_garbage_collector_test.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = lru_garbage_collector_test.h; sourceTree = "<group>"; };
CC572A9168BBEF7B83E4BBC5 /* view_snapshot_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = view_snapshot_test.cc; sourceTree = "<group>"; };
CCC9BD953F121B9E29F9AA42 /* user_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; name = user_test.cc; path = credentials/user_test.cc; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2272,6 +2276,7 @@
124C932B22C1642C00CA8C2D /* CodableIntegrationTests.swift */,
3355BE9391CC4857AF0BDAE3 /* DatabaseTests.swift */,
62E54B832A9E910A003347C8 /* IndexingTests.swift */,
CA8A97046A25F4B8AE21D542 /* LargeDocTests.swift */,
621D620928F9CE7400D2FA26 /* QueryIntegrationTests.swift */,
4D65F6E69993611D47DC8E7C /* SnapshotListenerSourceTests.swift */,
EFF22EA92C5060A4009A369B /* VectorIntegrationTests.swift */,
Expand Down Expand Up @@ -4649,6 +4654,7 @@
432056C4D1259F76C80FC2A8 /* FSTUserDataReaderTests.mm in Sources */,
3B1E27D951407FD237E64D07 /* FirestoreEncoderTests.swift in Sources */,
62E54B862A9E910B003347C8 /* IndexingTests.swift in Sources */,
047F6D7CC8DA617E4113D648 /* LargeDocTests.swift in Sources */,
621D620C28F9CE7400D2FA26 /* QueryIntegrationTests.swift in Sources */,
1CFBD4563960D8A20C4679A3 /* SnapshotListenerSourceTests.swift in Sources */,
EFF22EAC2C5060A4009A369B /* VectorIntegrationTests.swift in Sources */,
Expand Down Expand Up @@ -4896,6 +4902,7 @@
75A176239B37354588769206 /* FSTUserDataReaderTests.mm in Sources */,
5E89B1A5A5430713C79C4854 /* FirestoreEncoderTests.swift in Sources */,
62E54B852A9E910B003347C8 /* IndexingTests.swift in Sources */,
D9B76AABA2000B5B33E7011B /* LargeDocTests.swift in Sources */,
621D620B28F9CE7400D2FA26 /* QueryIntegrationTests.swift in Sources */,
A0BC30D482B0ABD1A3A24CDC /* SnapshotListenerSourceTests.swift in Sources */,
EFF22EAB2C5060A4009A369B /* VectorIntegrationTests.swift in Sources */,
Expand Down Expand Up @@ -5395,6 +5402,7 @@
F5BDECEB3B43BD1591EEADBD /* FSTUserDataReaderTests.mm in Sources */,
6F45846C159D3C063DBD3CBE /* FirestoreEncoderTests.swift in Sources */,
62E54B842A9E910B003347C8 /* IndexingTests.swift in Sources */,
6AC95F65EE2E7A7DB27F227D /* LargeDocTests.swift in Sources */,
621D620A28F9CE7400D2FA26 /* QueryIntegrationTests.swift in Sources */,
B00F8D1819EE20C45B660940 /* SnapshotListenerSourceTests.swift in Sources */,
EFF22EAA2C5060A4009A369B /* VectorIntegrationTests.swift in Sources */,
Expand Down
162 changes: 162 additions & 0 deletions Firestore/Swift/Tests/Integration/LargeDocTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*
* 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.
*/

import Combine
import FirebaseFirestore
import Foundation

// iOS 15 required for test implementation, not BSON types
@available(iOS 15, tvOS 15, macOS 12.0, macCatalyst 13, watchOS 7, *)
class LargeDocIntegrationTests: FSTIntegrationTestCase {
/**
* Returns a dictionary containing a Data object (blob) with a size approaching
* the maximum allowed in a Firestore document.
*/
func getLargestDocContent() -> [String: Any] {
let maxBytesPerFieldValue = 1_048_487

// Subtract 8 for '__name__', 20 for its value, and 4 for 'blob'.
let numBytesToUse = maxBytesPerFieldValue - 8 - 20 - 4

// Create a buffer to hold the random bytes.
var bytes = [UInt8](repeating: 0, count: numBytesToUse)

// Fill the buffer with random bytes.
_ = SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes)
let blobData = Data(bytes)

return ["blob": blobData]
}

func testCanCRUDAndQueryLargeDocuments() async throws {
let collRef = collectionRef()
let docRef = collRef.document()
let data = getLargestDocContent()

// Set
try await docRef.setData(data)

// Get
var snapshot = try await docRef.getDocument()
XCTAssertEqual(snapshot.data() as? NSDictionary, data as NSDictionary)

// Update
let newData = getLargestDocContent()
try await docRef.updateData(newData)
snapshot = try await docRef.getDocument()
XCTAssertEqual(snapshot.data() as? NSDictionary, newData as NSDictionary)

// Query
let querySnapshot = try await collRef.getDocuments()
XCTAssertEqual(querySnapshot.count, 1)
XCTAssertEqual(querySnapshot.documents.first?.data() as? NSDictionary, newData as NSDictionary)

// Delete
try await docRef.delete()
snapshot = try await docRef.getDocument()
XCTAssertFalse(snapshot.exists)
}

func testCanCRUDLargeDocumentsInsideTransaction() async throws {
let collRef = collectionRef()
let docRef1 = collRef.document()
let docRef2 = collRef.document()
let docRef3 = collRef.document()
let data = getLargestDocContent()
let newData = getLargestDocContent()

try await docRef1.setData(data)
try await docRef3.setData(data)

_ = try await collRef.firestore.runTransaction { transaction, err -> Any? in
do {
// Get and update
let snapshot = try transaction.getDocument(docRef1)
XCTAssertEqual(snapshot.data() as? NSDictionary, data as NSDictionary)
transaction.updateData(newData, forDocument: docRef1)

// Set
transaction.setData(data, forDocument: docRef2)

// Delete
transaction.deleteDocument(docRef3)

} catch let fetchError as NSError {
err?.pointee = fetchError
return nil
}
return nil
}

// Verification
var snapshot = try await docRef1.getDocument()
XCTAssertEqual(snapshot.data() as? NSDictionary, newData as NSDictionary)

snapshot = try await docRef2.getDocument()
XCTAssertEqual(snapshot.data() as? NSDictionary, data as NSDictionary)

snapshot = try await docRef3.getDocument()
XCTAssertFalse(snapshot.exists)
}

func testListenToLargeQuerySnapshot() throws {
let collRef = collectionRef()
let data = getLargestDocContent()

writeDocumentRef(collRef.document(), data: data)

// Fulfill an expectation when the listener receives its first snapshot
let expectation = self.expectation(description: "Query snapshot listener received data")
var querySnapshot: QuerySnapshot?

let registration = collRef.addSnapshotListener { snapshot, error in
XCTAssertNil(error, "Listener returned an error")
querySnapshot = snapshot!
expectation.fulfill()
}

// Wait for the expectation to be fulfilled
waitForExpectations(timeout: 5.0)
registration.remove()

XCTAssertEqual(querySnapshot!.documents.count, 1)
XCTAssertEqual(querySnapshot!.documents.first?.data() as? NSDictionary, data as NSDictionary)
}

func testListenToLargeDocumentSnapshot() throws {
let docRef = collectionRef().document()
let data = getLargestDocContent()

writeDocumentRef(docRef, data: data)

// Fulfill an expectation when the listener receives its first snapshot
let expectation = self.expectation(description: "Document snapshot listener received data")
var documentSnapshot: DocumentSnapshot?

let registration = docRef.addSnapshotListener { snapshot, error in
XCTAssertNil(error, "Listener returned an error")
documentSnapshot = snapshot
expectation.fulfill()
}

// Wait for the expectation to be fulfilled
waitForExpectations(timeout: 5.0)
registration.remove()

XCTAssertTrue(documentSnapshot!.exists)
XCTAssertEqual(documentSnapshot!.data() as? NSDictionary, data as NSDictionary)
}
}
Loading