From 85d8eae902c49f39d106318bd517ad8357ffd766 Mon Sep 17 00:00:00 2001 From: Azat Date: Sun, 4 Sep 2022 23:49:14 +0300 Subject: [PATCH 1/4] Add cleaning of the allInvocations property in the MockingContext on calling the clearInvocations method --- Sources/MockingbirdFramework/Mocking/MockingContext.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/MockingbirdFramework/Mocking/MockingContext.swift b/Sources/MockingbirdFramework/Mocking/MockingContext.swift index 1ddde7c7..e69ab58d 100644 --- a/Sources/MockingbirdFramework/Mocking/MockingContext.swift +++ b/Sources/MockingbirdFramework/Mocking/MockingContext.swift @@ -75,6 +75,7 @@ import Foundation func clearInvocations() { invocations.update { $0.removeAll() } + allInvocations.update { $0.removeAll() } } func removeInvocations(before invocation: Invocation, inclusive: Bool = false) { From 1b50464dc9fb576f7c8c6a44517bdab2fac96d46 Mon Sep 17 00:00:00 2001 From: Azat Date: Fri, 24 Mar 2023 23:11:59 +0300 Subject: [PATCH 2/4] Fix eventually function --- .../xcshareddata/swiftpm/Package.resolved | 186 +++++++++--------- .../Verification/AsyncVerification.swift | 69 +++++++ 2 files changed, 161 insertions(+), 94 deletions(-) diff --git a/Mockingbird.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Mockingbird.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 40a892e2..5e90a26f 100644 --- a/Mockingbird.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Mockingbird.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,97 +1,95 @@ { - "object": { - "pins": [ - { - "package": "AEXML", - "repositoryURL": "https://github.com/tadija/AEXML.git", - "state": { - "branch": null, - "revision": "38f7d00b23ecd891e1ee656fa6aeebd6ba04ecc3", - "version": "4.6.1" - } - }, - { - "package": "PathKit", - "repositoryURL": "https://github.com/kylef/PathKit", - "state": { - "branch": null, - "revision": "3bfd2737b700b9a36565a8c94f4ad2b050a5e574", - "version": "1.0.1" - } - }, - { - "package": "SourceKitten", - "repositoryURL": "https://github.com/jpsim/SourceKitten.git", - "state": { - "branch": null, - "revision": "558628392eb31d37cb251cfe626c53eafd330df6", - "version": "0.31.1" - } - }, - { - "package": "Spectre", - "repositoryURL": "https://github.com/kylef/Spectre.git", - "state": { - "branch": null, - "revision": "26cc5e9ae0947092c7139ef7ba612e34646086c7", - "version": "0.10.1" - } - }, - { - "package": "swift-argument-parser", - "repositoryURL": "https://github.com/apple/swift-argument-parser.git", - "state": { - "branch": null, - "revision": "e1465042f195f374b94f915ba8ca49de24300a0d", - "version": "1.0.2" - } - }, - { - "package": "SwiftSyntax", - "repositoryURL": "https://github.com/apple/swift-syntax.git", - "state": { - "branch": null, - "revision": "75e60475d9d8fd5bbc16a12e0eaa2cb01b0c322e", - "version": "0.50500.0" - } - }, - { - "package": "SWXMLHash", - "repositoryURL": "https://github.com/drmohundro/SWXMLHash.git", - "state": { - "branch": null, - "revision": "9183170d20857753d4f331b0ca63f73c60764bf3", - "version": "5.0.2" - } - }, - { - "package": "XcodeProj", - "repositoryURL": "https://github.com/tuist/XcodeProj.git", - "state": { - "branch": null, - "revision": "c75c3acc25460195cfd203a04dde165395bf00e0", - "version": "8.7.1" - } - }, - { - "package": "Yams", - "repositoryURL": "https://github.com/jpsim/Yams.git", - "state": { - "branch": null, - "revision": "9ff1cc9327586db4e0c8f46f064b6a82ec1566fa", - "version": "4.0.6" - } - }, - { - "package": "ZIPFoundation", - "repositoryURL": "https://github.com/weichsel/ZIPFoundation.git", - "state": { - "branch": null, - "revision": "7254c74b49cec2cb81520523ba993c671f71b066", - "version": "0.9.14" - } + "pins" : [ + { + "identity" : "aexml", + "kind" : "remoteSourceControl", + "location" : "https://github.com/tadija/AEXML.git", + "state" : { + "revision" : "38f7d00b23ecd891e1ee656fa6aeebd6ba04ecc3", + "version" : "4.6.1" } - ] - }, - "version": 1 + }, + { + "identity" : "pathkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/kylef/PathKit", + "state" : { + "revision" : "3bfd2737b700b9a36565a8c94f4ad2b050a5e574", + "version" : "1.0.1" + } + }, + { + "identity" : "sourcekitten", + "kind" : "remoteSourceControl", + "location" : "https://github.com/jpsim/SourceKitten.git", + "state" : { + "revision" : "558628392eb31d37cb251cfe626c53eafd330df6", + "version" : "0.31.1" + } + }, + { + "identity" : "spectre", + "kind" : "remoteSourceControl", + "location" : "https://github.com/kylef/Spectre.git", + "state" : { + "revision" : "26cc5e9ae0947092c7139ef7ba612e34646086c7", + "version" : "0.10.1" + } + }, + { + "identity" : "swift-argument-parser", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-argument-parser.git", + "state" : { + "revision" : "e1465042f195f374b94f915ba8ca49de24300a0d", + "version" : "1.0.2" + } + }, + { + "identity" : "swift-syntax", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-syntax.git", + "state" : { + "revision" : "75e60475d9d8fd5bbc16a12e0eaa2cb01b0c322e", + "version" : "0.50500.0" + } + }, + { + "identity" : "swxmlhash", + "kind" : "remoteSourceControl", + "location" : "https://github.com/drmohundro/SWXMLHash.git", + "state" : { + "revision" : "9183170d20857753d4f331b0ca63f73c60764bf3", + "version" : "5.0.2" + } + }, + { + "identity" : "xcodeproj", + "kind" : "remoteSourceControl", + "location" : "https://github.com/tuist/XcodeProj.git", + "state" : { + "revision" : "c75c3acc25460195cfd203a04dde165395bf00e0", + "version" : "8.7.1" + } + }, + { + "identity" : "yams", + "kind" : "remoteSourceControl", + "location" : "https://github.com/jpsim/Yams.git", + "state" : { + "revision" : "9ff1cc9327586db4e0c8f46f064b6a82ec1566fa", + "version" : "4.0.6" + } + }, + { + "identity" : "zipfoundation", + "kind" : "remoteSourceControl", + "location" : "https://github.com/weichsel/ZIPFoundation.git", + "state" : { + "revision" : "1b662e2e7a091710ad8a963263939984e2ebf527", + "version" : "0.9.14" + } + } + ], + "version" : 2 } diff --git a/Sources/MockingbirdFramework/Verification/AsyncVerification.swift b/Sources/MockingbirdFramework/Verification/AsyncVerification.swift index 06e84fa4..e79d94da 100644 --- a/Sources/MockingbirdFramework/Verification/AsyncVerification.swift +++ b/Sources/MockingbirdFramework/Verification/AsyncVerification.swift @@ -54,6 +54,20 @@ public extension NSObject { createAsyncContext(expectation: expectation, block: block) return TestExpectation.create(from: expectation) } + + @available(iOS 13.0, *) + @discardableResult + func eventually(_ description: String = "Async verification group", + _ block: @escaping () async -> Void) -> TestExpectation { + let expectation: XCTestExpectation = { + guard let testCase = self as? XCTestCase else { + return XCTestExpectation(description: description) + } + return testCase.expectation(description: description) + }() + createAsyncContext(expectation: expectation, block: block) + return TestExpectation.create(from: expectation) + } } /// Internal helper for `eventually` async verification scopes. @@ -99,3 +113,58 @@ func createAsyncContext(expectation: XCTestExpectation, block scope: () -> Void) try? group.verify() } + +@available(iOS 13.0, *) +func createAsyncContext(expectation: XCTestExpectation, block scope: @escaping () async -> Void) { + let group = ExpectationGroup { group in + expectation.expectedFulfillmentCount = group.countExpectations() + + group.expectations.forEach({ capturedExpectation in + let observer = InvocationObserver({ (invocation, mockingContext) -> Bool in + do { + try expect(mockingContext, + handled: capturedExpectation.invocation, + using: capturedExpectation.expectation) + expectation.fulfill() + return true + } catch { + return false + } + }) + capturedExpectation.mockingContext + .addObserver(observer, for: capturedExpectation.invocation.selectorName) + }) + + group.subgroups.forEach({ subgroup in + let observer = InvocationObserver({ (invocation, mockingContext) -> Bool in + do { + try subgroup.verify() + expectation.fulfill() + return true + } catch { + return false + } + }) + subgroup.expectations.forEach({ $0.mockingContext.addObserver(observer) }) + }) + } + + let queue = DispatchQueue(label: "co.bird.mockingbird.verify.eventually") + queue.setSpecific(key: ExpectationGroup.contextKey, value: group) + + let semaphore = DispatchSemaphore(value: 1) + semaphore.wait() + + queue.sync { + + Task { + + await scope() + semaphore.signal() + } + + semaphore.wait() + } + + try? group.verify() +} From e405fbe5ac673810de0fcddf826b8823ac1be2b6 Mon Sep 17 00:00:00 2001 From: Azat Date: Sat, 25 Mar 2023 00:24:09 +0300 Subject: [PATCH 3/4] Fix eventually function --- .../Verification/AsyncVerification.swift | 10 +++++++++- .../Verification/ExpectationGroup.swift | 6 +++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Sources/MockingbirdFramework/Verification/AsyncVerification.swift b/Sources/MockingbirdFramework/Verification/AsyncVerification.swift index e79d94da..ed102d34 100644 --- a/Sources/MockingbirdFramework/Verification/AsyncVerification.swift +++ b/Sources/MockingbirdFramework/Verification/AsyncVerification.swift @@ -159,7 +159,10 @@ func createAsyncContext(expectation: XCTestExpectation, block scope: @escaping ( Task { - await scope() + await Transaction.$currentExpectationGroup.withValue(group) { + await scope() + } + semaphore.signal() } @@ -168,3 +171,8 @@ func createAsyncContext(expectation: XCTestExpectation, block scope: @escaping ( try? group.verify() } + +@available(iOS 13.0, *) +enum Transaction { + @TaskLocal static var currentExpectationGroup: ExpectationGroup? = nil +} diff --git a/Sources/MockingbirdFramework/Verification/ExpectationGroup.swift b/Sources/MockingbirdFramework/Verification/ExpectationGroup.swift index bc6e52db..a703b45f 100644 --- a/Sources/MockingbirdFramework/Verification/ExpectationGroup.swift +++ b/Sources/MockingbirdFramework/Verification/ExpectationGroup.swift @@ -55,6 +55,10 @@ class ExpectationGroup { extension DispatchQueue { class var currentExpectationGroup: ExpectationGroup? { - return DispatchQueue.getSpecific(key: ExpectationGroup.contextKey) + if #available(iOS 13.0, *) { + return Transaction.currentExpectationGroup ?? DispatchQueue.getSpecific(key: ExpectationGroup.contextKey) + } else { + return DispatchQueue.getSpecific(key: ExpectationGroup.contextKey) + } } } From d0dd9d36b2c569741b4f0ee34776e037c1a77fa1 Mon Sep 17 00:00:00 2001 From: Azat Date: Sat, 25 Mar 2023 00:57:47 +0300 Subject: [PATCH 4/4] Fix function --- .../MockingbirdFramework/Verification/AsyncVerification.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/MockingbirdFramework/Verification/AsyncVerification.swift b/Sources/MockingbirdFramework/Verification/AsyncVerification.swift index ed102d34..36900605 100644 --- a/Sources/MockingbirdFramework/Verification/AsyncVerification.swift +++ b/Sources/MockingbirdFramework/Verification/AsyncVerification.swift @@ -152,8 +152,7 @@ func createAsyncContext(expectation: XCTestExpectation, block scope: @escaping ( let queue = DispatchQueue(label: "co.bird.mockingbird.verify.eventually") queue.setSpecific(key: ExpectationGroup.contextKey, value: group) - let semaphore = DispatchSemaphore(value: 1) - semaphore.wait() + let semaphore = DispatchSemaphore(value: 0) queue.sync { @@ -167,6 +166,7 @@ func createAsyncContext(expectation: XCTestExpectation, block scope: @escaping ( } semaphore.wait() + semaphore.signal() } try? group.verify()