Implement Filter/TryFilter (#22)

* Adds filter and try filter implementations

* Implement Filter

* Remove @testable declaration

* Fix linting

* Updates tests and creates testing helper

* Fix allTests to include all tests

* Renames TestHelper to OperatorTestHelper and adds documentation

* Adds more test coverage

* Updates to use subclasses for filter / tryfilter

* Adds subscription test

* Fix subscriber demand to be lazy

* Fix CustomPublisherBase changes from master

* Fix iOS availability on test helper

* Updates availability for test functions

* Simplify Filter implementation, add more tests

* Ensure test suite consistency on Darwin and Linux

* Add missing tests to XCTestManifests.swift
This commit is contained in:
Joe Spadafora
2019-08-01 17:20:35 -04:00
committed by Sergej Jaskiewicz
parent d2b8709afb
commit d3888a3808
41 changed files with 1253 additions and 110 deletions
+14
View File
@@ -75,3 +75,17 @@ generic_type_name:
attributes:
always_on_line_above:
- "@usableFromInline"
custom_rules:
no_foundation_dependency:
included: Sources/OpenCombine
name: "No Foundation Dependency"
regex: "^import.*Foundation.*$"
message: "We don't want to depend on Foundation"
severity: error
no_dispatch_dependency:
included: Sources/OpenCombine
name: "No Dispatch Dependency"
regex: "^import.*Dispatch.*$"
message: "We don't want to depend on Dispatch"
severity: error
+4 -4
View File
@@ -52,13 +52,13 @@ install:
fi
script:
- if [[ $OPENCOMBINE_TEST == "YES" ]]; then
swift test -c debug --enable-code-coverage --sanitize thread;
make test-debug;
fi
- if [[ $OPENCOMBINE_TEST == "YES" ]]; then
swift test -c release;
make test-release;
fi
- if [[ $OPENCOMBINE_COMPATIBILITY_TEST == "YES" ]]; then
swift package generate-xcodeproj --xcconfig-overrides iOS-Combine-Compatibility.xcconfig;
make generate-compatibility-xcodeproj;
set -o pipefail && xcodebuild -scheme OpenCombine-Package -sdk iphonesimulator13.0 -destination "platform=iOS Simulator,name=iPhone Xs,OS=13.0" build test | xcpretty;
fi
- if [[ $SWIFT_LINT == "YES" ]]; then
@@ -69,7 +69,7 @@ script:
fi
after_success:
- if [[ $CODE_COVERAGE == "YES" ]]; then
swift package generate-xcodeproj --enable-code-coverage;
make generate-xcodeproj;
xcodebuild -scheme OpenCombine-Package build test | xcpretty;
bash <(curl -s https://codecov.io/bash);
fi
+27
View File
@@ -2,6 +2,33 @@ import Danger
let danger = Danger()
do {
let addedTestFiles = danger
.git
.createdFiles
.filter { $0.hasSuffix("Tests.swift") }
let modifiedXCTestManifests = danger
.git
.modifiedFiles
.contains { $0.hasSuffix("XCTestManifests.swift") }
if !addedTestFiles.isEmpty && !modifiedXCTestManifests {
let addedTestsClasses = addedTestFiles.map {
"- `\($0.split(separator: "/").last!.dropLast(6))`\n"
}.joined()
fail("""
You've added the following test classes:
\(addedTestsClasses)
but forgot to modify `XCTestManifests.swift`.
""")
}
}
SwiftLint.lint(inline: true,
configFile: ".swiftlint.yml",
strict: true,
+26
View File
@@ -0,0 +1,26 @@
debug:
swift build -c debug
release:
swift build -c release
test-debug:
swift test -c debug --enable-code-coverage --sanitize thread
test-release:
swift test -c release
swift-version:
swift -version
test-compatibility:
swift test -Xswiftc -DOPENCOMBINE_COMPATIBILITY_TEST
generate-compatibility-xcodeproj:
swift package generate-xcodeproj --xcconfig-overrides Combine-Compatibility.xcconfig; \
open OpenCombine.xcodeproj
generate-xcodeproj:
swift package generate-xcodeproj --enable-code-coverage
.PHONY: debug release test-debug test-release swift-version test-compatibility-debug generate-compatibility-xcodeproj generate-xcodeproj
-90
View File
@@ -115,82 +115,6 @@ extension Publisher {
public func measureInterval<S>(using scheduler: S, options: S.SchedulerOptions? = nil) -> Publishers.MeasureInterval<Self, S> where S : Scheduler
}
extension Publishers {
/// A publisher that republishes all elements that match a provided closure.
public struct Filter<Upstream> : Publisher where Upstream : Publisher {
/// The kind of values published by this publisher.
public typealias Output = Upstream.Output
/// The kind of errors this publisher might publish.
///
/// Use `Never` if this `Publisher` does not publish errors.
public typealias Failure = Upstream.Failure
/// The publisher from which this publisher receives elements.
public let upstream: Upstream
/// A closure that indicates whether to republish an element.
public let isIncluded: (Upstream.Output) -> Bool
public init(upstream: Upstream, isIncluded: @escaping (Output) -> Bool)
/// This function is called to attach the specified `Subscriber` to this `Publisher` by `subscribe(_:)`
///
/// - SeeAlso: `subscribe(_:)`
/// - Parameters:
/// - subscriber: The subscriber to attach to this `Publisher`.
/// once attached it can begin to receive values.
public func receive<S>(subscriber: S) where S : Subscriber, Upstream.Failure == S.Failure, Upstream.Output == S.Input
}
/// A publisher that republishes all elements that match a provided error-throwing closure.
public struct TryFilter<Upstream> : Publisher where Upstream : Publisher {
/// The kind of values published by this publisher.
public typealias Output = Upstream.Output
/// The kind of errors this publisher might publish.
///
/// Use `Never` if this `Publisher` does not publish errors.
public typealias Failure = Error
/// The publisher from which this publisher receives elements.
public let upstream: Upstream
/// A error-throwing closure that indicates whether to republish an element.
public let isIncluded: (Upstream.Output) throws -> Bool
public init(upstream: Upstream, isIncluded: @escaping (Upstream.Output) throws -> Bool)
/// This function is called to attach the specified `Subscriber` to this `Publisher` by `subscribe(_:)`
///
/// - SeeAlso: `subscribe(_:)`
/// - Parameters:
/// - subscriber: The subscriber to attach to this `Publisher`.
/// once attached it can begin to receive values.
public func receive<S>(subscriber: S) where S : Subscriber, Upstream.Output == S.Input, S.Failure == Publishers.TryFilter<Upstream>.Failure
}
}
extension Publisher {
/// Republishes all elements that match a provided closure.
///
/// - Parameter isIncluded: A closure that takes one element and returns a Boolean value indicating whether to republish the element.
/// - Returns: A publisher that republishes all elements that satisfy the closure.
public func filter(_ isIncluded: @escaping (Self.Output) -> Bool) -> Publishers.Filter<Self>
/// Republishes all elements that match a provided error-throwing closure.
///
/// If the `isIncluded` closure throws an error, the publisher fails with that error.
///
/// - Parameter isIncluded: A closure that takes one element and returns a Boolean value indicating whether to republish the element.
/// - Returns: A publisher that republishes all elements that satisfy the closure.
public func tryFilter(_ isIncluded: @escaping (Self.Output) throws -> Bool) -> Publishers.TryFilter<Self>
}
extension Publishers {
/// A publisher that raises a debugger signal when a provided closure needs to stop the process in the debugger.
@@ -3196,20 +3120,6 @@ extension Publisher {
public func tryFirst(where predicate: @escaping (Self.Output) throws -> Bool) -> Publishers.TryFirstWhere<Self>
}
extension Publishers.Filter {
public func filter(_ isIncluded: @escaping (Publishers.Filter<Upstream>.Output) -> Bool) -> Publishers.Filter<Upstream>
public func tryFilter(_ isIncluded: @escaping (Publishers.Filter<Upstream>.Output) throws -> Bool) -> Publishers.TryFilter<Upstream>
}
extension Publishers.TryFilter {
public func filter(_ isIncluded: @escaping (Publishers.TryFilter<Upstream>.Output) -> Bool) -> Publishers.TryFilter<Upstream>
public func tryFilter(_ isIncluded: @escaping (Publishers.TryFilter<Upstream>.Output) throws -> Bool) -> Publishers.TryFilter<Upstream>
}
extension Just {
public func prepend(_ elements: Output...) -> Publishers.Sequence<[Output], Just<Output>.Failure>
+3 -6
View File
@@ -139,9 +139,7 @@ public struct ImmediateScheduler: Scheduler {
tolerance: SchedulerTimeType.Stride,
options: SchedulerOptions?,
_ action: @escaping () -> Void) {
fatalError(
"Attempt to schedule something in the future on the immediate scheduler"
)
action()
}
/// Performs the action at some time after the specified date, at the specified
@@ -151,8 +149,7 @@ public struct ImmediateScheduler: Scheduler {
tolerance: SchedulerTimeType.Stride,
options: SchedulerOptions?,
_ action: @escaping () -> Void) -> Cancellable {
fatalError(
"Attempt to schedule something in the future on the immediate scheduler"
)
action()
return Subscriptions.empty
}
}
@@ -0,0 +1,228 @@
//
// Publishers.Filter.swift
//
//
// Created by Joseph Spadafora on 7/3/19.
//
extension Publisher {
/// Republishes all elements that match a provided closure.
///
/// - Parameter isIncluded: A closure that takes one element and returns
/// a Boolean value indicating whether to republish the element.
/// - Returns: A publisher that republishes all elements that satisfy the closure.
public func filter(
_ isIncluded: @escaping (Output) -> Bool
) -> Publishers.Filter<Self> {
return Publishers.Filter(upstream: self, isIncluded: isIncluded)
}
/// Republishes all elements that match a provided error-throwing closure.
///
/// If the `isIncluded` closure throws an error, the publisher fails with that error.
///
/// - Parameter isIncluded: A closure that takes one element and returns a
/// Boolean value indicating whether to republish the element.
/// - Returns: A publisher that republishes all elements that satisfy the closure.
public func tryFilter(
_ isIncluded: @escaping (Output) throws -> Bool
) -> Publishers.TryFilter<Self> {
return Publishers.TryFilter(upstream: self, isIncluded: isIncluded)
}
}
extension Publishers.Filter {
public func filter(
_ isIncluded: @escaping (Output) -> Bool
) -> Publishers.Filter<Upstream> {
return .init(upstream: upstream) { self.isIncluded($0) && isIncluded($0) }
}
public func tryFilter(
_ isIncluded: @escaping (Output) throws -> Bool
) -> Publishers.TryFilter<Upstream> {
return .init(upstream: upstream) { try self.isIncluded($0) && isIncluded($0) }
}
}
extension Publishers.TryFilter {
public func filter(
_ isIncluded: @escaping (Output) -> Bool
) -> Publishers.TryFilter<Upstream> {
return .init(upstream: upstream) { try self.isIncluded($0) && isIncluded($0) }
}
public func tryFilter(
_ isIncluded: @escaping (Output) throws -> Bool
) -> Publishers.TryFilter<Upstream> {
return .init(upstream: upstream) { try self.isIncluded($0) && isIncluded($0) }
}
}
extension Publishers {
/// A publisher that republishes all elements that match a provided closure.
public struct Filter<Upstream: Publisher>: Publisher {
/// The kind of values published by this publisher.
public typealias Output = Upstream.Output
/// The kind of errors this publisher might publish.
///
/// Use `Never` if this `Publisher` does not publish errors.
public typealias Failure = Upstream.Failure
/// The publisher from which this publisher receives elements.
public let upstream: Upstream
/// A closure that indicates whether to republish an element.
public let isIncluded: (Upstream.Output) -> Bool
public init(upstream: Upstream, isIncluded: @escaping (Output) -> Bool) {
self.upstream = upstream
self.isIncluded = isIncluded
}
/// This function is called to attach the specified `Subscriber`
/// to this `Publisher` by `subscribe(_:)`
///
/// - SeeAlso: `subscribe(_:)`
/// - Parameters:
/// - subscriber: The subscriber to attach to this `Publisher`.
/// once attached it can begin to receive values.
public func receive<SubscriberType: Subscriber>(subscriber: SubscriberType)
where Upstream.Failure == SubscriberType.Failure,
Upstream.Output == SubscriberType.Input
{
let filter = Inner(downstream: subscriber, isIncluded: catching(isIncluded))
upstream.receive(subscriber: filter)
}
}
/// A publisher that republishes all elements that match
/// a provided error-throwing closure.
public struct TryFilter<Upstream> : Publisher where Upstream : Publisher {
/// The kind of values published by this publisher.
public typealias Output = Upstream.Output
/// The kind of errors this publisher might publish.
///
/// Use `Never` if this `Publisher` does not publish errors.
public typealias Failure = Error
/// The publisher from which this publisher receives elements.
public let upstream: Upstream
/// A error-throwing closure that indicates whether to republish an element.
public let isIncluded: (Upstream.Output) throws -> Bool
public init(upstream: Upstream,
isIncluded: @escaping (Upstream.Output) throws -> Bool) {
self.upstream = upstream
self.isIncluded = isIncluded
}
/// This function is called to attach the specified `Subscriber`
/// to this `Publisher` by `subscribe(_:)`
///
/// - SeeAlso: `subscribe(_:)`
/// - Parameters:
/// - subscriber: The subscriber to attach to this `Publisher`.
/// once attached it can begin to receive values.
public func receive<Downstream: Subscriber>(subscriber: Downstream)
where Upstream.Output == Downstream.Input,
Downstream.Failure == Failure
{
let filter = Inner(downstream: subscriber, isIncluded: catching(isIncluded))
upstream.receive(subscriber: filter)
}
}
}
private class _Filter<Upstream: Publisher, Downstream: Subscriber>
: OperatorSubscription<Downstream>,
Subscription
where Upstream.Output == Downstream.Input
{
typealias Input = Upstream.Output
typealias Failure = Upstream.Failure
typealias Predicate = (Input) -> Result<Bool, Downstream.Failure>
private var _isIncluded: Predicate?
var isFinished: Bool {
return _isIncluded == nil
}
init(downstream: Downstream, isIncluded: @escaping Predicate) {
_isIncluded = isIncluded
super.init(downstream: downstream)
}
func receive(subscription: Subscription) {
upstreamSubscription = subscription
downstream.receive(subscription: self)
}
func receive(_ input: Input) -> Subscribers.Demand {
guard let isIncluded = _isIncluded else { return .none }
switch isIncluded(input) {
case .success(let isIncluded):
return isIncluded ? downstream.receive(input) : .max(1)
case .failure(let error):
downstream.receive(completion: .failure(error))
cancel()
return .none
}
}
func request(_ demand: Subscribers.Demand) {
guard !isFinished else { return }
upstreamSubscription?.request(demand)
}
override func cancel() {
_isIncluded = nil
upstreamSubscription?.cancel()
upstreamSubscription = nil
}
}
extension Publishers.Filter {
private final class Inner<Downstream: Subscriber>
: _Filter<Upstream, Downstream>,
Subscriber,
CustomStringConvertible
where Upstream.Output == Downstream.Input,
Upstream.Failure == Downstream.Failure {
var description: String { return "Filter" }
func receive(completion: Subscribers.Completion<Failure>) {
guard !isFinished else { return }
downstream.receive(completion: completion)
}
}
}
extension Publishers.TryFilter {
private final class Inner<Downstream: Subscriber>
: _Filter<Upstream, Downstream>,
Subscriber,
CustomStringConvertible
where Upstream.Output == Downstream.Input, Downstream.Failure == Error {
var description: String { return "TryFilter" }
func receive(completion: Subscribers.Completion<Failure>) {
guard !isFinished else { return }
downstream.receive(completion: completion.eraseError())
}
}
}
@@ -23,6 +23,7 @@ final class AnyCancellableTests: XCTestCase {
("testStoreInArbitraryCollection", testStoreInArbitraryCollection),
("testStoreInSet", testStoreInSet),
("testIndirectCancellation", testIndirectCancellation),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
func testClosureInitialized() {
@@ -151,4 +152,17 @@ final class AnyCancellableTests: XCTestCase {
cancellable1.cancel()
XCTAssertEqual(subscription.history, [.cancelled])
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
@@ -19,6 +19,7 @@ final class AnyPublisherTests: XCTestCase {
static let allTests = [
("testErasePublisher", testErasePublisher),
("testDescription", testDescription),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
private typealias Sut = AnyPublisher<Int, TestingError>
@@ -42,4 +43,17 @@ final class AnyPublisherTests: XCTestCase {
XCTAssertEqual(erased.description, "AnyPublisher")
XCTAssertEqual(erased.description, erased.playgroundDescription as? String)
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
@@ -27,6 +27,7 @@ final class AnySubscriberTests: XCTestCase {
("testErasingSubscriberSubscription", testErasingSubscriberSubscription),
("testErasingSubject", testErasingSubject),
("testErasingSubjectSubscription", testErasingSubjectSubscription),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
func testCombineIdentifier() {
@@ -180,6 +181,19 @@ final class AnySubscriberTests: XCTestCase {
XCTAssertEqual(subject.history, [.subscription("Subject"),
.completion(.finished)])
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
@available(macOS 10.15, iOS 13.0, *)
@@ -21,6 +21,7 @@ final class CombineIdentifierTests: PerformanceTestCase {
("testDefaultInitialized", testDefaultInitialized),
("testAnyObject", testAnyObject),
("testDefaultInitializedPerformance", testDefaultInitializedPerformance),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
func testDefaultInitialized() {
@@ -56,4 +57,17 @@ final class CombineIdentifierTests: PerformanceTestCase {
}
}
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
@@ -31,6 +31,7 @@ final class CurrentValueSubjectTests: XCTestCase {
("testSendSubscription", testSendSubscription),
("testLifecycle", testLifecycle),
("testSynchronization", testSynchronization),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
private typealias Sut = CurrentValueSubject<Int, TestingError>
@@ -506,4 +507,17 @@ final class CurrentValueSubjectTests: XCTestCase {
XCTAssertEqual(completions.value.count, 200)
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
@@ -0,0 +1,71 @@
//
// OperatorTestHelper.swift
//
//
// Created by Joseph Spadafora on 7/6/19.
//
import XCTest
#if OPENCOMBINE_COMPATIBILITY_TEST
import Combine
#else
import OpenCombine
#endif
/// `OperatorTestHelper` is an abstraction that helps avoid a lot of boilerplate when
/// testing an operator. It is initialized with a publisher type and creates a
/// `CustomSubscription`, `CustomPublisherBase` and `TrackingSubscriberBase`.
@available(macOS 10.15, iOS 13.0, *)
class OperatorTestHelper<SourceValue: Equatable,
SourcePublisher,
Sut: Publisher>
where Sut.Output: Equatable,
SourcePublisher: CustomPublisherBase<SourceValue, TestingError>
{
typealias Value = Sut.Output
typealias Failure = Sut.Failure
let subscription: CustomSubscription
let publisher: SourcePublisher
let tracking: TrackingSubscriberBase<Value, Failure>
private(set) var sut: Sut
var downstreamSubscription: Subscription?
/// This initializes the `OperatorTestHelper`. In most cases,
/// you can just pass a `publisherType` and closure
/// for `createSut` to get all the setup that you'll need for a test.
/// - Parameter publisherType: This should be filled in with the
/// type of `CustomPublisherBase` that you would like the
/// operator you are testing to be built from.
/// - Parameter initialDemand: This is the demand that the
/// created `TrackingSubscriber` should return upon receiving a subscription.
/// - Parameter receiveValueDemand: This is the demand that the
/// created `TrackingSubscriber should return upon receiving a value.
/// - Parameter customSubscription: This parameter defaults to `CustomSubscription()`,
/// but can be replaced with your own instance if you want to override
/// any of the default `CustomSubscription` initializer closures.
/// - Parameter createSut: This closure takes a new concrete instance
/// of the `publisherType` as an input to the closure and creates an
/// instance of the operator that you are trying to test.
init(publisherType: SourcePublisher.Type,
initialDemand: Subscribers.Demand,
receiveValueDemand: Subscribers.Demand,
customSubscription: CustomSubscription = CustomSubscription(),
createSut: (SourcePublisher) -> Sut)
{
self.subscription = customSubscription
let createdPublisher = publisherType.init(subscription: customSubscription)
self.publisher = createdPublisher
self.sut = createSut(createdPublisher)
self.tracking = TrackingSubscriberBase<Value, Failure>(
receiveSubscription: {
$0.request(initialDemand)
},
receiveValue: { _ in receiveValueDemand }
)
tracking.onSubscribe = { self.downstreamSubscription = $0 }
sut.subscribe(tracking)
}
}
@@ -86,6 +86,12 @@ final class TrackingSubscriberBase<Value: Equatable, Failure: Error>
private let _receiveCompletion: ((Subscribers.Completion<Failure>) -> Void)?
private let _onDeinit: (() -> Void)?
var onSubscribe: ((Subscription) -> Void)?
var onValue: ((Input) -> Void)?
var onFinish: (() -> Void)?
var onFailure: ((Failure) -> Void)?
var onDeinit: (() -> Void)?
/// The history of subscriptions, inputs and completions of this subscriber
private(set) var history: [Event] = []
@@ -144,16 +150,24 @@ final class TrackingSubscriberBase<Value: Equatable, Failure: Error>
func receive(subscription: Subscription) {
history.append(.subscription(.init(subscription)))
onSubscribe?(subscription)
_receiveSubscription?(subscription)
}
func receive(_ input: Value) -> Subscribers.Demand {
history.append(.value(input))
onValue?(input)
return _receiveValue?(input) ?? .none
}
func receive(completion: Subscribers.Completion<Failure>) {
history.append(.completion(completion))
switch completion {
case .failure(let error):
onFailure?(error)
case .finished:
onFinish?()
}
_receiveCompletion?(completion)
}
@@ -162,6 +176,7 @@ final class TrackingSubscriberBase<Value: Equatable, Failure: Error>
}
deinit {
onDeinit?()
_onDeinit?()
}
}
@@ -19,6 +19,7 @@ final class ImmediateSchedulerTests: XCTestCase {
static let allTests = [
("testStride", testSchedulerTimeType),
("testActions", testActions),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
func testSchedulerTimeType() throws {
@@ -80,5 +81,35 @@ final class ImmediateSchedulerTests: XCTestCase {
}
XCTAssertTrue(fired)
fired = false
ImmediateScheduler.shared.schedule(after: ImmediateScheduler.shared.now) {
fired = true
}
XCTAssertTrue(fired)
fired = false
let cancellable = ImmediateScheduler
.shared
.schedule(after: ImmediateScheduler.shared.now, interval: 10) {
fired = true
}
XCTAssertTrue(fired)
XCTAssertEqual(String(describing: cancellable), "Empty")
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
@@ -27,6 +27,7 @@ final class PassthroughSubjectTests: XCTestCase {
("testSendSubscription", testSendSubscription),
("testLifecycle", testLifecycle),
("testSynchronization", testSynchronization),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
private typealias Sut = PassthroughSubject<Int, TestingError>
@@ -440,4 +441,17 @@ final class PassthroughSubjectTests: XCTestCase {
XCTAssertEqual(completions.value.count, 200)
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
@@ -20,6 +20,7 @@ final class PublisherTests: XCTestCase {
("testSubscribeSubscriber", testSubscribeSubscriber),
("testSubscribeSubject", testSubscribeSubject),
("testSubjectSubscriber", testSubjectSubscriber),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
func testSubscribeSubscriber() {
@@ -105,4 +106,17 @@ final class PublisherTests: XCTestCase {
XCTAssert(subjectDestroyed)
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
@@ -19,10 +19,12 @@ final class CountTests: XCTestCase {
static let allTests = [
("testSendsCorrectCount", testSendsCorrectCount),
("testCountWaitsUntilFinishedToSend", testCountWaitsUntilFinishedToSend),
("testDemand", testDemand),
("testAddingSubscriberRequestsUnlimitedDemand",
testAddingSubscriberRequestsUnlimitedDemand),
("testReceivesSubscriptionBeforeRequestingUpstream",
testReceivesSubscriptionBeforeRequestingUpstream)
testReceivesSubscriptionBeforeRequestingUpstream),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
func testSendsCorrectCount() {
@@ -147,4 +149,17 @@ final class CountTests: XCTestCase {
XCTAssertEqual(receiveOrder, [receiveDownstream, upstreamRequest])
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
@@ -18,7 +18,8 @@ final class DecodeTests: XCTestCase {
static let allTests = [
("testDecodingSuccess", testDecodingSuccess),
("testDecodingFailure", testDecodingFailure),
("testDemand", testDemand)
("testDemand", testDemand),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
var jsonEncoder: TestEncoder = TestEncoder()
@@ -92,6 +93,19 @@ final class DecodeTests: XCTestCase {
XCTAssertEqual(publisher.send(10), .none)
XCTAssertEqual(subscription.history, [.requested(.max(42)), .cancelled])
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
let testValue = ["test": "TestDecodable"]
@@ -19,7 +19,8 @@ final class DeferredTests: XCTestCase {
static let allTests = [
("testDeferredCreatedAfterSubscription",
testDeferredCreatedAfterSubscription)
testDeferredCreatedAfterSubscription),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
func testDeferredCreatedAfterSubscription() {
@@ -51,4 +52,17 @@ final class DeferredTests: XCTestCase {
XCTAssertEqual(tracking.history, [.subscription("CustomSubscription"),
.subscription("CustomSubscription")])
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
@@ -24,8 +24,10 @@ final class DropWhileTests: XCTestCase {
("testDemand", testDemand),
("testTryDropWhileCancelsUpstreamOnThrow",
testTryDropWhileCancelsUpstreamOnThrow),
("testDropWhileCompletion",
testDropWhileCompletion),
("testDropWhileCompletion", testDropWhileCompletion),
("testCancelAlreadyCancelled", testCancelAlreadyCancelled),
("testLifecycle", testLifecycle),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
func testDropWhile() {
@@ -331,4 +333,17 @@ final class DropWhileTests: XCTestCase {
try XCTUnwrap(subscription).cancel()
XCTAssertEqual(deinitCounter, 0)
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
@@ -20,6 +20,7 @@ final class EmptyTests: XCTestCase {
("testEmpty", testEmpty),
("testImmediatelyCancel", testImmediatelyCancel),
("testEquatable", testEquatable),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
func testEmpty() {
@@ -75,4 +76,17 @@ final class EmptyTests: XCTestCase {
outputType: Int.self,
failureType: Error.self))
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
@@ -21,6 +21,7 @@ final class EncodeTests: XCTestCase {
("testEncodingFailure", testEncodingFailure),
("testDemand", testDemand),
("testEncodeSuccessHistory", testEncodeSuccessHistory),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
private var encoder = TestEncoder()
@@ -108,4 +109,17 @@ final class EncodeTests: XCTestCase {
XCTAssertEqual(publisher.send(["test" : "TestDecodable"]), .max(2))
XCTAssertEqual(subscription.history, [.requested(.max(37))])
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
@@ -18,6 +18,7 @@ final class FailTests: XCTestCase {
static let allTests = [
("testSubscription", testSubscription),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
private typealias Sut = Fail<Int, TestingError>
@@ -30,4 +31,17 @@ final class FailTests: XCTestCase {
XCTAssertEqual(tracking.history, [.subscription("Empty"),
.completion(.failure(.oops))])
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
@@ -0,0 +1,426 @@
//
// FilterTests.swift
//
//
// Created by Joseph Spadafora on 6/25/19.
//
import XCTest
#if OPENCOMBINE_COMPATIBILITY_TEST
import Combine
#else
import OpenCombine
#endif
@available(macOS 10.15, iOS 13.0, *)
final class FilterTests: XCTestCase {
static let allTests = [
("testFilterRemovesElements", testFilterRemovesElements),
("testTryFilterWorks", testTryFilterWorks),
("testTryFilterCompletesWithErrorWhenThrown",
testTryFilterCompletesWithErrorWhenThrown),
("testCanCompleteWithFinished", testCanCompleteWithFinished),
("testFilterCanCompleteWithError", testFilterCanCompleteWithError),
("testTryFilterCanCompleteWithError", testTryFilterCanCompleteWithError),
("testFilterSubscriptionDemand", testFilterSubscriptionDemand),
("testTryFilterSubscriptionDemand", testTryFilterSubscriptionDemand),
("testFilterCancel", testFilterCancel),
("testTryFilterCancel", testTryFilterCancel),
("testCancelAlreadyCancelled", testCancelAlreadyCancelled),
("testLifecycle", testLifecycle),
("testFilterOperatorSpecializationForFilter",
testFilterOperatorSpecializationForFilter),
("testTryFilterOperatorSpecializationForFilter",
testTryFilterOperatorSpecializationForFilter),
("testFilterOperatorSpecializationForTryFilter",
testFilterOperatorSpecializationForTryFilter),
("testTryFilterOperatorSpecializationForTryFilter",
testTryFilterOperatorSpecializationForTryFilter),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
func testFilterRemovesElements() {
// Given
let helper = OperatorTestHelper(publisherType: CustomPublisher.self,
initialDemand: .max(2),
receiveValueDemand: .none) {
$0.filter { $0.isMultiple(of: 2) }
}
// When
for i in 1...5 {
XCTAssertEqual(helper.publisher.send(i),
helper.sut.isIncluded(i) ? .none : .max(1))
}
// Then
XCTAssertEqual(helper.tracking.history, [.subscription("Filter"),
.value(2),
.value(4)])
}
func testTryFilterWorks() {
// Given
let helper = OperatorTestHelper(publisherType: CustomPublisher.self,
initialDemand: .max(2),
receiveValueDemand: .none) {
$0.tryFilter {
try $0.isMultiple(of: 2) && nonthrowingReturn($0)
}
}
// When
for i in 1...5 {
XCTAssertEqual(helper.publisher.send(i),
try helper.sut.isIncluded(i) ? .none : .max(1))
}
// Then
XCTAssertEqual(helper.tracking.history, [.subscription("TryFilter"),
.value(2),
.value(4)])
}
func testTryFilterCompletesWithErrorWhenThrown() {
// Given
let helper = OperatorTestHelper(publisherType: CustomPublisher.self,
initialDemand: .unlimited,
receiveValueDemand: .none) {
$0.tryFilter {
try failOnFive(value: $0)
}
}
// When
for i in 1...5 {
_ = helper.publisher.send(i)
}
helper.publisher.send(completion: .finished)
// Then
XCTAssertEqual(helper.tracking.history, [.subscription("TryFilter"),
.value(1),
.value(2),
.value(3),
.value(4),
.completion(.failure(TestingError.oops))
])
}
func testCanCompleteWithFinished() {
// Given
let helper = OperatorTestHelper(publisherType: CustomPublisher.self,
initialDemand: .unlimited,
receiveValueDemand: .none) {
$0.filter { _ in true }
}
// When
XCTAssertEqual(helper.publisher.send(1), .none)
helper.publisher.send(completion: .finished)
// Then
XCTAssertEqual(helper.tracking.history, [.subscription("Filter"),
.value(1),
.completion(.finished)])
}
func testFilterCanCompleteWithError() {
// Given
let helper = OperatorTestHelper(publisherType: CustomPublisher.self,
initialDemand: .unlimited,
receiveValueDemand: .none) {
$0.filter { _ in true }
}
// When
XCTAssertEqual(helper.publisher.send(1), .none)
helper.publisher.send(completion: .failure(.oops))
// Then
XCTAssertEqual(helper.tracking.history, [.subscription("Filter"),
.value(1),
.completion(.failure(.oops))])
}
func testTryFilterCanCompleteWithError() {
// Given
let helper = OperatorTestHelper(
publisherType: CustomPublisher.self,
initialDemand: .unlimited,
receiveValueDemand: .none,
createSut: {
$0.tryFilter { _ in true }
}
)
// When
XCTAssertEqual(helper.publisher.send(1), .none)
helper.publisher.send(completion: .failure(.oops))
// Then
XCTAssertEqual(helper.tracking.history,
[.subscription("TryFilter"),
.value(1),
.completion(.failure(TestingError.oops))])
}
func testFilterSubscriptionDemand() {
let helper = OperatorTestHelper(
publisherType: CustomPublisher.self,
initialDemand: .max(3),
receiveValueDemand: .none,
createSut: {
$0.filter { $0.isMultiple(of: 2) }
}
)
XCTAssertEqual(helper.publisher.send(1), .max(1))
XCTAssertEqual(helper.publisher.send(2), .max(0))
XCTAssertEqual(helper.publisher.send(3), .max(1))
XCTAssertEqual(helper.publisher.send(4), .max(0))
XCTAssertEqual(helper.publisher.send(5), .max(1))
XCTAssertEqual(helper.publisher.send(6), .max(0))
XCTAssertEqual(helper.publisher.send(7), .max(1))
XCTAssertEqual(helper.publisher.send(8), .max(0))
XCTAssertEqual(helper.subscription.history, [.requested(.max(3))])
}
func testTryFilterSubscriptionDemand() {
let helper = OperatorTestHelper(publisherType: CustomPublisher.self,
initialDemand: .max(3),
receiveValueDemand: .none) {
$0.tryFilter { $0.isMultiple(of: 2) }
}
XCTAssertEqual(helper.publisher.send(1), .max(1))
XCTAssertEqual(helper.publisher.send(2), .max(0))
XCTAssertEqual(helper.publisher.send(3), .max(1))
XCTAssertEqual(helper.publisher.send(4), .max(0))
XCTAssertEqual(helper.publisher.send(5), .max(1))
XCTAssertEqual(helper.publisher.send(6), .max(0))
XCTAssertEqual(helper.publisher.send(7), .max(1))
XCTAssertEqual(helper.publisher.send(8), .max(0))
}
func testFilterCancel() throws {
let helper = OperatorTestHelper(publisherType: CustomPublisher.self,
initialDemand: .unlimited,
receiveValueDemand: .none,
createSut: { $0.filter { $0.isMultiple(of: 2) } })
try XCTUnwrap(helper.downstreamSubscription).cancel()
XCTAssertEqual(helper.publisher.send(2), .none)
helper.publisher.send(completion: .finished)
XCTAssertEqual(helper.publisher.send(4), .none)
XCTAssertEqual(helper.subscription.history, [.requested(.unlimited), .cancelled])
XCTAssertEqual(helper.tracking.history, [.subscription("Filter")])
}
func testTryFilterCancel() throws {
let helper = OperatorTestHelper(
publisherType: CustomPublisher.self,
initialDemand: .unlimited,
receiveValueDemand: .none,
createSut: {
$0.tryFilter { try failOnFive(value: $0) && $0.isMultiple(of: 2) }
}
)
try XCTUnwrap(helper.downstreamSubscription).cancel()
XCTAssertEqual(helper.publisher.send(2), .none)
helper.publisher.send(completion: .finished)
XCTAssertEqual(helper.publisher.send(4), .none)
XCTAssertEqual(helper.publisher.send(5), .none)
XCTAssertEqual(helper.subscription.history, [.requested(.unlimited), .cancelled])
XCTAssertEqual(helper.tracking.history, [.subscription("TryFilter")])
}
func testCancelAlreadyCancelled() throws {
let helper = OperatorTestHelper(publisherType: CustomPublisher.self,
initialDemand: .unlimited,
receiveValueDemand: .none,
createSut: { $0.filter { $0.isMultiple(of: 2) } })
try XCTUnwrap(helper.downstreamSubscription).cancel()
try XCTUnwrap(helper.downstreamSubscription).request(.unlimited)
try XCTUnwrap(helper.downstreamSubscription).cancel()
XCTAssertEqual(helper.subscription.history, [.requested(.unlimited), .cancelled])
}
func testLifecycle() throws {
var deinitCounter = 0
let onDeinit = { deinitCounter += 1 }
do {
let passthrough = PassthroughSubject<Int, TestingError>()
let filter = passthrough.filter { $0.isMultiple(of: 2) }
let emptySubscriber = TrackingSubscriber(onDeinit: onDeinit)
XCTAssertTrue(emptySubscriber.history.isEmpty)
filter.subscribe(emptySubscriber)
XCTAssertEqual(emptySubscriber.subscriptions.count, 1)
passthrough.send(31)
XCTAssertEqual(emptySubscriber.inputs.count, 0)
passthrough.send(completion: .failure("failure"))
XCTAssertEqual(emptySubscriber.completions.count, 1)
}
XCTAssertEqual(deinitCounter, 0)
do {
let passthrough = PassthroughSubject<Int, TestingError>()
let filter = passthrough.filter { $0.isMultiple(of: 2) }
let emptySubscriber = TrackingSubscriber(onDeinit: onDeinit)
XCTAssertTrue(emptySubscriber.history.isEmpty)
filter.subscribe(emptySubscriber)
XCTAssertEqual(emptySubscriber.subscriptions.count, 1)
XCTAssertEqual(emptySubscriber.inputs.count, 0)
XCTAssertEqual(emptySubscriber.completions.count, 0)
}
XCTAssertEqual(deinitCounter, 0)
var subscription: Subscription?
do {
let passthrough = PassthroughSubject<Int, TestingError>()
let filter = passthrough.filter { $0.isMultiple(of: 2) }
let emptySubscriber = TrackingSubscriber(
receiveSubscription: { subscription = $0; $0.request(.unlimited) },
onDeinit: onDeinit
)
XCTAssertTrue(emptySubscriber.history.isEmpty)
filter.subscribe(emptySubscriber)
XCTAssertEqual(emptySubscriber.subscriptions.count, 1)
passthrough.send(32)
XCTAssertEqual(emptySubscriber.inputs.count, 1)
XCTAssertEqual(emptySubscriber.completions.count, 0)
XCTAssertNotNil(subscription)
}
XCTAssertEqual(deinitCounter, 0)
try XCTUnwrap(subscription).cancel()
XCTAssertEqual(deinitCounter, 0)
}
func testFilterOperatorSpecializationForFilter() {
// Given
let helper = OperatorTestHelper(publisherType: CustomPublisher.self,
initialDemand: .max(1),
receiveValueDemand: .none) {
$0.filter {
$0.isMultiple(of: 3)
}.filter {
$0.isMultiple(of: 5)
}
}
// When
for i in 1...20 {
XCTAssertEqual(helper.publisher.send(i),
helper.sut.isIncluded(i) ? .none : .max(1))
}
// Then
XCTAssertEqual(helper.tracking.history, [.subscription("Filter"), .value(15)])
}
func testTryFilterOperatorSpecializationForFilter() {
// Given
let helper = OperatorTestHelper(publisherType: CustomPublisher.self,
initialDemand: .max(1),
receiveValueDemand: .none) {
$0.filter {
$0.isMultiple(of: 3)
}.tryFilter {
$0.isMultiple(of: 5)
}
}
// When
for i in 1...20 {
XCTAssertEqual(helper.publisher.send(i),
try helper.sut.isIncluded(i) ? .none : .max(1))
}
// Then
XCTAssertEqual(helper.tracking.history, [.subscription("TryFilter"), .value(15)])
}
func testFilterOperatorSpecializationForTryFilter() {
// Given
let helper = OperatorTestHelper(publisherType: CustomPublisher.self,
initialDemand: .max(1),
receiveValueDemand: .none) {
$0.tryFilter {
$0.isMultiple(of: 3)
}.filter {
$0.isMultiple(of: 5)
}
}
// When
for i in 1...20 {
XCTAssertEqual(helper.publisher.send(i),
try helper.sut.isIncluded(i) ? .none : .max(1))
}
// Then
XCTAssertEqual(helper.tracking.history, [.subscription("TryFilter"), .value(15)])
}
func testTryFilterOperatorSpecializationForTryFilter() {
// Given
let helper = OperatorTestHelper(publisherType: CustomPublisher.self,
initialDemand: .max(3),
receiveValueDemand: .none) {
$0.tryFilter {
$0.isMultiple(of: 3)
}.tryFilter {
$0.isMultiple(of: 5)
}
}
// When
for i in 1...20 {
XCTAssertEqual(helper.publisher.send(i),
try helper.sut.isIncluded(i) ? .none : .max(1))
}
// Then
XCTAssertEqual(helper.tracking.history, [.subscription("TryFilter"),
.value(15)])
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
private func nonthrowingReturn(_ value: Int) throws -> Bool {
return true
}
private func failOnFive(value: Int) throws -> Bool {
if value == 5 {
throw TestingError.oops
}
return true
}
@@ -65,6 +65,7 @@ final class JustTests: XCTestCase {
("testPrefixWhileOperatorSpecialization", testPrefixWhileOperatorSpecialization),
("testSetFailureTypeOperatorSpecialization",
testSetFailureTypeOperatorSpecialization),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
private typealias Sut = Just
@@ -357,4 +358,17 @@ final class JustTests: XCTestCase {
func testSetFailureTypeOperatorSpecialization() {
XCTAssertEqual(try Sut(73).setFailureType(to: TestingError.self).result.get(), 73)
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
@@ -26,6 +26,7 @@ final class MapErrorTests: XCTestCase {
("testCancel", testCancel),
("testCancelAlreadyCancelled", testCancelAlreadyCancelled),
("testLifecycle", testLifecycle),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
func testEmpty() {
@@ -251,6 +252,19 @@ final class MapErrorTests: XCTestCase {
try XCTUnwrap(subscription).cancel()
XCTAssertEqual(deinitCounter, 2)
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
private struct OtherError: Error {
@@ -36,7 +36,8 @@ final class MapTests: XCTestCase {
("testMapOperatorSpecializationForTryMap",
testMapOperatorSpecializationForTryMap),
("testTryMapOperatorSpecializationForTryMap",
testTryMapOperatorSpecializationForTryMap)
testTryMapOperatorSpecializationForTryMap),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
func testEmpty() {
@@ -458,4 +459,17 @@ final class MapTests: XCTestCase {
.value(11),
.completion(.failure(TestingError.oops))])
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
@@ -22,6 +22,7 @@ final class MulticastTests: XCTestCase {
("testMulticastDisconnect", testMulticastDisconnect),
("testLateSubscriber", testLateSubscriber),
("testSubscribeAfterCompletion", testSubscribeAfterCompletion),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
func testMulticast() throws {
@@ -285,4 +286,17 @@ final class MulticastTests: XCTestCase {
XCTAssertEqual(lateSubscriber.history, [.subscription("Multicast")])
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
@@ -54,6 +54,7 @@ final class OptionalPublisherTests: XCTestCase {
testOutputInRangeOperatorSpecialization),
("testPrefixOperatorSpecialization", testPrefixOperatorSpecialization),
("testPrefixWhileOperatorSpecialization", testPrefixWhileOperatorSpecialization),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
#if OPENCOMBINE_COMPATIBILITY_TEST || !canImport(Combine)
@@ -367,4 +368,17 @@ final class OptionalPublisherTests: XCTestCase {
XCTAssertEqual(count, 2)
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
@@ -20,6 +20,7 @@ final class PrintTests: XCTestCase {
("testPrintWithoutPrefix", testPrintWithoutPrefix),
("testPrintWithPrefix", testPrintWithPrefix),
("testSynchronization", testSynchronization),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
func testPrintWithoutPrefix() {
@@ -212,6 +213,19 @@ final class PrintTests: XCTestCase {
XCTAssertEqual(counter.value, 200)
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
private final class StringStream: TextOutputStream {
@@ -18,7 +18,8 @@ final class ReplaceNilTests: XCTestCase {
static let allTests = [
("testReplacesNilElement", testReplacesNilElement),
("testExistingElementIsPreserved", testExistingElementIsPreserved),
("testMultipleReplacements", testMultipleReplacements)
("testMultipleReplacements", testMultipleReplacements),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
func testReplacesNilElement() {
@@ -91,4 +92,17 @@ final class ReplaceNilTests: XCTestCase {
.value(42),
.completion(.finished)])
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
@@ -57,6 +57,7 @@ final class ResultPublisherTests: XCTestCase {
("testTryScanOperatorSpecialization", testTryScanOperatorSpecialization),
("testSetFailureTypeOperatorSpecialization",
testSetFailureTypeOperatorSpecialization),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
#if OPENCOMBINE_COMPATIBILITY_TEST || !canImport(Combine)
@@ -451,4 +452,17 @@ final class ResultPublisherTests: XCTestCase {
73
)
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
@@ -68,6 +68,7 @@ final class SequenceTests: XCTestCase {
testAppendSequenceOperatorSpecialization),
("testAppendPublisherOperatorSpecialization",
testAppendPublisherOperatorSpecialization),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
#if OPENCOMBINE_COMPATIBILITY_TEST || !canImport(Combine)
@@ -662,6 +663,19 @@ final class SequenceTests: XCTestCase {
XCTAssertEqual(newCollection.history, [.initFromSequence,
.appendSequence])
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
private final class Counter: Sequence, IteratorProtocol, CustomStringConvertible {
@@ -25,6 +25,7 @@ final class SetFailureTypeTests: XCTestCase {
("testCompletion", testCompletion),
("testCancel", testCancel),
("testCancelAlreadyCancelled", testCancelAlreadyCancelled),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
func testEmpty() {
@@ -169,4 +170,17 @@ final class SetFailureTypeTests: XCTestCase {
.requested(.unlimited),
.cancelled])
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
@@ -22,6 +22,7 @@ final class AssignTests: XCTestCase {
("testSubscription", testSubscription),
("testReceiveValue", testReceiveValue),
("testPublisherOperator", testPublisherOperator),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
private typealias Sut<Root> = Subscribers.Assign<Root, Int>
@@ -129,4 +130,17 @@ final class AssignTests: XCTestCase {
publisher.send(100)
XCTAssertEqual(object.value, 42)
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
@@ -19,7 +19,8 @@ import Foundation
final class CompletionTests: XCTestCase {
static let allTests = [
("testEncodingDecoding", testEncodingDecoding)
("testEncodingDecoding", testEncodingDecoding),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
typealias Sut = Subscribers.Completion<TestingError>
@@ -67,4 +68,17 @@ final class CompletionTests: XCTestCase {
}
}
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
@@ -22,6 +22,7 @@ final class SinkTests: XCTestCase {
("testSubscription", testSubscription),
("testReceiveValue", testReceiveValue),
("testPublisherOperator", testPublisherOperator),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
private typealias Sut = Subscribers.Sink<Int, Never>
@@ -116,4 +117,17 @@ final class SinkTests: XCTestCase {
publisher.send(100)
XCTAssertEqual(value, 42)
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
@@ -28,6 +28,7 @@ final class SubscribersDemandTests: XCTestCase {
("testDescription", testDescription),
("testEncodeDecodeJSON", testEncodeDecodeJSON),
("testEncodeDecodePlist", testEncodeDecodePlist),
("testTestSuiteIncludesAllTests", testTestSuiteIncludesAllTests),
]
func testCrashesOnNegativeValue() {
@@ -325,6 +326,19 @@ final class SubscribersDemandTests: XCTestCase {
XCTAssertEqual(decodedIllFormedTooBig.value.description, "unlimited")
}
// MARK: -
func testTestSuiteIncludesAllTests() {
// https://oleb.net/blog/2017/03/keeping-xctest-in-sync/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let thisClass = type(of: self)
let allTestsCount = thisClass.allTests.count
let darwinCount = thisClass.defaultTestSuite.testCaseCount
XCTAssertEqual(allTestsCount,
darwinCount,
"\(darwinCount - allTestsCount) tests are missing from allTests")
#endif
}
}
@available(macOS 10.15, iOS 13.0, *)
+6 -2
View File
@@ -12,28 +12,32 @@ public func allTests() -> [XCTestCaseEntry] {
return [
testCase(AnyCancellableTests.allTests),
testCase(AnyPublisherTests.allTests),
testCase(AnySubscriberTests.allTests),
testCase(AssignTests.allTests),
testCase(CombineIdentifierTests.allTests),
testCase(CompletionTests.allTests),
testCase(CountTests.allTests),
testCase(CurrentValueSubjectTests.allTests),
testCase(DecodeTests.allTests),
testCase(DeferredTests.allTests),
testCase(DropWhileTests.allTests),
testCase(EmptyTests.allTests),
testCase(EncodeTests.allTests),
testCase(FailTests.allTests),
testCase(FilterTests.allTests),
testCase(ImmediateSchedulerTests.allTests),
testCase(JustTests.allTests),
testCase(MapErrorTests.allTests),
testCase(MapTests.allTests),
testCase(MulticastTests.allTests),
testCase(ResultPublisherTests.allTests),
testCase(OptionalPublisherTests.allTests),
testCase(PassthroughSubjectTests.allTests),
testCase(PrintTests.allTests),
testCase(PublisherTests.allTests),
testCase(ReplaceNilTests.allTests),
testCase(SetFailureTypeTests.allTests),
testCase(ResultPublisherTests.allTests),
testCase(SequenceTests.allTests),
testCase(SetFailureTypeTests.allTests),
testCase(SinkTests.allTests),
testCase(SubscribersDemandTests.allTests),
]