From bf5a87e89cad3959325f3d1c2cea6afde62ae8b6 Mon Sep 17 00:00:00 2001 From: Hadi Dbouk Date: Fri, 1 Nov 2024 21:15:40 +0200 Subject: [PATCH] refactor(tests): use Swift Testing instead of Quick and Nimble --- Package.resolved | 43 ---- Package.swift | 4 - ...ft => AnyCurrentValuePublisherTests.swift} | 33 ++- .../CombineTests/CombineLatestManySpec.swift | 194 ------------------ .../CombineTests/CombineLatestManyTests.swift | 191 +++++++++++++++++ .../CombineTests/SinkForLifetimeSpec.swift | 52 ----- .../CombineTests/SinkForLifetimeTests.swift | 46 +++++ 7 files changed, 252 insertions(+), 311 deletions(-) delete mode 100644 Package.resolved rename Tests/FueledUtils/CombineTests/{AnyCurrentValuePublisherSpec.swift => AnyCurrentValuePublisherTests.swift} (50%) delete mode 100644 Tests/FueledUtils/CombineTests/CombineLatestManySpec.swift create mode 100644 Tests/FueledUtils/CombineTests/CombineLatestManyTests.swift delete mode 100644 Tests/FueledUtils/CombineTests/SinkForLifetimeSpec.swift create mode 100644 Tests/FueledUtils/CombineTests/SinkForLifetimeTests.swift diff --git a/Package.resolved b/Package.resolved deleted file mode 100644 index 4915d1ad..00000000 --- a/Package.resolved +++ /dev/null @@ -1,43 +0,0 @@ -{ - "object": { - "pins": [ - { - "package": "CwlCatchException", - "repositoryURL": "https://github.com/mattgallagher/CwlCatchException.git", - "state": { - "branch": null, - "revision": "35f9e770f54ce62dd8526470f14c6e137cef3eea", - "version": "2.1.1" - } - }, - { - "package": "CwlPreconditionTesting", - "repositoryURL": "https://github.com/mattgallagher/CwlPreconditionTesting.git", - "state": { - "branch": null, - "revision": "c21f7bab5ca8eee0a9998bbd17ca1d0eb45d4688", - "version": "2.1.0" - } - }, - { - "package": "Nimble", - "repositoryURL": "https://github.com/Quick/Nimble.git", - "state": { - "branch": null, - "revision": "c93f16c25af5770f0d3e6af27c9634640946b068", - "version": "9.2.1" - } - }, - { - "package": "Quick", - "repositoryURL": "https://github.com/Quick/Quick.git", - "state": { - "branch": null, - "revision": "bd86ca0141e3cfb333546de5a11ede63f0c4a0e6", - "version": "4.0.0" - } - } - ] - }, - "version": 1 -} diff --git a/Package.swift b/Package.swift index d4a89e21..c0efe793 100644 --- a/Package.swift +++ b/Package.swift @@ -23,8 +23,6 @@ let package = Package( ), ], dependencies: [ - .package(url: "https://github.com/Quick/Quick.git", from: "4.0.0"), - .package(url: "https://github.com/Quick/Nimble.git", from: "9.0.0"), ], targets: [ .target( @@ -53,8 +51,6 @@ let package = Package( name: "FueledUtilsCombineTests", dependencies: [ "FueledUtilsCombine", - "Quick", - "Nimble", ], path: "Tests/FueledUtils/CombineTests" ), diff --git a/Tests/FueledUtils/CombineTests/AnyCurrentValuePublisherSpec.swift b/Tests/FueledUtils/CombineTests/AnyCurrentValuePublisherTests.swift similarity index 50% rename from Tests/FueledUtils/CombineTests/AnyCurrentValuePublisherSpec.swift rename to Tests/FueledUtils/CombineTests/AnyCurrentValuePublisherTests.swift index 15cd8212..5e81ffc8 100644 --- a/Tests/FueledUtils/CombineTests/AnyCurrentValuePublisherSpec.swift +++ b/Tests/FueledUtils/CombineTests/AnyCurrentValuePublisherTests.swift @@ -1,4 +1,4 @@ -// Copyright © 2020 Fueled Digital Media, LLC +// Copyright © 2024 Fueled Digital Media, LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,23 +15,20 @@ @testable import FueledUtilsCombine import Combine -import Quick -import Nimble +import Testing -class AnyCurrentValuePublisherSpec: QuickSpec { - override func spec() { - describe("AnyCurrentValuePublisherSpec") { - describe("Initialization") { - it("Should initialize with a stored value") { - let publisher = AnyCurrentValuePublisher(1) - expect(publisher.value) == 1 - } - it("Should initialize with a nested CurrentValueSubject") { - let subject = CurrentValueSubject(2) - let publisher = AnyCurrentValuePublisher(subject) - expect(publisher.value) == 2 - } - } - } +@Suite("AnyCurrentValuePluisher Initialization") +struct AnyCurrentValuePublisherTests { + @Test("Should initialize with a stored value") + func withAStoredValue() { + let publisher = AnyCurrentValuePublisher(1) + #expect(publisher.value == 1) + } + + @Test("Should initialize with a nested CurrentValueSubject") + func withNestedCurrentValueSubject() { + let subject = CurrentValueSubject(2) + let publisher = AnyCurrentValuePublisher(subject) + #expect(publisher.value == 2) } } diff --git a/Tests/FueledUtils/CombineTests/CombineLatestManySpec.swift b/Tests/FueledUtils/CombineTests/CombineLatestManySpec.swift deleted file mode 100644 index 8dcc1bd8..00000000 --- a/Tests/FueledUtils/CombineTests/CombineLatestManySpec.swift +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright © 2020 Fueled Digital Media, LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -@testable import FueledUtilsCombine - -import Combine -import Foundation -import Quick -import Nimble - -class CombineLatestManySpec: QuickSpec { - private var cancellables: [AnyCancellable]! - - override func spec() { - describe("Publishers.CombineLatestMany") { - beforeEach { - self.cancellables = [] - } - describe("with zero elements") { - it("should complete instantaneously with an empty array and no errors") { - var completionCount = 0 - var valueCount = 0 - Publishers.CombineLatestMany<[Just]>([]).sink( - receiveCompletion: { completion in - let isFinished: Bool - switch completion { - case .failure: - isFinished = false - case .finished: - isFinished = true - } - expect(isFinished) == true - completionCount += 1 - }, - receiveValue: { values in - expect(values).to(haveCount(0)) - valueCount += 1 - } - ) - .store(in: &self.cancellables) - - expect(completionCount) == 1 - expect(valueCount) == 1 - } - } - describe("with two elements") { - it("should match the native CombineLatest behavior") { - var completionCount = 0 - var valueCount = 0 - var nativeValues: [Int] = [] - var manyValues: [Int] = [] - - func publisher(_ value: Int) -> AnyPublisher { - Just(value).delay(for: 0.3, scheduler: DispatchQueue.main) - .eraseToAnyPublisher() - } - - Publishers.CombineLatest( - publisher(1).append(publisher(3)), - publisher(2) - ) - .sink( - receiveCompletion: { completion in - let isFinished: Bool - switch completion { - case .failure: - isFinished = false - case .finished: - isFinished = true - } - expect(isFinished) == true - completionCount += 1 - }, - receiveValue: { value1, value2 in - nativeValues = [value1, value2] - valueCount += 1 - } - ) - .store(in: &self.cancellables) - - Publishers.CombineLatestMany( - [ - publisher(1).append(publisher(3)).eraseToAnyPublisher(), - publisher(2).eraseToAnyPublisher() - ] - ) - .sink( - receiveCompletion: { completion in - let isFinished: Bool - switch completion { - case .failure: - isFinished = false - case .finished: - isFinished = true - } - expect(isFinished) == true - completionCount += 1 - }, - receiveValue: { values in - manyValues = values - valueCount += 1 - } - ) - .store(in: &self.cancellables) - - expect(completionCount).toEventually(equal(2)) - expect(valueCount).toEventually(equal(4)) - - expect(nativeValues).toEventually(equal([3, 2])) - expect(manyValues).toEventually(equal([3, 2])) - } - } - describe("with two publishers") { - it("should correctly interrupt the publishers when interrupted") { - var subscriptionCount = 0 - var cancelCount = 0 - var nativeValueCount = 0 - var nativeValues: [Int] = [] - var manyValueCount = 0 - var manyValues: [Int] = [] - - func publisher(_ value: Int) -> AnyPublisher { - Just(1).delay(for: 0.5, scheduler: DispatchQueue.main) - .handleEvents( - receiveSubscription: { _ in - subscriptionCount += 1 - }, - receiveCancel: { - cancelCount += 1 - } - ) - .eraseToAnyPublisher() - } - - var cancellable = Publishers.CombineLatest( - publisher(1), - publisher(2) - ) - .handleEvents( - receiveCancel: { - cancelCount += 1 - } - ) - .sink( - receiveValue: { value1, value2 in - nativeValues = [value1, value2] - nativeValueCount += 1 - } - ) - - cancellable = Publishers.CombineLatestMany( - [ - publisher(1), - publisher(2) - ] - ) - .handleEvents( - receiveCancel: { - cancelCount += 1 - } - ) - .sink( - receiveValue: { values in - manyValues = values - manyValueCount += 1 - } - ) - - cancellable.cancel() - - expect(subscriptionCount) == 4 - expect(cancelCount).toEventually(equal(6)) - - expect(nativeValueCount).toEventually(equal(0)) - expect(nativeValues).toEventually(haveCount(0)) - - expect(manyValueCount).toEventually(equal(0)) - expect(manyValues).toEventually(haveCount(0)) - } - } - } - } -} diff --git a/Tests/FueledUtils/CombineTests/CombineLatestManyTests.swift b/Tests/FueledUtils/CombineTests/CombineLatestManyTests.swift new file mode 100644 index 00000000..17eda705 --- /dev/null +++ b/Tests/FueledUtils/CombineTests/CombineLatestManyTests.swift @@ -0,0 +1,191 @@ +// Copyright © 2024 Fueled Digital Media, LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +@testable import FueledUtilsCombine + +import Combine +import Foundation +import Testing + +@Suite("Combine Latest Many") +struct CombineLatestManyTests { + private var cancellables = [AnyCancellable]() + + @Test("With zero elements it should complete with an empty array and no errors") + mutating func withZeroElements() { + var completionCount = 0 + var valueCount = 0 + let publishers: [AnyPublisher] = [] + Publishers.CombineLatestMany(publishers).sink( + receiveCompletion: { completion in + let isFinished: Bool + switch completion { + case .failure: + isFinished = false + case .finished: + isFinished = true + } + #expect(isFinished == true) + completionCount += 1 + }, + receiveValue: { values in + #expect(values.count == .zero) + valueCount += 1 + } + ) + .store(in: &self.cancellables) + + #expect(completionCount == 1) + #expect(valueCount == 1) + } + + @Test("With two elements should match the native CombineLatest behavior") + mutating func withTwoElements() async { + var completionCount = 0 + var valueCount = 0 + var nativeValues: [Int] = [] + var manyValues: [Int] = [] + + func publisher(_ value: Int) -> AnyPublisher { + Just(value) + .delay(for: 0.1, scheduler: DispatchQueue.main) + .eraseToAnyPublisher() + } + + Publishers.CombineLatest( + publisher(1).append(publisher(3)), + publisher(2) + ) + .sink( + receiveCompletion: { completion in + let isFinished: Bool + switch completion { + case .failure: + isFinished = false + case .finished: + isFinished = true + } + #expect(isFinished == true) + completionCount += 1 + }, + receiveValue: { value1, value2 in + nativeValues = [value1, value2] + valueCount += 1 + } + ) + .store(in: &self.cancellables) + + Publishers.CombineLatestMany( + [ + publisher(1).append(publisher(3)).eraseToAnyPublisher(), + publisher(2).eraseToAnyPublisher() + ] + ) + .sink( + receiveCompletion: { completion in + let isFinished: Bool + switch completion { + case .failure: + isFinished = false + case .finished: + isFinished = true + } + #expect(isFinished == true) + completionCount += 1 + }, + receiveValue: { values in + manyValues = values + valueCount += 1 + } + ) + .store(in: &self.cancellables) + + try? await Task.sleep(for: .seconds(0.3)) + + #expect(completionCount == 2) + #expect(valueCount == 4) + + #expect(nativeValues == [3, 2]) + #expect(manyValues == [3, 2]) + } + + @Test("With two publishers should correctly interrupt the publishers when interrupted") + mutating func withTwoPublishers() async { + var subscriptionCount = 0 + var cancelCount = 0 + var nativeValueCount = 0 + var nativeValues: [Int] = [] + var manyValueCount = 0 + var manyValues: [Int] = [] + + func publisher(_ value: Int) -> AnyPublisher { + Just(1) + .delay(for: 0.1, scheduler: DispatchQueue.main) + .handleEvents( + receiveSubscription: { _ in + subscriptionCount += 1 + }, + receiveCancel: { + cancelCount += 1 + } + ) + .eraseToAnyPublisher() + } + + var cancellable = Publishers.CombineLatest( + publisher(1), + publisher(2) + ) + .handleEvents( + receiveCancel: { + cancelCount += 1 + } + ) + .sink( + receiveValue: { value1, value2 in + nativeValues = [value1, value2] + nativeValueCount += 1 + } + ) + + cancellable = Publishers.CombineLatestMany( + [ + publisher(1), + publisher(2) + ] + ) + .handleEvents( + receiveCancel: { + cancelCount += 1 + } + ) + .sink( + receiveValue: { values in + manyValues = values + manyValueCount += 1 + } + ) + + cancellable.cancel() + + #expect(subscriptionCount == 4) + #expect(cancelCount == 6) + + #expect(nativeValueCount == 0) + #expect(nativeValues.count == 0) + + #expect(manyValueCount == 0) + #expect(manyValues.count == 0) + } +} diff --git a/Tests/FueledUtils/CombineTests/SinkForLifetimeSpec.swift b/Tests/FueledUtils/CombineTests/SinkForLifetimeSpec.swift deleted file mode 100644 index edab0ba1..00000000 --- a/Tests/FueledUtils/CombineTests/SinkForLifetimeSpec.swift +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright © 2020 Fueled Digital Media, LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -@testable import FueledUtilsCombine - -import Combine -import Foundation -import Quick -import Nimble - -class SinkForLifetimeSpec: QuickSpec { - override func spec() { - describe("sinkForLifeTimeOf()") { - it("should cancel itself automatically when the object becomes out of scope") { - var object: NSObject! - var valueCount = 0 - var cancelCount = 0 - do { - object = NSObject() - Timer.publish(every: 0.42, on: .current, in: .common) - .autoconnect() - .handleEvents( - receiveCancel: { - cancelCount += 1 - } - ) - .sinkForLifetimeOf(object) { _ in - valueCount += 1 - } - } - - DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { - object = nil - } - - expect(valueCount).toEventually(equal(2)) - expect(cancelCount).toEventually(equal(1), timeout: .seconds(2)) - } - } - } -} diff --git a/Tests/FueledUtils/CombineTests/SinkForLifetimeTests.swift b/Tests/FueledUtils/CombineTests/SinkForLifetimeTests.swift new file mode 100644 index 00000000..1deb7c3c --- /dev/null +++ b/Tests/FueledUtils/CombineTests/SinkForLifetimeTests.swift @@ -0,0 +1,46 @@ +// Copyright © 2024 Fueled Digital Media, LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +@testable import FueledUtilsCombine + +import Combine +import Foundation +import Testing + +@Suite("Sink For Life Time") +struct SinkForLifetimeTests { + @Test("Should cancel itself automatically when the object becomes out of scope") + func whenOutOfScope() async throws { + var object: NSObject! + var valueCount = 0 + var cancelCount = 0 + object = NSObject() + Timer.publish(every: 0.42, on: .main, in: .common) + .autoconnect() + .handleEvents( + receiveCancel: { + cancelCount += 1 + } + ) + .sinkForLifetimeOf(object) { _ in + valueCount += 1 + } + + try await Task.sleep(for: .seconds(1)) + object = nil + + #expect(valueCount == 2) + #expect(cancelCount == 1) + } +}