From 2cfeeb554dcb781a2f0efca0c4a9e49d25fd4d3c Mon Sep 17 00:00:00 2001 From: Pasin Suriyentrakorn Date: Tue, 25 Aug 2026 19:22:00 -0700 Subject: [PATCH 1/2] CBL-8744 : Add binary test targets for the Swift test suites Restructure Swift tests for running the tests against a pre-built CouchbaseLiteSwift binary instead of a build from the working tree, following the same design as the Objective-C binary test targets. - Add CBL_EE_Swift_Binary_Tests and CBL_Swift_Binary_Tests, which compile and link against frameworks staged in BinaryTests/Frameworks. Each runs on macOS and iOS. The community target leaves out the tests for Enterprise edition. - Remove @testable imports from the test files. Most were unused. The files that keep white box tests import the module conditionally, as a binary framework has no testability, and guard those tests with #if !CBL_BINARY_TEST. - Keep MultipeerReplicatorTest internal only, as its identity setup requires the internal issuer API. - Replace the internal keychain test API in the listener tests with public SecItem based cleanup helpers, and derive the anonymous identity common name at runtime in the same way as the Objective-C tests. The internal API is deleted in the EE repo. - Align the keychain item count helper between the Objective-C and Swift tests. Both take the item class and the discovered common name as arguments. - Delete testJSONRepresentation which uses an internal API and is not useful. - Add testCleanUpAnonymousIdentities to verify that the cleanup leaves no anonymous identities nor certificates in the keychain. - Convert the keychain guard early returns to XCTSkipUnless so that runs without keychain access report skips instead of passing vacuously. - Set CBL_BINARY_TEST in the targets' xcconfigs via SWIFT_ACTIVE_COMPILATION_CONDITIONS and GCC_PREPROCESSOR_DEFINITIONS. - Rename the shared iOS host application target to CBL_Binary_Tests_iOS_App, as it now hosts the binary tests of both languages. - Add a scheme per edition and platform, following the Objective-C binary test schemes. --- CouchbaseLite.xcodeproj/project.pbxproj | 396 +++++++++++++++++- .../CBL_EE_ObjC_Binary_Tests_iOS_App.xcscheme | 2 +- .../CBL_EE_Swift_Binary_Tests.xcscheme | 55 +++ ...CBL_EE_Swift_Binary_Tests_iOS_App.xcscheme | 85 ++++ .../CBL_ObjC_Binary_Tests_iOS_App.xcscheme | 2 +- .../xcschemes/CBL_Swift_Binary_Tests.xcscheme | 55 +++ .../CBL_Swift_Binary_Tests_iOS_App.xcscheme | 85 ++++ .../Tests/URLEndpointListenerTest+Main.m | 19 +- .../CouchbaseLiteSwift-EE.private.txt | 1 - .../CouchbaseLiteSwift-EE.private.modulemap | 1 - Swift/Query.swift | 17 - Swift/Tests/CBLTestCase.swift | 2 +- Swift/Tests/CBLTestHelper.h | 3 - Swift/Tests/CBLTestHelper.m | 20 +- Swift/Tests/CollectionTest.swift | 2 +- Swift/Tests/DatabaseTest.swift | 2 +- Swift/Tests/LogSinkTest.swift | 2 +- Swift/Tests/MultipeerReplicatorTest.swift | 6 + Swift/Tests/PartialIndexTest.swift | 2 +- Swift/Tests/PublisherTest.swift | 2 +- Swift/Tests/QueryTest+Collection.swift | 2 +- Swift/Tests/QueryTest.swift | 21 +- Swift/Tests/ReplicatorTest+Collection.swift | 2 +- .../Tests/ReplicatorTest+CustomConflict.swift | 2 +- .../Tests/ReplicatorTest+PendingDocIds.swift | 2 +- Swift/Tests/TLSIdentityTest.swift | 15 + .../URLEndpointListenerTest+Collection.swift | 8 +- Swift/Tests/URLEndpointListenerTest.swift | 234 +++++++++-- Swift/Tests/UnnestArrayTest.swift | 16 + Swift/Tests/VectorSearchTest+Lazy.swift | 2 +- Swift/Tests/VectorSearchTest.swift | 2 +- ...nfig => CBL_Binary_Tests_iOS_App.xcconfig} | 2 +- xcconfigs/CBL_EE_Swift_Binary_Tests.xcconfig | 21 + xcconfigs/CBL_Swift_Binary_Tests.xcconfig | 71 ++++ 34 files changed, 1015 insertions(+), 144 deletions(-) create mode 100644 CouchbaseLite.xcodeproj/xcshareddata/xcschemes/CBL_EE_Swift_Binary_Tests.xcscheme create mode 100644 CouchbaseLite.xcodeproj/xcshareddata/xcschemes/CBL_EE_Swift_Binary_Tests_iOS_App.xcscheme create mode 100644 CouchbaseLite.xcodeproj/xcshareddata/xcschemes/CBL_Swift_Binary_Tests.xcscheme create mode 100644 CouchbaseLite.xcodeproj/xcshareddata/xcschemes/CBL_Swift_Binary_Tests_iOS_App.xcscheme rename xcconfigs/{CBL_ObjC_Binary_Tests_iOS_App.xcconfig => CBL_Binary_Tests_iOS_App.xcconfig} (97%) create mode 100644 xcconfigs/CBL_EE_Swift_Binary_Tests.xcconfig create mode 100644 xcconfigs/CBL_Swift_Binary_Tests.xcconfig diff --git a/CouchbaseLite.xcodeproj/project.pbxproj b/CouchbaseLite.xcodeproj/project.pbxproj index 95457725a..ac9664270 100644 --- a/CouchbaseLite.xcodeproj/project.pbxproj +++ b/CouchbaseLite.xcodeproj/project.pbxproj @@ -588,6 +588,71 @@ 40E46B212DD6A850007E495D /* CBLDNSService.h in Headers */ = {isa = PBXBuildFile; fileRef = 69ABB5DB2976A5CA00DA0229 /* CBLDNSService.h */; }; 40E46B222DD6A850007E495D /* CBLDNSService.h in Headers */ = {isa = PBXBuildFile; fileRef = 69ABB5DB2976A5CA00DA0229 /* CBLDNSService.h */; }; 40E46B232DD6A905007E495D /* libEnterpriseBits.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 40E46AC12DD6A42B007E495D /* libEnterpriseBits.a */; }; + 40E92C40303E4282005C088C /* CouchbaseLiteVectorSearch.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 40CC4BE3302D86A70002386D /* CouchbaseLiteVectorSearch.xcframework */; }; + 40E92C43303E4282005C088C /* Support in Resources */ = {isa = PBXBuildFile; fileRef = 93DECF3E200DBE5800F44953 /* Support */; }; + 40E92C45303E4282005C088C /* CouchbaseLiteVectorSearch.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 40CC4BE3302D86A70002386D /* CouchbaseLiteVectorSearch.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 40E92C4E303E444D005C088C /* DatabaseTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27BE3B4C1E4E51C80012B74A /* DatabaseTest.swift */; }; + 40E92C4F303E444D005C088C /* ReplicatorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93DB80021ED8FE5100C4F845 /* ReplicatorTest.swift */; }; + 40E92C50303E444D005C088C /* LogSinkTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40938A0B2D4AFA5900691393 /* LogSinkTest.swift */; }; + 40E92C51303E444D005C088C /* URLEndpointListenerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 934608EE247F35CC00CF2F27 /* URLEndpointListenerTest.swift */; }; + 40E92C52303E444D005C088C /* URLEndpointListenerTest+Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A13DD3328B8809300BC1084 /* URLEndpointListenerTest+Collection.swift */; }; + 40E92C53303E444D005C088C /* CollectionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A6084EB2875842A0037C66F /* CollectionTest.swift */; }; + 40E92C54303E444D005C088C /* FragmentTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93F74A8A1EC5549C001289F8 /* FragmentTest.swift */; }; + 40E92C55303E444D005C088C /* PublisherTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE9D61A02D6F453C00A6EB82 /* PublisherTest.swift */; }; + 40E92C56303E444D005C088C /* ReplicatorTest+CustomConflict.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AA3D6B022A1A3490098E16B /* ReplicatorTest+CustomConflict.swift */; }; + 40E92C57303E444D005C088C /* TestCustomLogSink.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEA6C1662E721DAC00A0B8BA /* TestCustomLogSink.swift */; }; + 40E92C58303E444D005C088C /* QueryTest+Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8F84E72893F187008C4333 /* QueryTest+Collection.swift */; }; + 40E92C5A303E444D005C088C /* DocumentExpirationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93C50E9F21BDFB7C00C7E980 /* DocumentExpirationTest.swift */; }; + 40E92C5B303E444D005C088C /* DictionaryTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93C3985E1EC66A89005A7A96 /* DictionaryTest.swift */; }; + 40E92C5C303E444D005C088C /* NotificationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93E17F141ED4ED4000671CA1 /* NotificationTest.swift */; }; + 40E92C5D303E444D005C088C /* QueryTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93A91C6B1E81CE4D003D01A2 /* QueryTest.swift */; }; + 40E92C5E303E444D005C088C /* PredictiveQueryTest+CoreML.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93AE16E2221DC92E00539C05 /* PredictiveQueryTest+CoreML.swift */; }; + 40E92C5F303E444D005C088C /* UnnestArrayTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = AECA897C2CADADAA00C7B6BE /* UnnestArrayTest.swift */; }; + 40E92C60303E444D005C088C /* ReplicatorTest+PendingDocIds.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A690CC824214EE70084D017 /* ReplicatorTest+PendingDocIds.swift */; }; + 40E92C61303E444D005C088C /* TLSIdentityTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93095B23246CF1DE005633B4 /* TLSIdentityTest.swift */; }; + 40E92C62303E444D005C088C /* DateTimeQueryFunctionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8DD7D921C9876E00741C47 /* DateTimeQueryFunctionTest.swift */; }; + 40E92C63303E444D005C088C /* ReplicatorTest+Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AC754622897A9C9006CF48F /* ReplicatorTest+Collection.swift */; }; + 40E92C64303E444D005C088C /* VectorSearchTest+Lazy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 406F8DFC2C27F097000223FC /* VectorSearchTest+Lazy.swift */; }; + 40E92C65303E444D005C088C /* CodableTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7999862D70C624002C8D71 /* CodableTest.swift */; }; + 40E92C66303E444D005C088C /* PredictiveQueryTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93EB25A321CDCC160006FB88 /* PredictiveQueryTest.swift */; }; + 40E92C67303E444D005C088C /* DocumentTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27BE3B4E1E4E65EB0012B74A /* DocumentTest.swift */; }; + 40E92C68303E444D005C088C /* VectorSearchTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE5803A32B99C67D001A1BE3 /* VectorSearchTest.swift */; }; + 40E92C69303E444D005C088C /* CBLTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27BE3B4A1E4E46120012B74A /* CBLTestCase.swift */; }; + 40E92C6A303E444D005C088C /* PartialIndexTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEFEED792D4AB994008AF4C2 /* PartialIndexTest.swift */; }; + 40E92C6B303E444D005C088C /* DatabaseEncryptionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9369A6B6207DEB5F009B5B83 /* DatabaseEncryptionTest.swift */; }; + 40E92C6D303E444D005C088C /* ArrayTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93629CFF1EC96D9500F79834 /* ArrayTest.swift */; }; + 40E92C6E303E448D005C088C /* CBLTestHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 934C2CDD1F4FC0D20010316F /* CBLTestHelper.m */; }; + 40E92C6F303E4498005C088C /* WordEmbeddingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE5803D72B9B5B2A001A1BE3 /* WordEmbeddingModel.swift */; }; + 40E92C72303E4506005C088C /* CouchbaseLiteSwift.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 40E92C70303E44E5005C088C /* CouchbaseLiteSwift.xcframework */; }; + 40E92C73303E4506005C088C /* CouchbaseLiteSwift.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 40E92C70303E44E5005C088C /* CouchbaseLiteSwift.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 40E92C79303E45A3005C088C /* DatabaseTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27BE3B4C1E4E51C80012B74A /* DatabaseTest.swift */; }; + 40E92C7A303E45A3005C088C /* ReplicatorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93DB80021ED8FE5100C4F845 /* ReplicatorTest.swift */; }; + 40E92C7B303E45A3005C088C /* LogSinkTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40938A0B2D4AFA5900691393 /* LogSinkTest.swift */; }; + 40E92C7C303E45A3005C088C /* CBLTestHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 934C2CDD1F4FC0D20010316F /* CBLTestHelper.m */; }; + 40E92C7F303E45A3005C088C /* CollectionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A6084EB2875842A0037C66F /* CollectionTest.swift */; }; + 40E92C80303E45A3005C088C /* FragmentTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93F74A8A1EC5549C001289F8 /* FragmentTest.swift */; }; + 40E92C81303E45A3005C088C /* PublisherTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE9D61A02D6F453C00A6EB82 /* PublisherTest.swift */; }; + 40E92C82303E45A3005C088C /* ReplicatorTest+CustomConflict.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AA3D6B022A1A3490098E16B /* ReplicatorTest+CustomConflict.swift */; }; + 40E92C83303E45A3005C088C /* TestCustomLogSink.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEA6C1662E721DAC00A0B8BA /* TestCustomLogSink.swift */; }; + 40E92C84303E45A3005C088C /* QueryTest+Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8F84E72893F187008C4333 /* QueryTest+Collection.swift */; }; + 40E92C85303E45A3005C088C /* DocumentExpirationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93C50E9F21BDFB7C00C7E980 /* DocumentExpirationTest.swift */; }; + 40E92C86303E45A3005C088C /* DictionaryTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93C3985E1EC66A89005A7A96 /* DictionaryTest.swift */; }; + 40E92C87303E45A3005C088C /* NotificationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93E17F141ED4ED4000671CA1 /* NotificationTest.swift */; }; + 40E92C88303E45A3005C088C /* QueryTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93A91C6B1E81CE4D003D01A2 /* QueryTest.swift */; }; + 40E92C8A303E45A3005C088C /* UnnestArrayTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = AECA897C2CADADAA00C7B6BE /* UnnestArrayTest.swift */; }; + 40E92C8B303E45A3005C088C /* ReplicatorTest+PendingDocIds.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A690CC824214EE70084D017 /* ReplicatorTest+PendingDocIds.swift */; }; + 40E92C8D303E45A3005C088C /* DateTimeQueryFunctionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8DD7D921C9876E00741C47 /* DateTimeQueryFunctionTest.swift */; }; + 40E92C8E303E45A3005C088C /* ReplicatorTest+Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AC754622897A9C9006CF48F /* ReplicatorTest+Collection.swift */; }; + 40E92C90303E45A3005C088C /* CodableTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7999862D70C624002C8D71 /* CodableTest.swift */; }; + 40E92C92303E45A3005C088C /* DocumentTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27BE3B4E1E4E65EB0012B74A /* DocumentTest.swift */; }; + 40E92C94303E45A3005C088C /* CBLTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27BE3B4A1E4E46120012B74A /* CBLTestCase.swift */; }; + 40E92C95303E45A3005C088C /* PartialIndexTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEFEED792D4AB994008AF4C2 /* PartialIndexTest.swift */; }; + 40E92C97303E45A3005C088C /* ArrayTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93629CFF1EC96D9500F79834 /* ArrayTest.swift */; }; + 40E92C9A303E45A3005C088C /* CouchbaseLiteVectorSearch.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 40CC4BE3302D86A70002386D /* CouchbaseLiteVectorSearch.xcframework */; }; + 40E92C9B303E45A3005C088C /* CouchbaseLiteSwift.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 40E92C70303E44E5005C088C /* CouchbaseLiteSwift.xcframework */; }; + 40E92C9D303E45A3005C088C /* Support in Resources */ = {isa = PBXBuildFile; fileRef = 93DECF3E200DBE5800F44953 /* Support */; }; + 40E92C9F303E45A3005C088C /* CouchbaseLiteVectorSearch.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 40CC4BE3302D86A70002386D /* CouchbaseLiteVectorSearch.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 40E92CA0303E45A3005C088C /* CouchbaseLiteSwift.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 40E92C70303E44E5005C088C /* CouchbaseLiteSwift.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 40ECAE862E0E08CC00C109A6 /* Precondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40ECAE852E0E08CC00C109A6 /* Precondition.swift */; }; 40ECAE872E0E08CC00C109A6 /* Precondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40ECAE852E0E08CC00C109A6 /* Precondition.swift */; }; 40ECAE8A2E0E0B0F00C109A6 /* CBLPrecondition.m in Sources */ = {isa = PBXBuildFile; fileRef = 40ECAE892E0E0B0F00C109A6 /* CBLPrecondition.m */; }; @@ -709,14 +774,12 @@ 40FC1C072B928AC400394276 /* CBLEncryptionKey+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 40FC1AB72B9286E700394276 /* CBLEncryptionKey+Internal.h */; }; 40FC1C082B928AC400394276 /* CBLDatabase+EncryptionInternal.mm in Sources */ = {isa = PBXBuildFile; fileRef = 40FC1AB82B9286E700394276 /* CBLDatabase+EncryptionInternal.mm */; }; 40FC1C092B928ADC00394276 /* CBLURLEndpointListener+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 40FC1ABB2B9286E700394276 /* CBLURLEndpointListener+Internal.h */; }; - 40FC1C0A2B928ADC00394276 /* CBLURLEndpointListener+Swift.h in Headers */ = {isa = PBXBuildFile; fileRef = 40FC1ABC2B9286E700394276 /* CBLURLEndpointListener+Swift.h */; }; 40FC1C0B2B928ADC00394276 /* CBLListenerPasswordAuthenticator+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 40FC1ABE2B9286E700394276 /* CBLListenerPasswordAuthenticator+Internal.h */; }; 40FC1C0C2B928ADC00394276 /* CBLTLSIdentity+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 40FC1ABA2B9286E700394276 /* CBLTLSIdentity+Internal.h */; }; 40FC1C0D2B928ADC00394276 /* CBLTLSIdentity+Swift.h in Headers */ = {isa = PBXBuildFile; fileRef = 40FC1ABD2B9286E700394276 /* CBLTLSIdentity+Swift.h */; }; 40FC1C0E2B928ADC00394276 /* CBLURLEndpointListenerConfiguration+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 40FC1ABF2B9286E700394276 /* CBLURLEndpointListenerConfiguration+Internal.h */; }; 40FC1C0F2B928ADC00394276 /* CBLListenerCertificateAuthenticator+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 40FC1AC02B9286E700394276 /* CBLListenerCertificateAuthenticator+Internal.h */; }; 40FC1C102B928ADD00394276 /* CBLURLEndpointListener+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 40FC1ABB2B9286E700394276 /* CBLURLEndpointListener+Internal.h */; }; - 40FC1C112B928ADD00394276 /* CBLURLEndpointListener+Swift.h in Headers */ = {isa = PBXBuildFile; fileRef = 40FC1ABC2B9286E700394276 /* CBLURLEndpointListener+Swift.h */; settings = {ATTRIBUTES = (Private, ); }; }; 40FC1C122B928ADD00394276 /* CBLListenerPasswordAuthenticator+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 40FC1ABE2B9286E700394276 /* CBLListenerPasswordAuthenticator+Internal.h */; }; 40FC1C132B928ADD00394276 /* CBLTLSIdentity+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 40FC1ABA2B9286E700394276 /* CBLTLSIdentity+Internal.h */; }; 40FC1C142B928ADD00394276 /* CBLTLSIdentity+Swift.h in Headers */ = {isa = PBXBuildFile; fileRef = 40FC1ABD2B9286E700394276 /* CBLTLSIdentity+Swift.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -2333,6 +2396,30 @@ name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; + 40E92C44303E4282005C088C /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 40E92C45303E4282005C088C /* CouchbaseLiteVectorSearch.xcframework in Embed Frameworks */, + 40E92C73303E4506005C088C /* CouchbaseLiteSwift.xcframework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; + 40E92C9E303E45A3005C088C /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 40E92C9F303E45A3005C088C /* CouchbaseLiteVectorSearch.xcframework in Embed Frameworks */, + 40E92CA0303E45A3005C088C /* CouchbaseLiteSwift.xcframework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; 93095B0A246BC325005633B4 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -2587,7 +2674,7 @@ 40CC4BE1302D86A30002386D /* CouchbaseLite.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = CouchbaseLite.xcframework; path = BinaryTests/Frameworks/CouchbaseLite.xcframework; sourceTree = ""; }; 40CC4BE3302D86A70002386D /* CouchbaseLiteVectorSearch.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = CouchbaseLiteVectorSearch.xcframework; path = BinaryTests/Frameworks/CouchbaseLiteVectorSearch.xcframework; sourceTree = ""; }; 40CC4C4E3033CE540002386D /* CBL_Binary_Tests.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CBL_Binary_Tests.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 40CC4CAD3033EEF90002386D /* CBL_ObjC_Binary_Tests_iOS_App.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = CBL_ObjC_Binary_Tests_iOS_App.xcconfig; sourceTree = ""; }; + 40CC4CAD3033EEF90002386D /* CBL_Binary_Tests_iOS_App.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = CBL_Binary_Tests_iOS_App.xcconfig; sourceTree = ""; }; 40CC4D38303403F30002386D /* CouchbaseLiteBinaryTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CouchbaseLiteBinaryTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 40D6BCBF2DDD183000F209D7 /* MultipeerReplicatorConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultipeerReplicatorConfiguration.swift; sourceTree = ""; }; 40D6BCC12DDD191C00F209D7 /* MultipeerCertificateAuthenticator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultipeerCertificateAuthenticator.swift; sourceTree = ""; }; @@ -2624,6 +2711,11 @@ 40E46B122DD6A7B5007E495D /* CBLReplicatorStatus+Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CBLReplicatorStatus+Internal.h"; sourceTree = ""; }; 40E46B172DD6A808007E495D /* CBLConflictResolverService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CBLConflictResolverService.h; sourceTree = ""; }; 40E46B182DD6A808007E495D /* CBLConflictResolverService.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CBLConflictResolverService.m; sourceTree = ""; }; + 40E92C4C303E4282005C088C /* CBL_EE_Swift_Binary_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CouchbaseLiteSwiftBinaryTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 40E92C4D303E42FA005C088C /* CBL_EE_Swift_Binary_Tests.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = CBL_EE_Swift_Binary_Tests.xcconfig; sourceTree = ""; }; + 40E92C70303E44E5005C088C /* CouchbaseLiteSwift.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = CouchbaseLiteSwift.xcframework; path = BinaryTests/Frameworks/CouchbaseLiteSwift.xcframework; sourceTree = ""; }; + 40E92CA6303E45A3005C088C /* CBL_Swift_Binary_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CouchbaseLiteSwiftBinaryTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 40E92CA7303E4635005C088C /* CBL_Swift_Binary_Tests.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = CBL_Swift_Binary_Tests.xcconfig; sourceTree = ""; }; 40ECAE852E0E08CC00C109A6 /* Precondition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Precondition.swift; sourceTree = ""; }; 40ECAE882E0E0B0F00C109A6 /* CBLPrecondition.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CBLPrecondition.h; sourceTree = ""; }; 40ECAE892E0E0B0F00C109A6 /* CBLPrecondition.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CBLPrecondition.m; sourceTree = ""; }; @@ -2651,7 +2743,6 @@ 40FC1AB82B9286E700394276 /* CBLDatabase+EncryptionInternal.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "CBLDatabase+EncryptionInternal.mm"; sourceTree = ""; }; 40FC1ABA2B9286E700394276 /* CBLTLSIdentity+Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CBLTLSIdentity+Internal.h"; sourceTree = ""; }; 40FC1ABB2B9286E700394276 /* CBLURLEndpointListener+Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CBLURLEndpointListener+Internal.h"; sourceTree = ""; }; - 40FC1ABC2B9286E700394276 /* CBLURLEndpointListener+Swift.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CBLURLEndpointListener+Swift.h"; sourceTree = ""; }; 40FC1ABD2B9286E700394276 /* CBLTLSIdentity+Swift.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CBLTLSIdentity+Swift.h"; sourceTree = ""; }; 40FC1ABE2B9286E700394276 /* CBLListenerPasswordAuthenticator+Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CBLListenerPasswordAuthenticator+Internal.h"; sourceTree = ""; }; 40FC1ABF2B9286E700394276 /* CBLURLEndpointListenerConfiguration+Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CBLURLEndpointListenerConfiguration+Internal.h"; sourceTree = ""; }; @@ -3194,6 +3285,24 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 40E92C3F303E4282005C088C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 40E92C40303E4282005C088C /* CouchbaseLiteVectorSearch.xcframework in Frameworks */, + 40E92C72303E4506005C088C /* CouchbaseLiteSwift.xcframework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 40E92C99303E45A3005C088C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 40E92C9A303E45A3005C088C /* CouchbaseLiteVectorSearch.xcframework in Frameworks */, + 40E92C9B303E45A3005C088C /* CouchbaseLiteSwift.xcframework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 930B63CD246B9CD1006D94FF /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -3476,6 +3585,7 @@ 27EF6AF01E32D12C004748DF /* Frameworks */ = { isa = PBXGroup; children = ( + 40E92C70303E44E5005C088C /* CouchbaseLiteSwift.xcframework */, 40CC4BE3302D86A70002386D /* CouchbaseLiteVectorSearch.xcframework */, 40CC4BE1302D86A30002386D /* CouchbaseLite.xcframework */, 40815FB32F1058C3004D8590 /* CoreBluetooth.framework */, @@ -3698,7 +3808,6 @@ 40FC1ABA2B9286E700394276 /* CBLTLSIdentity+Internal.h */, 40FC1ABD2B9286E700394276 /* CBLTLSIdentity+Swift.h */, 40FC1ABB2B9286E700394276 /* CBLURLEndpointListener+Internal.h */, - 40FC1ABC2B9286E700394276 /* CBLURLEndpointListener+Swift.h */, 40FC1ABF2B9286E700394276 /* CBLURLEndpointListenerConfiguration+Internal.h */, ); path = Listener; @@ -4182,6 +4291,7 @@ 930B63FF246BA5F8006D94FF /* CBL_Swift_Tests.xcconfig */, 93BB1C7A246BAFBB004FFA00 /* CBL_Swift_Tests_iOS.xcconfig */, 93BB1C7B246BB145004FFA00 /* CBL_Swift_Tests_iOS_App.xcconfig */, + 40E92CA7303E4635005C088C /* CBL_Swift_Binary_Tests.xcconfig */, 4006AB492B9047320036E66D /* CBL_EE_Common.xcconfig */, 4006AB472B9046980036E66D /* CBL_EE_ObjC.xcconfig */, 4006AB482B9046CC0036E66D /* CBL_EE_Swift.xcconfig */, @@ -4189,10 +4299,11 @@ 932C69DD2453636100C382FB /* CBL_EE_ObjC_Tests_iOS.xcconfig */, 932C69DC2453636100C382FB /* CBL_EE_ObjC_Tests_iOS_App.xcconfig */, 40CC4B9A302C31BD0002386D /* CBL_EE_ObjC_Binary_Tests.xcconfig */, - 40CC4CAD3033EEF90002386D /* CBL_ObjC_Binary_Tests_iOS_App.xcconfig */, + 40CC4CAD3033EEF90002386D /* CBL_Binary_Tests_iOS_App.xcconfig */, 4006AB4C2B9048930036E66D /* CBL_EE_Swift_Tests.xcconfig */, 93BB1C79246BAF89004FFA00 /* CBL_EE_Swift_Tests_iOS.xcconfig */, 93BB1C7C246BB151004FFA00 /* CBL_EE_Swift_Tests_iOS_App.xcconfig */, + 40E92C4D303E42FA005C088C /* CBL_EE_Swift_Binary_Tests.xcconfig */, 40600A792DD7D6E500E696B7 /* CBL_OS_Target_Versions.xcconfig */, 9378C5D91E26CC46001BB196 /* Project_Debug.xcconfig */, 9378C5DA1E26CC46001BB196 /* Project_Release.xcconfig */, @@ -4479,6 +4590,8 @@ 40CC4B80302C2FF90002386D /* CouchbaseLiteBinaryTests.xctest */, 40CC4C4E3033CE540002386D /* CBL_Binary_Tests.app */, 40CC4D38303403F30002386D /* CouchbaseLiteBinaryTests.xctest */, + 40E92C4C303E4282005C088C /* CBL_EE_Swift_Binary_Tests.xctest */, + 40E92CA6303E45A3005C088C /* CBL_Swift_Binary_Tests.xctest */, ); name = Products; sourceTree = ""; @@ -5118,7 +5231,6 @@ 9343EFC1207D611600F19A89 /* CBLQueryParameters.h in Headers */, 1AAFB676284A263C00878453 /* CBLQueryFactory.h in Headers */, 40FC1C222B928B5000394276 /* CBLPrediction+Swift.h in Headers */, - 40FC1C0A2B928ADC00394276 /* CBLURLEndpointListener+Swift.h in Headers */, 9343EFC2207D611600F19A89 /* CBLVersion.h in Headers */, 40FC1C192B928B5000394276 /* CBLCoreMLPredictiveModel+Internal.h in Headers */, AE5CA6AD2E5E2584003A9E89 /* CBLMessageEndpointListenerConfiguration+Internal.h in Headers */, @@ -5330,7 +5442,6 @@ 933F83A621F9819B0093EC88 /* CBLDatabase+Swift.h in Headers */, 9343F0DC207D61AB00F19A89 /* CBLReplicatorChange.h in Headers */, 40FC1B692B9287BD00394276 /* CBLURLEndpointListener.h in Headers */, - 40FC1C112B928ADD00394276 /* CBLURLEndpointListener+Swift.h in Headers */, 9343F0DD207D61AB00F19A89 /* CBLArray+Swift.h in Headers */, 9343F0DE207D61AB00F19A89 /* CBLListenerToken.h in Headers */, 9343F0DF207D61AB00F19A89 /* CBLQueryOrdering.h in Headers */, @@ -5755,9 +5866,9 @@ productReference = 40CC4B80302C2FF90002386D /* CouchbaseLiteBinaryTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; - 40CC4C4D3033CE540002386D /* CBL_ObjC_Binary_Tests_iOS_App */ = { + 40CC4C4D3033CE540002386D /* CBL_Binary_Tests_iOS_App */ = { isa = PBXNativeTarget; - buildConfigurationList = 40CC4C653033CE550002386D /* Build configuration list for PBXNativeTarget "CBL_ObjC_Binary_Tests_iOS_App" */; + buildConfigurationList = 40CC4C653033CE550002386D /* Build configuration list for PBXNativeTarget "CBL_Binary_Tests_iOS_App" */; buildPhases = ( 40CC4C4A3033CE540002386D /* Sources */, 40CC4C4B3033CE540002386D /* Frameworks */, @@ -5767,10 +5878,10 @@ ); dependencies = ( ); - name = CBL_ObjC_Binary_Tests_iOS_App; + name = CBL_Binary_Tests_iOS_App; packageProductDependencies = ( ); - productName = CBL_ObjC_Binary_Tests_iOS_App; + productName = CBL_Binary_Tests_iOS_App; productReference = 40CC4C4E3033CE540002386D /* CBL_Binary_Tests.app */; productType = "com.apple.product-type.application"; }; @@ -5795,6 +5906,48 @@ productReference = 40CC4D38303403F30002386D /* CouchbaseLiteBinaryTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + 40E92C11303E4282005C088C /* CBL_EE_Swift_Binary_Tests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 40E92C47303E4282005C088C /* Build configuration list for PBXNativeTarget "CBL_EE_Swift_Binary_Tests" */; + buildPhases = ( + 40E92C12303E4282005C088C /* Check Binary Frameworks */, + 40E92C13303E4282005C088C /* Sources */, + 40E92C3F303E4282005C088C /* Frameworks */, + 40E92C42303E4282005C088C /* Resources */, + 40E92C44303E4282005C088C /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = CBL_EE_Swift_Binary_Tests; + packageProductDependencies = ( + ); + productName = CouchbaseLiteSwiftBinaryTests; + productReference = 40E92C4C303E4282005C088C /* CBL_EE_Swift_Binary_Tests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 40E92C76303E45A3005C088C /* CBL_Swift_Binary_Tests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 40E92CA1303E45A3005C088C /* Build configuration list for PBXNativeTarget "CBL_Swift_Binary_Tests" */; + buildPhases = ( + 40E92C77303E45A3005C088C /* Check Binary Frameworks */, + 40E92C78303E45A3005C088C /* Sources */, + 40E92C99303E45A3005C088C /* Frameworks */, + 40E92C9C303E45A3005C088C /* Resources */, + 40E92C9E303E45A3005C088C /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = CBL_Swift_Binary_Tests; + packageProductDependencies = ( + ); + productName = CouchbaseLiteSwiftBinaryTests; + productReference = 40E92CA6303E45A3005C088C /* CBL_Swift_Binary_Tests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; 930B63CF246B9CD1006D94FF /* CBL_EE_Swift_Tests_iOS_App */ = { isa = PBXNativeTarget; buildConfigurationList = 930B63FD246B9CD2006D94FF /* Build configuration list for PBXNativeTarget "CBL_EE_Swift_Tests_iOS_App" */; @@ -6126,6 +6279,12 @@ 40CC4CFD303403F30002386D = { TestTargetID = 40CC4C4D3033CE540002386D; }; + 40E92C11303E4282005C088C = { + TestTargetID = 40CC4C4D3033CE540002386D; + }; + 40E92C76303E45A3005C088C = { + TestTargetID = 40CC4C4D3033CE540002386D; + }; 930B63CF246B9CD1006D94FF = { CreatedOnToolsVersion = 10.1; DevelopmentTeam = N2Q372V7W2; @@ -6220,6 +6379,8 @@ 275F927B1E4D30A4007FD5A2 /* CBL_Swift_Tests */, 93BB1C7D246BB1DB004FFA00 /* CBL_Swift_Tests_iOS */, 93BB1C8A246BB1F1004FFA00 /* CBL_Swift_Tests_iOS_App */, + 40E92C76303E45A3005C088C /* CBL_Swift_Binary_Tests */, + 40E92C11303E4282005C088C /* CBL_EE_Swift_Binary_Tests */, 275FF6121E3FE9DB005F90DD /* PerfTests_Mac */, 9343EF2A207D611600F19A89 /* CBL_EE_ObjC */, 9343F011207D61AB00F19A89 /* CBL_EE_Swift */, @@ -6228,7 +6389,7 @@ 9343F153207D62C900F19A89 /* CBL_EE_Objc_Tests_iOS_App */, 40CC4CFD303403F30002386D /* CBL_ObjC_Binary_Tests */, 40CC4B7F302C2FF90002386D /* CBL_EE_ObjC_Binary_Tests */, - 40CC4C4D3033CE540002386D /* CBL_ObjC_Binary_Tests_iOS_App */, + 40CC4C4D3033CE540002386D /* CBL_Binary_Tests_iOS_App */, 9343F18A207D636300F19A89 /* CBL_EE_Swift_Tests */, 930B63E2246B9CD2006D94FF /* CBL_EE_Swift_Tests_iOS */, 930B63CF246B9CD1006D94FF /* CBL_EE_Swift_Tests_iOS_App */, @@ -6404,6 +6565,22 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 40E92C42303E4282005C088C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 40E92C43303E4282005C088C /* Support in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 40E92C9C303E45A3005C088C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 40E92C9D303E45A3005C088C /* Support in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 40EF690B2B7757A200F0CB50 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -6576,6 +6753,42 @@ shellPath = /bin/sh; shellScript = "# Verify that binary frameworks are staged, and log what is being tested:\nINFO_FILE=\"${SRCROOT}/BinaryTests/Frameworks/info.txt\"\nif [ -f \"${INFO_FILE}\" ]; then\n cat \"${INFO_FILE}\"\nelse\n echo \"error: No binary frameworks staged in BinaryTests/Frameworks. Run 'Scripts/prepare_binary_test.sh [-]' first.\"\n exit 1\nfi\n"; }; + 40E92C12303E4282005C088C /* Check Binary Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Check Binary Frameworks"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Verify that binary frameworks are staged, and log what is being tested:\nINFO_FILE=\"${SRCROOT}/BinaryTests/Frameworks/info.txt\"\nif [ -f \"${INFO_FILE}\" ]; then\n cat \"${INFO_FILE}\"\nelse\n echo \"error: No binary frameworks staged in BinaryTests/Frameworks. Run 'Scripts/prepare_binary_test.sh [-]' first.\"\n exit 1\nfi\n"; + }; + 40E92C77303E45A3005C088C /* Check Binary Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Check Binary Frameworks"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Verify that binary frameworks are staged, and log what is being tested:\nINFO_FILE=\"${SRCROOT}/BinaryTests/Frameworks/info.txt\"\nif [ -f \"${INFO_FILE}\" ]; then\n cat \"${INFO_FILE}\"\nelse\n echo \"error: No binary frameworks staged in BinaryTests/Frameworks. Run 'Scripts/prepare_binary_test.sh [-]' first.\"\n exit 1\nfi\n"; + }; 40EF68002B71889F00F0CB50 /* Remove Private Module */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -7041,6 +7254,75 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 40E92C13303E4282005C088C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 40E92C4E303E444D005C088C /* DatabaseTest.swift in Sources */, + 40E92C4F303E444D005C088C /* ReplicatorTest.swift in Sources */, + 40E92C50303E444D005C088C /* LogSinkTest.swift in Sources */, + 40E92C6E303E448D005C088C /* CBLTestHelper.m in Sources */, + 40E92C51303E444D005C088C /* URLEndpointListenerTest.swift in Sources */, + 40E92C52303E444D005C088C /* URLEndpointListenerTest+Collection.swift in Sources */, + 40E92C53303E444D005C088C /* CollectionTest.swift in Sources */, + 40E92C54303E444D005C088C /* FragmentTest.swift in Sources */, + 40E92C55303E444D005C088C /* PublisherTest.swift in Sources */, + 40E92C56303E444D005C088C /* ReplicatorTest+CustomConflict.swift in Sources */, + 40E92C57303E444D005C088C /* TestCustomLogSink.swift in Sources */, + 40E92C58303E444D005C088C /* QueryTest+Collection.swift in Sources */, + 40E92C5A303E444D005C088C /* DocumentExpirationTest.swift in Sources */, + 40E92C5B303E444D005C088C /* DictionaryTest.swift in Sources */, + 40E92C5C303E444D005C088C /* NotificationTest.swift in Sources */, + 40E92C5D303E444D005C088C /* QueryTest.swift in Sources */, + 40E92C5E303E444D005C088C /* PredictiveQueryTest+CoreML.swift in Sources */, + 40E92C5F303E444D005C088C /* UnnestArrayTest.swift in Sources */, + 40E92C60303E444D005C088C /* ReplicatorTest+PendingDocIds.swift in Sources */, + 40E92C61303E444D005C088C /* TLSIdentityTest.swift in Sources */, + 40E92C62303E444D005C088C /* DateTimeQueryFunctionTest.swift in Sources */, + 40E92C63303E444D005C088C /* ReplicatorTest+Collection.swift in Sources */, + 40E92C64303E444D005C088C /* VectorSearchTest+Lazy.swift in Sources */, + 40E92C65303E444D005C088C /* CodableTest.swift in Sources */, + 40E92C66303E444D005C088C /* PredictiveQueryTest.swift in Sources */, + 40E92C67303E444D005C088C /* DocumentTest.swift in Sources */, + 40E92C68303E444D005C088C /* VectorSearchTest.swift in Sources */, + 40E92C69303E444D005C088C /* CBLTestCase.swift in Sources */, + 40E92C6A303E444D005C088C /* PartialIndexTest.swift in Sources */, + 40E92C6B303E444D005C088C /* DatabaseEncryptionTest.swift in Sources */, + 40E92C6D303E444D005C088C /* ArrayTest.swift in Sources */, + 40E92C6F303E4498005C088C /* WordEmbeddingModel.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 40E92C78303E45A3005C088C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 40E92C79303E45A3005C088C /* DatabaseTest.swift in Sources */, + 40E92C7A303E45A3005C088C /* ReplicatorTest.swift in Sources */, + 40E92C7B303E45A3005C088C /* LogSinkTest.swift in Sources */, + 40E92C7C303E45A3005C088C /* CBLTestHelper.m in Sources */, + 40E92C7F303E45A3005C088C /* CollectionTest.swift in Sources */, + 40E92C80303E45A3005C088C /* FragmentTest.swift in Sources */, + 40E92C81303E45A3005C088C /* PublisherTest.swift in Sources */, + 40E92C82303E45A3005C088C /* ReplicatorTest+CustomConflict.swift in Sources */, + 40E92C83303E45A3005C088C /* TestCustomLogSink.swift in Sources */, + 40E92C84303E45A3005C088C /* QueryTest+Collection.swift in Sources */, + 40E92C85303E45A3005C088C /* DocumentExpirationTest.swift in Sources */, + 40E92C86303E45A3005C088C /* DictionaryTest.swift in Sources */, + 40E92C87303E45A3005C088C /* NotificationTest.swift in Sources */, + 40E92C88303E45A3005C088C /* QueryTest.swift in Sources */, + 40E92C8A303E45A3005C088C /* UnnestArrayTest.swift in Sources */, + 40E92C8B303E45A3005C088C /* ReplicatorTest+PendingDocIds.swift in Sources */, + 40E92C8D303E45A3005C088C /* DateTimeQueryFunctionTest.swift in Sources */, + 40E92C8E303E45A3005C088C /* ReplicatorTest+Collection.swift in Sources */, + 40E92C90303E45A3005C088C /* CodableTest.swift in Sources */, + 40E92C92303E45A3005C088C /* DocumentTest.swift in Sources */, + 40E92C94303E45A3005C088C /* CBLTestCase.swift in Sources */, + 40E92C95303E45A3005C088C /* PartialIndexTest.swift in Sources */, + 40E92C97303E45A3005C088C /* ArrayTest.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 930B63CC246B9CD1006D94FF /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -8817,7 +9099,7 @@ }; 40CC4C663033CE550002386D /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 40CC4CAD3033EEF90002386D /* CBL_ObjC_Binary_Tests_iOS_App.xcconfig */; + baseConfigurationReference = 40CC4CAD3033EEF90002386D /* CBL_Binary_Tests_iOS_App.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -8887,7 +9169,7 @@ }; 40CC4C673033CE550002386D /* Debug_EE */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 40CC4CAD3033EEF90002386D /* CBL_ObjC_Binary_Tests_iOS_App.xcconfig */; + baseConfigurationReference = 40CC4CAD3033EEF90002386D /* CBL_Binary_Tests_iOS_App.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -8957,7 +9239,7 @@ }; 40CC4C683033CE550002386D /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 40CC4CAD3033EEF90002386D /* CBL_ObjC_Binary_Tests_iOS_App.xcconfig */; + baseConfigurationReference = 40CC4CAD3033EEF90002386D /* CBL_Binary_Tests_iOS_App.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -9026,7 +9308,7 @@ }; 40CC4C693033CE550002386D /* Release_EE */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 40CC4CAD3033EEF90002386D /* CBL_ObjC_Binary_Tests_iOS_App.xcconfig */; + baseConfigurationReference = 40CC4CAD3033EEF90002386D /* CBL_Binary_Tests_iOS_App.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -9121,6 +9403,62 @@ }; name = Release_EE; }; + 40E92C48303E4282005C088C /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 40E92C4D303E42FA005C088C /* CBL_EE_Swift_Binary_Tests.xcconfig */; + buildSettings = { + }; + name = Debug; + }; + 40E92C49303E4282005C088C /* Debug_EE */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 40E92C4D303E42FA005C088C /* CBL_EE_Swift_Binary_Tests.xcconfig */; + buildSettings = { + }; + name = Debug_EE; + }; + 40E92C4A303E4282005C088C /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 40E92C4D303E42FA005C088C /* CBL_EE_Swift_Binary_Tests.xcconfig */; + buildSettings = { + }; + name = Release; + }; + 40E92C4B303E4282005C088C /* Release_EE */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 40E92C4D303E42FA005C088C /* CBL_EE_Swift_Binary_Tests.xcconfig */; + buildSettings = { + }; + name = Release_EE; + }; + 40E92CA2303E45A3005C088C /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 40E92CA7303E4635005C088C /* CBL_Swift_Binary_Tests.xcconfig */; + buildSettings = { + }; + name = Debug; + }; + 40E92CA3303E45A3005C088C /* Debug_EE */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 40E92CA7303E4635005C088C /* CBL_Swift_Binary_Tests.xcconfig */; + buildSettings = { + }; + name = Debug_EE; + }; + 40E92CA4303E45A3005C088C /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 40E92CA7303E4635005C088C /* CBL_Swift_Binary_Tests.xcconfig */; + buildSettings = { + }; + name = Release; + }; + 40E92CA5303E45A3005C088C /* Release_EE */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 40E92CA7303E4635005C088C /* CBL_Swift_Binary_Tests.xcconfig */; + buildSettings = { + }; + name = Release_EE; + }; 930B63EA246B9CD2006D94FF /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 93BB1C7C246BB151004FFA00 /* CBL_EE_Swift_Tests_iOS_App.xcconfig */; @@ -11054,7 +11392,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 40CC4C653033CE550002386D /* Build configuration list for PBXNativeTarget "CBL_ObjC_Binary_Tests_iOS_App" */ = { + 40CC4C653033CE550002386D /* Build configuration list for PBXNativeTarget "CBL_Binary_Tests_iOS_App" */ = { isa = XCConfigurationList; buildConfigurations = ( 40CC4C663033CE550002386D /* Debug */, @@ -11076,6 +11414,28 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 40E92C47303E4282005C088C /* Build configuration list for PBXNativeTarget "CBL_EE_Swift_Binary_Tests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 40E92C48303E4282005C088C /* Debug */, + 40E92C49303E4282005C088C /* Debug_EE */, + 40E92C4A303E4282005C088C /* Release */, + 40E92C4B303E4282005C088C /* Release_EE */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 40E92CA1303E45A3005C088C /* Build configuration list for PBXNativeTarget "CBL_Swift_Binary_Tests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 40E92CA2303E45A3005C088C /* Debug */, + 40E92CA3303E45A3005C088C /* Debug_EE */, + 40E92CA4303E45A3005C088C /* Release */, + 40E92CA5303E45A3005C088C /* Release_EE */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 930B63FD246B9CD2006D94FF /* Build configuration list for PBXNativeTarget "CBL_EE_Swift_Tests_iOS_App" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/CouchbaseLite.xcodeproj/xcshareddata/xcschemes/CBL_EE_ObjC_Binary_Tests_iOS_App.xcscheme b/CouchbaseLite.xcodeproj/xcshareddata/xcschemes/CBL_EE_ObjC_Binary_Tests_iOS_App.xcscheme index ebe0f3353..01040b0f7 100644 --- a/CouchbaseLite.xcodeproj/xcshareddata/xcschemes/CBL_EE_ObjC_Binary_Tests_iOS_App.xcscheme +++ b/CouchbaseLite.xcodeproj/xcshareddata/xcschemes/CBL_EE_ObjC_Binary_Tests_iOS_App.xcscheme @@ -17,7 +17,7 @@ BuildableIdentifier = "primary" BlueprintIdentifier = "40CC4C4D3033CE540002386D" BuildableName = "CBL_Binary_Tests.app" - BlueprintName = "CBL_ObjC_Binary_Tests_iOS_App" + BlueprintName = "CBL_Binary_Tests_iOS_App" ReferencedContainer = "container:CouchbaseLite.xcodeproj"> diff --git a/CouchbaseLite.xcodeproj/xcshareddata/xcschemes/CBL_EE_Swift_Binary_Tests.xcscheme b/CouchbaseLite.xcodeproj/xcshareddata/xcschemes/CBL_EE_Swift_Binary_Tests.xcscheme new file mode 100644 index 000000000..abfa73113 --- /dev/null +++ b/CouchbaseLite.xcodeproj/xcshareddata/xcschemes/CBL_EE_Swift_Binary_Tests.xcscheme @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/CouchbaseLite.xcodeproj/xcshareddata/xcschemes/CBL_EE_Swift_Binary_Tests_iOS_App.xcscheme b/CouchbaseLite.xcodeproj/xcshareddata/xcschemes/CBL_EE_Swift_Binary_Tests_iOS_App.xcscheme new file mode 100644 index 000000000..719d5b2c2 --- /dev/null +++ b/CouchbaseLite.xcodeproj/xcshareddata/xcschemes/CBL_EE_Swift_Binary_Tests_iOS_App.xcscheme @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CouchbaseLite.xcodeproj/xcshareddata/xcschemes/CBL_ObjC_Binary_Tests_iOS_App.xcscheme b/CouchbaseLite.xcodeproj/xcshareddata/xcschemes/CBL_ObjC_Binary_Tests_iOS_App.xcscheme index c61dfeaab..7c25f9ae1 100644 --- a/CouchbaseLite.xcodeproj/xcshareddata/xcschemes/CBL_ObjC_Binary_Tests_iOS_App.xcscheme +++ b/CouchbaseLite.xcodeproj/xcshareddata/xcschemes/CBL_ObjC_Binary_Tests_iOS_App.xcscheme @@ -17,7 +17,7 @@ BuildableIdentifier = "primary" BlueprintIdentifier = "40CC4C4D3033CE540002386D" BuildableName = "CBL_Binary_Tests.app" - BlueprintName = "CBL_ObjC_Binary_Tests_iOS_App" + BlueprintName = "CBL_Binary_Tests_iOS_App" ReferencedContainer = "container:CouchbaseLite.xcodeproj"> diff --git a/CouchbaseLite.xcodeproj/xcshareddata/xcschemes/CBL_Swift_Binary_Tests.xcscheme b/CouchbaseLite.xcodeproj/xcshareddata/xcschemes/CBL_Swift_Binary_Tests.xcscheme new file mode 100644 index 000000000..b0e884758 --- /dev/null +++ b/CouchbaseLite.xcodeproj/xcshareddata/xcschemes/CBL_Swift_Binary_Tests.xcscheme @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/CouchbaseLite.xcodeproj/xcshareddata/xcschemes/CBL_Swift_Binary_Tests_iOS_App.xcscheme b/CouchbaseLite.xcodeproj/xcshareddata/xcschemes/CBL_Swift_Binary_Tests_iOS_App.xcscheme new file mode 100644 index 000000000..76d436f09 --- /dev/null +++ b/CouchbaseLite.xcodeproj/xcshareddata/xcschemes/CBL_Swift_Binary_Tests_iOS_App.xcscheme @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Objective-C/Tests/URLEndpointListenerTest+Main.m b/Objective-C/Tests/URLEndpointListenerTest+Main.m index 5356b7045..4e8534a3c 100644 --- a/Objective-C/Tests/URLEndpointListenerTest+Main.m +++ b/Objective-C/Tests/URLEndpointListenerTest+Main.m @@ -1484,27 +1484,16 @@ - (void) testCleanUpAnonymousIdentities { // The identity created above must be found by the discovered name; this guards // against the zero-count checks below passing vacuously with a wrong name: - Assert([self anonymousIdentityCount] > 0); + Assert([self keychainItemCount: (id)kSecClassIdentity commonName: commonName] > 0); [self cleanUpAnonymousIdentities]; // No anonymous identities nor certificates should be left in the keychain: - AssertEqual([self anonymousIdentityCount], 0); - AssertEqual([self anonymousCertificateCount], 0); + AssertEqual([self keychainItemCount: (id)kSecClassIdentity commonName: commonName], 0); + AssertEqual([self keychainItemCount: (id)kSecClassCertificate commonName: commonName], 0); } -- (NSUInteger) anonymousIdentityCount { - return [self keychainItemCount: (id)kSecClassIdentity]; -} - -- (NSUInteger) anonymousCertificateCount { - return [self keychainItemCount: (id)kSecClassCertificate]; -} - -- (NSUInteger) keychainItemCount: (id)itemClass { - NSString* commonName = [self anonymousIdentityCommonName]; - AssertNotNil(commonName); - +- (NSUInteger) keychainItemCount: (id)itemClass commonName: (NSString*)commonName { NSDictionary* query = @{(id)kSecClass: itemClass, (id)kSecMatchLimit: (id)kSecMatchLimitAll, (id)kSecReturnRef: @YES}; diff --git a/Swift/ModuleMap/CouchbaseLiteSwift-EE.private.txt b/Swift/ModuleMap/CouchbaseLiteSwift-EE.private.txt index 4fcd7fbbd..76dc60230 100644 --- a/Swift/ModuleMap/CouchbaseLiteSwift-EE.private.txt +++ b/Swift/ModuleMap/CouchbaseLiteSwift-EE.private.txt @@ -39,7 +39,6 @@ header "CBLTLSIdentity.h" header "CBLTLSIdentity+Swift.h" header "CBLURLEndpointListener.h" - header "CBLURLEndpointListener+Swift.h" header "CBLURLEndpointListenerConfiguration.h" header "CBLVectorEncoding.h" header "CBLVectorIndexConfiguration.h" diff --git a/Swift/ModuleMap/Generated/CouchbaseLiteSwift-EE.private.modulemap b/Swift/ModuleMap/Generated/CouchbaseLiteSwift-EE.private.modulemap index 102956bed..4926203d3 100644 --- a/Swift/ModuleMap/Generated/CouchbaseLiteSwift-EE.private.modulemap +++ b/Swift/ModuleMap/Generated/CouchbaseLiteSwift-EE.private.modulemap @@ -153,7 +153,6 @@ framework module CouchbaseLiteSwift_Private { header "CBLTLSIdentity.h" header "CBLTLSIdentity+Swift.h" header "CBLURLEndpointListener.h" - header "CBLURLEndpointListener+Swift.h" header "CBLURLEndpointListenerConfiguration.h" header "CBLVectorEncoding.h" header "CBLVectorIndexConfiguration.h" diff --git a/Swift/Query.swift b/Swift/Query.swift index e93fc95fa..94ebd1677 100644 --- a/Swift/Query.swift +++ b/Swift/Query.swift @@ -165,23 +165,6 @@ public class Query { } } - /// Encoded JSON representation of the query. - /// Can be used later to reconstruct an identical query. - var json : Data { - prepareQuery() - return queryImpl!.json - } - - /// Initialize a Query given a JSON-encoded representation. - /// - Parameters: - /// - database: The database to query. - /// - json: JSON data encoding the query. This can be obtained from a Query object's - /// JSONRepresentation property. - init(database: Database, JSONRepresentation json: Data) { - self.database = database - queryImpl = CBLQuery(database: database.impl, json: json) - } - /// Creates a query, given the query string, as from the expression property. /// - Parameters: /// - database: The database to query. diff --git a/Swift/Tests/CBLTestCase.swift b/Swift/Tests/CBLTestCase.swift index 0e151a2df..14ed2afbd 100644 --- a/Swift/Tests/CBLTestCase.swift +++ b/Swift/Tests/CBLTestCase.swift @@ -19,7 +19,7 @@ import XCTest import Foundation -@testable import CouchbaseLiteSwift +import CouchbaseLiteSwift extension String { func toJSONObj() -> Any { diff --git a/Swift/Tests/CBLTestHelper.h b/Swift/Tests/CBLTestHelper.h index 4096a35b9..6901561e6 100644 --- a/Swift/Tests/CBLTestHelper.h +++ b/Swift/Tests/CBLTestHelper.h @@ -18,9 +18,6 @@ // #import -#import - -extern atomic_int gC4ExpectExceptions; @interface CBLTestHelper : NSObject diff --git a/Swift/Tests/CBLTestHelper.m b/Swift/Tests/CBLTestHelper.m index ea7d3d1a4..0f1359892 100644 --- a/Swift/Tests/CBLTestHelper.m +++ b/Swift/Tests/CBLTestHelper.m @@ -19,19 +19,31 @@ #import "CBLTestHelper.h" +#ifndef CBL_BINARY_TEST +#import +extern atomic_int gC4ExpectExceptions; +// Marks a scope that intentionally provokes exceptions, so that LiteCore's +// exception diagnostics don't flag them. No-op when testing a binary framework. +#define CBLExpectExceptionsBegin() ((void)++gC4ExpectExceptions) +#define CBLExpectExceptionsEnd() ((void)--gC4ExpectExceptions) +#else +#define CBLExpectExceptionsBegin() +#define CBLExpectExceptionsEnd() +#endif + @implementation CBLTestHelper + (void) allowExceptionIn: (void (^)(void))block { - ++gC4ExpectExceptions; + CBLExpectExceptionsBegin(); block(); - --gC4ExpectExceptions; + CBLExpectExceptionsEnd(); } + (BOOL) catchException: (void(^)(void))tryBlock error: (NSError **)error { @try { - ++gC4ExpectExceptions; + CBLExpectExceptionsBegin(); tryBlock(); - --gC4ExpectExceptions; + CBLExpectExceptionsEnd(); return YES; } @catch (NSException *exception) { diff --git a/Swift/Tests/CollectionTest.swift b/Swift/Tests/CollectionTest.swift index e64ef31c0..c731b537c 100644 --- a/Swift/Tests/CollectionTest.swift +++ b/Swift/Tests/CollectionTest.swift @@ -18,7 +18,7 @@ // import XCTest -@testable import CouchbaseLiteSwift +import CouchbaseLiteSwift class CollectionTest: CBLTestCase { diff --git a/Swift/Tests/DatabaseTest.swift b/Swift/Tests/DatabaseTest.swift index 7394b07fd..43b89cae2 100644 --- a/Swift/Tests/DatabaseTest.swift +++ b/Swift/Tests/DatabaseTest.swift @@ -18,7 +18,7 @@ // import XCTest -@testable import CouchbaseLiteSwift +import CouchbaseLiteSwift class DatabaseTest: CBLTestCase { diff --git a/Swift/Tests/LogSinkTest.swift b/Swift/Tests/LogSinkTest.swift index 0badcf6cd..19fe90f6d 100644 --- a/Swift/Tests/LogSinkTest.swift +++ b/Swift/Tests/LogSinkTest.swift @@ -18,7 +18,7 @@ // import XCTest -@testable import CouchbaseLiteSwift +import CouchbaseLiteSwift class LogSinkTest: CBLTestCase { diff --git a/Swift/Tests/MultipeerReplicatorTest.swift b/Swift/Tests/MultipeerReplicatorTest.swift index 18ea313e9..cd9f85067 100644 --- a/Swift/Tests/MultipeerReplicatorTest.swift +++ b/Swift/Tests/MultipeerReplicatorTest.swift @@ -19,6 +19,12 @@ import XCTest @testable import CouchbaseLiteSwift +// This test file uses internal APIs and is for the internal test targets only; +// it cannot be part of the binary test targets (CBL_*_Binary_Tests). +#if CBL_BINARY_TEST +#error("This test file uses internal APIs and cannot run against a binary framework.") +#endif + /// Test Spec : https://github.com/couchbaselabs/couchbase-lite-api/blob/master/spec/tests/T0012-MultipeerReplicator.md /// Version : 1.1.0 class MultipeerReplicatorTest: CBLTestCase { diff --git a/Swift/Tests/PartialIndexTest.swift b/Swift/Tests/PartialIndexTest.swift index 00383a072..388141d33 100644 --- a/Swift/Tests/PartialIndexTest.swift +++ b/Swift/Tests/PartialIndexTest.swift @@ -17,7 +17,7 @@ // import XCTest -@testable import CouchbaseLiteSwift +import CouchbaseLiteSwift /// Test Spec v1.0.3: /// https://github.com/couchbaselabs/couchbase-lite-api/blob/master/spec/tests/T0007-Partial-Index.md diff --git a/Swift/Tests/PublisherTest.swift b/Swift/Tests/PublisherTest.swift index 0f016c22f..82483eeee 100644 --- a/Swift/Tests/PublisherTest.swift +++ b/Swift/Tests/PublisherTest.swift @@ -19,7 +19,7 @@ import Foundation import Combine import XCTest -@testable import CouchbaseLiteSwift +import CouchbaseLiteSwift @available(iOS 13.0, *) class PublisherTest: CBLTestCase { diff --git a/Swift/Tests/QueryTest+Collection.swift b/Swift/Tests/QueryTest+Collection.swift index abfcb51b3..293b6778e 100644 --- a/Swift/Tests/QueryTest+Collection.swift +++ b/Swift/Tests/QueryTest+Collection.swift @@ -18,7 +18,7 @@ // import XCTest -@testable import CouchbaseLiteSwift +import CouchbaseLiteSwift class QueryTest_Collection: QueryTest { override func setUpWithError() throws { diff --git a/Swift/Tests/QueryTest.swift b/Swift/Tests/QueryTest.swift index ff4a55057..9609fd4cb 100644 --- a/Swift/Tests/QueryTest.swift +++ b/Swift/Tests/QueryTest.swift @@ -19,7 +19,7 @@ import XCTest -@testable import CouchbaseLiteSwift +import CouchbaseLiteSwift class QueryTest: CBLTestCase { @@ -1518,25 +1518,6 @@ class QueryTest: CBLTestCase { XCTAssertEqual(dict["address"] as! NSNull, NSNull()) } - func testJSONRepresentation() throws { - let doc1 = MutableDocument() - doc1.setValue("string", forKey: "string") - try saveDocument(doc1) - - let q1 = QueryBuilder.select(kDOCID).from(DataSource.collection(defaultCollection!)).where( - Expression.property("string").is(Expression.string("string"))) - let json = q1.json - - let q = Query(database: db, JSONRepresentation: json) - - let numRows = try verifyQuery(q) { (n, r) in - let doc = try defaultCollection!.document(id: r.string(at: 0)!)! - XCTAssertEqual(doc.id, doc1.id); - XCTAssertEqual(doc.string(forKey: "string")!, "string"); - } - XCTAssertEqual(numRows, 1); - } - // MARK: META - isDeleted func testMetaIsDeletedEmpty() throws { diff --git a/Swift/Tests/ReplicatorTest+Collection.swift b/Swift/Tests/ReplicatorTest+Collection.swift index 14a0ef98f..1a438a688 100644 --- a/Swift/Tests/ReplicatorTest+Collection.swift +++ b/Swift/Tests/ReplicatorTest+Collection.swift @@ -18,7 +18,7 @@ // import XCTest -@testable import CouchbaseLiteSwift +import CouchbaseLiteSwift class ReplicatorTest_Collection: ReplicatorTest { override func setUpWithError() throws { diff --git a/Swift/Tests/ReplicatorTest+CustomConflict.swift b/Swift/Tests/ReplicatorTest+CustomConflict.swift index d4347e64f..2d7f9cf21 100644 --- a/Swift/Tests/ReplicatorTest+CustomConflict.swift +++ b/Swift/Tests/ReplicatorTest+CustomConflict.swift @@ -18,7 +18,7 @@ // import XCTest -@testable import CouchbaseLiteSwift +import CouchbaseLiteSwift class ReplicatorTest_CustomConflict: ReplicatorTest { diff --git a/Swift/Tests/ReplicatorTest+PendingDocIds.swift b/Swift/Tests/ReplicatorTest+PendingDocIds.swift index 191fd18f5..1b52e0e0c 100644 --- a/Swift/Tests/ReplicatorTest+PendingDocIds.swift +++ b/Swift/Tests/ReplicatorTest+PendingDocIds.swift @@ -18,7 +18,7 @@ // import XCTest -@testable import CouchbaseLiteSwift +import CouchbaseLiteSwift class ReplicatorTest_PendingDocIds: ReplicatorTest { diff --git a/Swift/Tests/TLSIdentityTest.swift b/Swift/Tests/TLSIdentityTest.swift index 0b1341327..19c6e5ef5 100644 --- a/Swift/Tests/TLSIdentityTest.swift +++ b/Swift/Tests/TLSIdentityTest.swift @@ -17,7 +17,13 @@ // import XCTest +// The white-box tests in the Internal section below need @testable access; the binary +// tests compile against a shipped framework, which does not support testability. +#if CBL_BINARY_TEST +import CouchbaseLiteSwift +#else @testable import CouchbaseLiteSwift +#endif class TLSIdentityTest: CBLTestCase { let serverCertLabel = "CBL-Swift-Server-Cert" @@ -432,6 +438,13 @@ class TLSIdentityTest: CBLTestCase { XCTAssert(abs(expiration.timeIntervalSince1970 - identity!.expiration.timeIntervalSince1970) < 5.0) } + // MARK: - Internal + + // White-box tests that verify internal state; excluded from the binary tests. + // Note: a public rewrite (import the issuer via importIdentity and verify with + // SecTrust) is blocked by CBL-7005 (importIdentity broken on newer macOS). + #if !CBL_BINARY_TEST + func testCreateIdentitySignedWithIssuer() throws { try XCTSkipUnless(keyChainAccessAllowed) @@ -527,4 +540,6 @@ class TLSIdentityTest: CBLTestCase { checkIdentity = try TLSIdentity.identity(withLabel: self.serverCertLabel) XCTAssertNil(checkIdentity) } + + #endif } diff --git a/Swift/Tests/URLEndpointListenerTest+Collection.swift b/Swift/Tests/URLEndpointListenerTest+Collection.swift index cef2ad4f0..526da9da7 100644 --- a/Swift/Tests/URLEndpointListenerTest+Collection.swift +++ b/Swift/Tests/URLEndpointListenerTest+Collection.swift @@ -18,7 +18,7 @@ // import XCTest -@testable import CouchbaseLiteSwift +import CouchbaseLiteSwift class URLEndpointListenerTest_Collection: URLEndpointListenerTest { override func setUpWithError() throws { @@ -30,7 +30,7 @@ class URLEndpointListenerTest_Collection: URLEndpointListenerTest { } func testCollectionsSingleShotPushPullReplication() throws { - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) let col1a = try self.db.createCollection(name: "colA", scope: "scopeA") let col1b = try self.db.createCollection(name: "colB", scope: "scopeA") @@ -62,7 +62,7 @@ class URLEndpointListenerTest_Collection: URLEndpointListenerTest { } func testCollectionsContinuousPushPullReplication() throws { - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) let col1a = try self.db.createCollection(name: "colA", scope: "scopeA") let col1b = try self.db.createCollection(name: "colB", scope: "scopeA") @@ -95,7 +95,7 @@ class URLEndpointListenerTest_Collection: URLEndpointListenerTest { } func testMismatchedCollectionReplication() throws { - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) let col1a = try self.db.createCollection(name: "colA", scope: "scopeA") let col2b = try otherDB!.createCollection(name: "colB", scope: "scopeA") diff --git a/Swift/Tests/URLEndpointListenerTest.swift b/Swift/Tests/URLEndpointListenerTest.swift index 6bd239032..1c8fab0f0 100644 --- a/Swift/Tests/URLEndpointListenerTest.swift +++ b/Swift/Tests/URLEndpointListenerTest.swift @@ -17,7 +17,7 @@ // import XCTest -@testable import CouchbaseLiteSwift +import CouchbaseLiteSwift class URLEndpointListenerTest: ReplicatorTest { let wsPort: UInt16 = 4084 @@ -59,16 +59,99 @@ class URLEndpointListenerTest: ReplicatorTest { func stopListener(listener: URLEndpointListener? = nil) throws { let l = listener ?? self.listener - l?.stop() - if let id = l?.tlsIdentity { - try id.deleteFromKeyChain() + } + + // MARK: Keychain cleanup helpers + + /// The common name of the anonymous identities that the listener creates when started + /// without an identity. Discovered once per test process from a certificate the product + /// mints, rather than hardcoded, so the tests neither state the product's common name + /// nor drift from it. + private static var discoveredAnonymousIdentityCommonName: String? + + func anonymousIdentityCommonName() -> String? { + if let name = Self.discoveredAnonymousIdentityCommonName { return name } + if !self.keyChainAccessAllowed { return nil } + + // Start a TLS listener without an identity on an ephemeral port; the product + // mints an anonymous identity whose certificate carries the common name: + let config = URLEndpointListenerConfiguration(collections: [self.otherDB_defaultCollection!]) + let listener = URLEndpointListener(config: config) + guard (try? listener.start()) != nil else { return nil } + defer { listener.stop() } + + guard let cert = listener.tlsIdentity?.certs.first else { return nil } + let name = SecCertificateCopySubjectSummary(cert) as String? + Self.discoveredAnonymousIdentityCommonName = name + return name + } + + func cleanUpTLSIdentity(isServer: Bool) { + if !self.keyChainAccessAllowed { return } + + // Delete directly from the keychain by label so that partial identities + // (e.g. a leftover certificate without its key) get cleaned up as well: + let label = isServer ? serverCertLabel : clientCertLabel + for itemClass in [kSecClassIdentity, kSecClassCertificate] { + let query: [CFString: Any] = [kSecClass: itemClass, + kSecAttrLabel: label] + let status = SecItemDelete(query as CFDictionary) + XCTAssert(status == errSecSuccess || status == errSecItemNotFound || status == errSecInvalidItemRef || + status == errSecWrPerm /* items in a keychain that tests cannot modify (e.g. Local Items) */, + "Couldn't delete keychain items with label \(label) (OSStatus = \(status))") } } - - func cleanUpIdentities() throws { - self.ignoreException { - try URLEndpointListener.deleteAnonymousIdentities() + + func cleanUpAnonymousIdentities() { + if !self.keyChainAccessAllowed { return } + guard let anonymousIdentityCommonName = anonymousIdentityCommonName() else { return } + + var query: [CFString: Any] = [kSecClass: kSecClassIdentity, + kSecMatchLimit: kSecMatchLimitAll, + kSecReturnRef: true] + var result: CFTypeRef? + var status = SecItemCopyMatching(query as CFDictionary, &result) + if status != errSecItemNotFound { + XCTAssertEqual(status, errSecSuccess, "Cannot query identities (OSStatus = \(status))") + let identities = result as! [SecIdentity] + for identity in identities { + var certRef: SecCertificate? + guard SecIdentityCopyCertificate(identity, &certRef) == errSecSuccess, let cert = certRef else { + continue + } + let name = SecCertificateCopySubjectSummary(cert) as String? + if name == anonymousIdentityCommonName { + let del: [CFString: Any] = [kSecClass: kSecClassIdentity, + kSecValueRef: identity] + status = SecItemDelete(del as CFDictionary) + XCTAssert(status == errSecSuccess || status == errSecItemNotFound || status == errSecInvalidItemRef, + "Cannot delete anonymous identity (OSStatus = \(status))") + } + } + } + + // Deleting an identity doesn't remove its certificate; sweep the anonymous + // certificates (including any orphaned by previously interrupted test runs): + query = [kSecClass: kSecClassCertificate, + kSecMatchLimit: kSecMatchLimitAll, + kSecReturnRef: true] + result = nil + status = SecItemCopyMatching(query as CFDictionary, &result) + if status == errSecItemNotFound { + return + } + XCTAssertEqual(status, errSecSuccess, "Cannot query certificates (OSStatus = \(status))") + let certs = result as! [SecCertificate] + for cert in certs { + let name = SecCertificateCopySubjectSummary(cert) as String? + if name == anonymousIdentityCommonName { + let del: [CFString: Any] = [kSecClass: kSecClassCertificate, + kSecValueRef: cert] + status = SecItemDelete(del as CFDictionary) + XCTAssert(status == errSecSuccess || status == errSecItemNotFound || status == errSecInvalidItemRef, + "Cannot delete anonymous certificate (OSStatus = \(status))") + } } } @@ -78,9 +161,9 @@ class URLEndpointListenerTest: ReplicatorTest { if !self.keyChainAccessAllowed { return nil } let label = isServer ? serverCertLabel : clientCertLabel - - // cleanup client cert authenticator identity - try TLSIdentity.deleteIdentity(withLabel: label) + + // Cleanup: + cleanUpTLSIdentity(isServer: isServer) // Create client identity: let attrs = [certAttrCommonName: isServer ? "CBL-Server" : "daniel"] @@ -118,12 +201,14 @@ class URLEndpointListenerTest: ReplicatorTest { override func setUp() { super.setUp() - try! cleanUpIdentities() + cleanUpAnonymousIdentities() } - + override func tearDown() { try! stopListener() - try! cleanUpIdentities() + cleanUpAnonymousIdentities() + cleanUpTLSIdentity(isServer: true) + cleanUpTLSIdentity(isServer: false) super.tearDown() } } @@ -215,7 +300,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { } func validateActiveReplicationsAndURLEndpointListener(isDeleteDBs: Bool) throws { - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) let idleExp1 = allowOverfillExpectation(description: "replicator#1 idle") let idleExp2 = allowOverfillExpectation(description: "replicator#2 idle") @@ -276,7 +361,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { } func validateActiveReplicatorAndURLEndpointListeners(isDeleteDB: Bool) throws { - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) let idleExp = allowOverfillExpectation(description: "replicator idle") let stopExp = expectation(description: "replicator stop") @@ -326,7 +411,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { // MARK: -- Tests func testPort() throws { - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) var config = URLEndpointListenerConfiguration(collections: [self.otherDB_defaultCollection!]) config.port = wsPort @@ -342,7 +427,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { } func testEmptyPort() throws { - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) let config = URLEndpointListenerConfiguration(collections: [self.otherDB_defaultCollection!]) self.listener = URLEndpointListener(config: config) @@ -357,7 +442,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { } func testBusyPort() throws { - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) try startListener() @@ -371,7 +456,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { } func testURLs() throws { - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) var config = URLEndpointListenerConfiguration(collections: [self.otherDB_defaultCollection!]) config.port = wsPort @@ -387,7 +472,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { } func testTLSListenerAnonymousIdentity() throws { - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) let doc = createDocument("doc-1") try self.otherDB_defaultCollection!.save(document: doc) @@ -430,9 +515,66 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { XCTAssertNil(listener.tlsIdentity) try TLSIdentity.deleteIdentity(withLabel: serverCertLabel) } - + + func testCleanUpAnonymousIdentities() throws { + try XCTSkipUnless(self.keyChainAccessAllowed) + + // The cleanup helpers sweep by the common name discovered from a certificate + // that the product mints; the discovery must succeed for the cleanup to work: + let commonName = anonymousIdentityCommonName() + XCTAssertNotNil(commonName) + + // Starting a TLS listener without an identity creates an anonymous identity: + let config = URLEndpointListenerConfiguration(collections: [self.otherDB_defaultCollection!]) + try startListener(withConfig: config) + let identity = self.listener!.tlsIdentity + XCTAssertNotNil(identity) + + // The discovered name must match the certificate of an independently created + // anonymous identity: + XCTAssertEqual(SecCertificateCopySubjectSummary(identity!.certs[0]) as String?, commonName) + + try stopListener() + + // The identity created above must be found by the discovered name; this guards + // against the zero-count checks below passing vacuously with a wrong name: + XCTAssertGreaterThan(keychainItemCount(kSecClassIdentity, commonName: commonName!), 0) + + cleanUpAnonymousIdentities() + + // No anonymous identities nor certificates should be left in the keychain: + XCTAssertEqual(keychainItemCount(kSecClassIdentity, commonName: commonName!), 0) + XCTAssertEqual(keychainItemCount(kSecClassCertificate, commonName: commonName!), 0) + } + + func keychainItemCount(_ itemClass: CFString, commonName: String) -> Int { + let query: [CFString: Any] = [kSecClass: itemClass, + kSecMatchLimit: kSecMatchLimitAll, + kSecReturnRef: true] + var result: CFTypeRef? + let status = SecItemCopyMatching(query as CFDictionary, &result) + if status == errSecItemNotFound { return 0 } + XCTAssertEqual(status, errSecSuccess, "Cannot query keychain items (OSStatus = \(status))") + + var count = 0 + for item in (result as! [AnyObject]) { + var certRef: SecCertificate? + if itemClass == kSecClassIdentity { + guard SecIdentityCopyCertificate(item as! SecIdentity, &certRef) == errSecSuccess, + certRef != nil else { continue } + } else { + certRef = (item as! SecCertificate) + } + let name = SecCertificateCopySubjectSummary(certRef!) as String? + if name == commonName { + count += 1 + } + } + return count + } + func testTLSListenerUserIdentity() throws { - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) let doc = createDocument("doc-1") try self.otherDB_defaultCollection!.save(document: doc) @@ -478,7 +620,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { } func testNonTLSNullListenerAuthenticator() throws { - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) let listener = try startListener(tls: false) XCTAssertNil(listener.tlsIdentity) @@ -500,7 +642,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { } func testNonTLSPasswordListenerAuthenticator() throws { - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) // Listener: let listenerAuth = ListenerPasswordAuthenticator.init { @@ -540,7 +682,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { } func testClientCertAuthWithCallback() throws { - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) // Listener: let listenerAuth = ListenerCertificateAuthenticator.init { (certs) -> Bool in @@ -567,7 +709,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { } func testClientCertAuthWithCallbackError() throws { - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) // Listener: let listenerAuth = ListenerCertificateAuthenticator.init { (certs) -> Bool in @@ -593,7 +735,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { try XCTSkipIf(true, "CBL-7005 : importIdentityWithData not working on newer macOS") #endif - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) // Root Cert: let rootCertData = try dataFromResource(name: "identity/client-ca", ofType: "der") @@ -624,7 +766,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { } func testClientCertAuthWithRootCertsError() throws { - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) // Root Cert: let rootCertData = try dataFromResource(name: "identity/client-ca", ofType: "der") @@ -649,7 +791,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { } func testConnectionStatus() throws { - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) let replicatorStop = expectation(description: "replicator stop") let pullFilterBusy = expectation(description: "pull filter busy") @@ -705,7 +847,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { } func testMultipleListenersOnSameDatabase() throws { - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) let config = URLEndpointListenerConfiguration(collections: [self.otherDB_defaultCollection!]) let listener1 = URLEndpointListener(config: config) @@ -728,7 +870,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { } func testCloseWithActiveListener() throws { - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) try startListener() @@ -745,7 +887,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { func testEmptyNetworkInterface() throws { try XCTSkipIf(true, "Not applicable test on some network environment") - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) try startListener() let urls = self.listener!.urls! @@ -789,7 +931,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { } func testMultipleReplicatorsToListener() throws { - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) try startListener() @@ -804,7 +946,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { } func testMultipleReplicatorsToReadOnlyListener() throws { - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) var config = URLEndpointListenerConfiguration(collections: [self.otherDB_defaultCollection!]) config.readOnly = true @@ -820,7 +962,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { } func testReadOnlyListener() throws { - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) let doc1 = createDocument() try self.defaultCollection!.save(document: doc1) @@ -835,7 +977,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { } func testReplicatorServerCertificate() throws { - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) let x1 = allowOverfillExpectation(description: "idle") let x2 = expectation(description: "stopped") @@ -874,7 +1016,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { } func testReplicatorServerCertificateWithTLSError() throws { - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) var x1 = expectation(description: "stopped") @@ -963,7 +1105,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { } func testAcceptOnlySelfSignedServerCertificate() throws { - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) // Listener: let listener = try startListener() @@ -987,7 +1129,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { } func testPinnedServerCertificate() throws { - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) // Listener: let listener = try startListener() @@ -1016,7 +1158,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { try XCTSkipIf(true, "CBL-7005 : importIdentityWithData not working on newer macOS") #endif - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) let data = try dataFromResource(name: "identity/certs", ofType: "p12") var identity: TLSIdentity! @@ -1088,7 +1230,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { } func testTLSPasswordListenerAuthenticator() throws { - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) let doc1 = createDocument() try self.otherDB_defaultCollection!.save(document: doc1) @@ -1147,7 +1289,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { try XCTSkipIf(true, "CBL-7005 : importIdentityWithData not working on newer macOS") #endif - if !keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) try TLSIdentity.deleteIdentity(withLabel: serverCertLabel) let data = try dataFromResource(name: "identity/certs", ofType: "p12") @@ -1191,7 +1333,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { try XCTSkipIf(true, "CBL-7005 : importIdentityWithData not working on newer macOS") #endif - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) let data = try dataFromResource(name: "identity/certs", ofType: "p12") var identity: TLSIdentity! @@ -1226,7 +1368,7 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { } func testAcceptOnlySelfSignedCertificateWithPinnedCertificate() throws { - if !self.keyChainAccessAllowed { return } + try XCTSkipUnless(self.keyChainAccessAllowed) // Listener: let listener = try startListener() @@ -1395,7 +1537,7 @@ extension URLEndpointListener { comps.scheme = self.config.disableTLS ? "ws" : "wss" comps.host = "localhost" comps.port = Int(self.port!) - comps.path = "/\(self.config.database.name)" + comps.path = "/\(self.config.collections.first!.database.name)" return comps.url! } diff --git a/Swift/Tests/UnnestArrayTest.swift b/Swift/Tests/UnnestArrayTest.swift index 8e4db4350..14d8e588c 100644 --- a/Swift/Tests/UnnestArrayTest.swift +++ b/Swift/Tests/UnnestArrayTest.swift @@ -17,7 +17,13 @@ // import XCTest +// The internal-API assertions below (spec-marked internal steps) need @testable access; +// the binary tests compile against a shipped framework, which does not support testability. +#if CBL_BINARY_TEST +import CouchbaseLiteSwift +#else @testable import CouchbaseLiteSwift +#endif /// Test Spec v1.0.1: /// https://github.com/couchbaselabs/couchbase-lite-api/blob/master/spec/tests/T0004-Unnest-Array-Index.md @@ -57,9 +63,14 @@ class UnnestArrayTest: CBLTestCase { try loadJSONResource("profiles_100", collection: profiles) let config = ArrayIndexConfiguration(path: "contacts") try profiles.createIndex(withName: "contacts", config: config) + XCTAssertEqual(try profiles.indexes(), ["contacts"]) + + // Step 5 uses an internal API to verify the index info: + #if !CBL_BINARY_TEST let indexes = try profiles.indexesInfo() XCTAssertEqual(indexes!.count, 1) XCTAssertEqual(indexes![0]["expr"] as! String, "") + #endif } /// 3. TestCreateArrayIndexWithPathAndExpressions @@ -78,8 +89,13 @@ class UnnestArrayTest: CBLTestCase { try loadJSONResource("profiles_100", collection: profiles) let config = ArrayIndexConfiguration(path: "contacts",expressions: ["address.city", "address.state"]) try profiles.createIndex(withName: "contacts", config: config) + XCTAssertEqual(try profiles.indexes(), ["contacts"]) + + // Step 5 uses an internal API to verify the index info: + #if !CBL_BINARY_TEST let indexes = try profiles.indexesInfo() XCTAssertEqual(indexes!.count, 1) XCTAssertEqual(indexes![0]["expr"] as! String, "address.city,address.state") + #endif } } diff --git a/Swift/Tests/VectorSearchTest+Lazy.swift b/Swift/Tests/VectorSearchTest+Lazy.swift index 1de694223..5d037c265 100644 --- a/Swift/Tests/VectorSearchTest+Lazy.swift +++ b/Swift/Tests/VectorSearchTest+Lazy.swift @@ -17,7 +17,7 @@ // import XCTest -@testable import CouchbaseLiteSwift +import CouchbaseLiteSwift /// /// Test Spec : diff --git a/Swift/Tests/VectorSearchTest.swift b/Swift/Tests/VectorSearchTest.swift index 15eb361d0..254834b32 100644 --- a/Swift/Tests/VectorSearchTest.swift +++ b/Swift/Tests/VectorSearchTest.swift @@ -17,7 +17,7 @@ // import XCTest -@testable import CouchbaseLiteSwift +import CouchbaseLiteSwift class VectorSearchTest: CBLTestCase { let dinnerVector = [0.03193166106939316, 0.032055653631687164, 0.07188114523887634, -0.09893740713596344, -0.07693558186292648, 0.07570040225982666, 0.42786234617233276, -0.11442682892084122, -0.7863243818283081, -0.47983086109161377, -0.10168658196926117, 0.10985997319221497, -0.15261511504650116, -0.08458329737186432, -0.16363860666751862, -0.20225222408771515, -0.2593214809894562, -0.032738097012043, -0.16649988293647766, -0.059701453894376755, 0.17472036182880402, -0.007310086861252785, -0.13918264210224152, -0.07260780036449432, -0.02461239881813526, -0.04195880889892578, -0.15714778006076813, 0.48038315773010254, 0.7536261677742004, 0.41809454560279846, -0.17144775390625, 0.18296195566654205, -0.10611499845981598, 0.11669538915157318, 0.07423929125070572, -0.3105475902557373, -0.045081984251737595, -0.18190748989582062, 0.22430984675884247, 0.05735112354159355, -0.017394868656992912, -0.148889422416687, -0.20618586242198944, -0.1446581482887268, 0.061972495168447495, 0.07787969708442688, 0.14225411415100098, 0.20560632646083832, 0.1786964386701584, -0.380594402551651, -0.18301603198051453, -0.19542981684207916, 0.3879885971546173, -0.2219538390636444, 0.11549852043390274, -0.0021717497147619724, -0.10556972026824951, 0.030264658853411674, 0.16252967715263367, 0.06010117009282112, -0.045007310807704926, 0.02435707487165928, 0.12623260915279388, -0.12688252329826355, -0.3306281864643097, 0.06452160328626633, 0.0707000121474266, -0.04959108680486679, -0.2567063570022583, -0.01878536120057106, -0.10857286304235458, -0.01754194125533104, -0.0713721290230751, 0.05946013703942299, -0.1821729987859726, -0.07293688505887985, -0.2778160572052002, 0.17880073189735413, -0.04669278487563133, 0.05351974070072174, -0.23292849957942963, 0.05746332183480263, 0.15462779998779297, -0.04772235080599785, -0.003306782804429531, 0.058290787041187286, 0.05908169597387314, 0.00504430802538991, -0.1262340396642685, 0.11612161248922348, 0.25303348898887634, 0.18580256402492523, 0.09704313427209854, -0.06087183952331543, 0.19697663187980652, -0.27528849244117737, -0.0837797075510025, -0.09988483041524887, -0.20565757155418396, 0.020984146744012833, 0.031014855951070786, 0.03521743416786194, -0.05171370506286621, 0.009112107567489147, -0.19296088814735413, -0.19363830983638763, 0.1591167151927948, -0.02629968523979187, -0.1695055067539215, -0.35807400941848755, -0.1935291737318039, -0.17090126872062683, -0.35123637318611145, -0.20035606622695923, -0.03487539291381836, 0.2650701701641083, -0.1588021069765091, 0.32268261909484863, -0.024521857500076294, -0.11985184997320175, 0.14826008677482605, 0.194917231798172, 0.07971998304128647, 0.07594677060842514, 0.007186363451182842, -0.14641280472278595, 0.053229596465826035, 0.0619836151599884, 0.003207010915502906, -0.12729716300964355, 0.13496214151382446, 0.107656329870224, -0.16516226530075073, -0.033881571143865585, -0.11175122112035751, -0.005806141998618841, -0.4765360355377197, 0.11495379358530045, 0.1472187340259552, 0.3781401813030243, 0.10045770555734634, -0.1352398842573166, -0.17544329166412354, -0.13191302120685577, -0.10440415143966675, 0.34598618745803833, 0.09728766977787018, -0.25583627820014954, 0.035236816853284836, 0.16205145418643951, -0.06128586828708649, 0.13735555112361908, 0.11582338809967041, -0.10182418674230576, 0.1370954066514969, 0.15048766136169434, 0.06671152263879776, -0.1884871870279312, -0.11004580557346344, 0.24694739282131195, -0.008159132674336433, -0.11668405681848526, -0.01214478351175785, 0.10379738360643387, -0.1626262664794922, 0.09377897530794144, 0.11594484746456146, -0.19621512293815613, 0.26271334290504456, 0.04888357222080231, -0.10103251039981842, 0.33250945806503296, 0.13565145432949066, -0.23888370394706726, -0.13335271179676056, -0.0076894499361515045, 0.18256276845932007, 0.3276212215423584, -0.06567271053791046, -0.1853761374950409, 0.08945729583501816, 0.13876311480998993, 0.09976287186145782, 0.07869105041027069, -0.1346970647573471, 0.29857659339904785, 0.1329529583454132, 0.11350086331367493, 0.09112624824047089, -0.12515446543693542, -0.07917925715446472, 0.2881546914577484, -1.4532661225530319e-05, -0.07712751626968384, 0.21063975989818573, 0.10858846455812454, -0.009552721865475178, 0.1629313975572586, -0.39703384041786194, 0.1904662847518921, 0.18924959003925323, -0.09611514210700989, 0.001136621693149209, -0.1293390840291977, -0.019481558352708817, 0.09661063551902771, -0.17659670114517212, 0.11671938002109528, 0.15038564801216125, -0.020016824826598167, -0.20642194151878357, 0.09050136059522629, -0.1768183410167694, -0.2891409397125244, 0.04596589505672455, -0.004407480824738741, 0.15323616564273834, 0.16503025591373444, 0.17370983958244324, 0.02883041836321354, 0.1463884711265564, 0.14786243438720703, -0.026439940556883812, -0.03113352134823799, 0.10978181660175323, 0.008928884752094746, 0.24813824892044067, -0.06918247044086456, 0.06958142668008804, 0.17475970089435577, 0.04911438003182411, 0.17614248394966125, 0.19236832857131958, -0.1425514668226242, -0.056531358510255814, -0.03680772706866264, -0.028677923604846, -0.11353116482496262, 0.012293893843889236, -0.05192646384239197, 0.20331953465938568, 0.09290937334299088, 0.15373043715953827, 0.21684466302394867, 0.40546831488609314, -0.23753701150417328, 0.27929359674453735, -0.07277711480855942, 0.046813879162073135, 0.06883064657449722, -0.1033223420381546, 0.15769273042678833, 0.21685580909252167, -0.00971329677850008, 0.17375953495502472, 0.027193285524845123, -0.09943609684705734, 0.05770351365208626, 0.0868956446647644, -0.02671697922050953, -0.02979189157485962, 0.024517420679330826, -0.03931192681193352, -0.35641804337501526, -0.10590721666812897, -0.2118944674730301, -0.22070199251174927, 0.0941486731171608, 0.19881175458431244, 0.1815279871225357, -0.1256905049085617, -0.0683583989739418, 0.19080783426761627, -0.009482398629188538, -0.04374842345714569, 0.08184348791837692, 0.20070189237594604, 0.039221834391355515, -0.12251003831624985, -0.04325549304485321, 0.03840530663728714, -0.19840988516807556, -0.13591833412647247, 0.03073180839419365, 0.1059495136141777, -0.10656466335058212, 0.048937033861875534, -0.1362423598766327, -0.04138947278261185, 0.10234509408473969, 0.09793911874294281, 0.1391254961490631, -0.0906999260187149, 0.146945983171463, 0.14941848814487457, 0.23930180072784424, 0.36049938201904297, 0.0239607822149992, 0.08884347230195999, 0.061145078390836716] diff --git a/xcconfigs/CBL_ObjC_Binary_Tests_iOS_App.xcconfig b/xcconfigs/CBL_Binary_Tests_iOS_App.xcconfig similarity index 97% rename from xcconfigs/CBL_ObjC_Binary_Tests_iOS_App.xcconfig rename to xcconfigs/CBL_Binary_Tests_iOS_App.xcconfig index a2588813a..3ee9939a0 100644 --- a/xcconfigs/CBL_ObjC_Binary_Tests_iOS_App.xcconfig +++ b/xcconfigs/CBL_Binary_Tests_iOS_App.xcconfig @@ -1,5 +1,5 @@ // -// CBL_ObjC_Binary_Tests_iOS_App.xcconfig +// CBL_Binary_Tests_iOS_App.xcconfig // CouchbaseLite // // Copyright (c) 2026 Couchbase, Inc All rights reserved. diff --git a/xcconfigs/CBL_EE_Swift_Binary_Tests.xcconfig b/xcconfigs/CBL_EE_Swift_Binary_Tests.xcconfig new file mode 100644 index 000000000..608ae0084 --- /dev/null +++ b/xcconfigs/CBL_EE_Swift_Binary_Tests.xcconfig @@ -0,0 +1,21 @@ +// +// CBL_EE_Swift_Binary_Tests.xcconfig +// CouchbaseLite +// +// Copyright (c) 2026 Couchbase, Inc All rights reserved. +// +// 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. +// + +#include "CBL_EE_Common.xcconfig" +#include "CBL_Swift_Binary_Tests.xcconfig" diff --git a/xcconfigs/CBL_Swift_Binary_Tests.xcconfig b/xcconfigs/CBL_Swift_Binary_Tests.xcconfig new file mode 100644 index 000000000..3ec2fdf57 --- /dev/null +++ b/xcconfigs/CBL_Swift_Binary_Tests.xcconfig @@ -0,0 +1,71 @@ +// +// CBL_Swift_Binary_Tests.xcconfig +// CouchbaseLite +// +// Copyright (c) 2026 Couchbase, Inc All rights reserved. +// +// 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. +// + +#include "CBL_OS_Target_Versions.xcconfig" + +// Black-box binary test suite: compiles against the public CouchbaseLiteSwift +// API only. The target has no dependency on the framework target; it compiles +// and links against a staged binary framework in BinaryTests/Frameworks, +// populated by Scripts/prepare_binary_test.sh. Deliberately has no internal or +// vendor (LiteCore/fleece) header search paths — any internal import is a +// compile error, and the shipped framework has no testability, so any +// @testable import fails as well. + +// Marks the target as a binary test suite. Swift test code guards white-box +// sections with #if !CBL_BINARY_TEST; the bridging helper (CBLTestHelper.m) +// uses the preprocessor definition. Set here rather than detected in code, so +// that a target missing it fails to build instead of silently dropping the +// guarded tests. +SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) CBL_BINARY_TEST +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) CBL_BINARY_TEST=1 + +GENERATE_INFOPLIST_FILE = YES +PRODUCT_BUNDLE_IDENTIFIER = com.couchbase.CouchbaseLiteSwiftBinaryTests +PRODUCT_NAME = CouchbaseLiteSwiftBinaryTests + +// Test code is not held to warnings-as-errors, same as the other test targets. +GCC_TREAT_WARNINGS_AS_ERRORS = NO + +// The staged binary xcframeworks in BinaryTests/Frameworks (see +// Scripts/prepare_binary_test.sh) are linked and embedded via the target's +// build phases; the embedded frameworks are loaded from the runpath below. +LD_RUNPATH_SEARCH_PATHS = $(inherited) @loader_path/Frameworks + +// Guarantee at compile time that the binary tests use only public API: without +// this, Xcode's project-wide header map would let the bridging header's quoted +// imports resolve internal headers, bypassing the search paths. +USE_HEADERMAP = NO + +// Test-owned directories only — never product header directories. +HEADER_SEARCH_PATHS = $(SRCROOT)/Swift/Tests + +SWIFT_OBJC_BRIDGING_HEADER = Swift/Tests/CBL Swift Tests-Bridging-Header.h + +// Need to disable as using Objective-C Bridging Header: +BUILD_LIBRARY_FOR_DISTRIBUTION = NO + +// iOS runs app-hosted: keychain APIs fail with errSecMissingEntitlement +// (-34018) in a hostless test runner, so the keychain tests need a real app +// process. macOS has no such restriction and runs hostless. BUNDLE_LOADER is +// deliberately unset — this bundle links the staged framework itself rather +// than resolving symbols through the host. +TEST_HOST[sdk=iphone*] = $(BUILT_PRODUCTS_DIR)/CBL_Binary_Tests.app/CBL_Binary_Tests + +// Signed for device runs like the other iOS test bundles; macOS needs no identity. +CODE_SIGN_IDENTITY[sdk=iphone*] = iPhone Developer From 49611e200de41872bfad6cf48f0612c17464a9de Mon Sep 17 00:00:00 2001 From: Pasin Suriyentrakorn Date: Tue, 25 Aug 2026 20:34:48 -0700 Subject: [PATCH 2/2] Fix per co-pilot code review - Ensure CBLExpectExceptionsEnd is always called via try/finally blocks. - Replace force unwraps and force casts with safe unwrapping and XCTFail or XCTUnwrap in URLEndpointListenerTest. --- Swift/Tests/CBLTestHelper.m | 20 +++++++++----- Swift/Tests/URLEndpointListenerTest.swift | 33 +++++++++++++---------- 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/Swift/Tests/CBLTestHelper.m b/Swift/Tests/CBLTestHelper.m index 0f1359892..55824f84b 100644 --- a/Swift/Tests/CBLTestHelper.m +++ b/Swift/Tests/CBLTestHelper.m @@ -35,22 +35,30 @@ @implementation CBLTestHelper + (void) allowExceptionIn: (void (^)(void))block { CBLExpectExceptionsBegin(); - block(); - CBLExpectExceptionsEnd(); + @try { + block(); + } + @finally { + CBLExpectExceptionsEnd(); + } } + (BOOL) catchException: (void(^)(void))tryBlock error: (NSError **)error { + CBLExpectExceptionsBegin(); @try { - CBLExpectExceptionsBegin(); tryBlock(); - CBLExpectExceptionsEnd(); return YES; } @catch (NSException *exception) { - *error = [[NSError alloc] initWithDomain: exception.name code: 0 - userInfo: exception.userInfo]; + if (error) { + *error = [[NSError alloc] initWithDomain: exception.name code: 0 + userInfo: exception.userInfo]; + } return NO; } + @finally { + CBLExpectExceptionsEnd(); + } } @end diff --git a/Swift/Tests/URLEndpointListenerTest.swift b/Swift/Tests/URLEndpointListenerTest.swift index 1c8fab0f0..f31fa2a74 100644 --- a/Swift/Tests/URLEndpointListenerTest.swift +++ b/Swift/Tests/URLEndpointListenerTest.swift @@ -113,8 +113,10 @@ class URLEndpointListenerTest: ReplicatorTest { var result: CFTypeRef? var status = SecItemCopyMatching(query as CFDictionary, &result) if status != errSecItemNotFound { - XCTAssertEqual(status, errSecSuccess, "Cannot query identities (OSStatus = \(status))") - let identities = result as! [SecIdentity] + guard status == errSecSuccess, let identities = result as? [SecIdentity] else { + XCTFail("Cannot query identities (OSStatus = \(status))") + return + } for identity in identities { var certRef: SecCertificate? guard SecIdentityCopyCertificate(identity, &certRef) == errSecSuccess, let cert = certRef else { @@ -141,8 +143,10 @@ class URLEndpointListenerTest: ReplicatorTest { if status == errSecItemNotFound { return } - XCTAssertEqual(status, errSecSuccess, "Cannot query certificates (OSStatus = \(status))") - let certs = result as! [SecCertificate] + guard status == errSecSuccess, let certs = result as? [SecCertificate] else { + XCTFail("Cannot query certificates (OSStatus = \(status))") + return + } for cert in certs { let name = SecCertificateCopySubjectSummary(cert) as String? if name == anonymousIdentityCommonName { @@ -521,30 +525,28 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { // The cleanup helpers sweep by the common name discovered from a certificate // that the product mints; the discovery must succeed for the cleanup to work: - let commonName = anonymousIdentityCommonName() - XCTAssertNotNil(commonName) + let commonName = try XCTUnwrap(anonymousIdentityCommonName()) // Starting a TLS listener without an identity creates an anonymous identity: let config = URLEndpointListenerConfiguration(collections: [self.otherDB_defaultCollection!]) try startListener(withConfig: config) - let identity = self.listener!.tlsIdentity - XCTAssertNotNil(identity) + let identity = try XCTUnwrap(self.listener!.tlsIdentity) // The discovered name must match the certificate of an independently created // anonymous identity: - XCTAssertEqual(SecCertificateCopySubjectSummary(identity!.certs[0]) as String?, commonName) + XCTAssertEqual(SecCertificateCopySubjectSummary(identity.certs[0]) as String?, commonName) try stopListener() // The identity created above must be found by the discovered name; this guards // against the zero-count checks below passing vacuously with a wrong name: - XCTAssertGreaterThan(keychainItemCount(kSecClassIdentity, commonName: commonName!), 0) + XCTAssertGreaterThan(keychainItemCount(kSecClassIdentity, commonName: commonName), 0) cleanUpAnonymousIdentities() // No anonymous identities nor certificates should be left in the keychain: - XCTAssertEqual(keychainItemCount(kSecClassIdentity, commonName: commonName!), 0) - XCTAssertEqual(keychainItemCount(kSecClassCertificate, commonName: commonName!), 0) + XCTAssertEqual(keychainItemCount(kSecClassIdentity, commonName: commonName), 0) + XCTAssertEqual(keychainItemCount(kSecClassCertificate, commonName: commonName), 0) } func keychainItemCount(_ itemClass: CFString, commonName: String) -> Int { @@ -554,10 +556,13 @@ class URLEndpointListenerTest_Main: URLEndpointListenerTest { var result: CFTypeRef? let status = SecItemCopyMatching(query as CFDictionary, &result) if status == errSecItemNotFound { return 0 } - XCTAssertEqual(status, errSecSuccess, "Cannot query keychain items (OSStatus = \(status))") + guard status == errSecSuccess, let items = result as? [AnyObject] else { + XCTFail("Cannot query keychain items (OSStatus = \(status))") + return 0 + } var count = 0 - for item in (result as! [AnyObject]) { + for item in items { var certRef: SecCertificate? if itemClass == kSecClassIdentity { guard SecIdentityCopyCertificate(item as! SecIdentity, &certRef) == errSecSuccess,