327 lines
12 KiB
Swift
327 lines
12 KiB
Swift
//
|
|
// PublishedTests.swift
|
|
//
|
|
//
|
|
// Created by Sergej Jaskiewicz on 08/09/2019.
|
|
//
|
|
|
|
import XCTest
|
|
|
|
#if swift(>=5.1)
|
|
|
|
#if OPENCOMBINE_COMPATIBILITY_TEST
|
|
import Combine
|
|
|
|
@available(macOS 10.15, iOS 13.0, *)
|
|
private typealias Published = Combine.Published
|
|
|
|
@available(macOS 10.15, iOS 13.0, *)
|
|
private typealias ObservableObject = Combine.ObservableObject
|
|
#else
|
|
import OpenCombine
|
|
|
|
private typealias Published = OpenCombine.Published
|
|
|
|
private typealias ObservableObject = OpenCombine.ObservableObject
|
|
#endif
|
|
|
|
@available(macOS 10.15, iOS 13.0, *)
|
|
final class PublishedTests: XCTestCase {
|
|
|
|
func testBasicBehavior() {
|
|
let testObject = TestObject()
|
|
var downstreamSubscription1: Subscription?
|
|
let tracking1 = TrackingSubscriberBase<Int, Never>(
|
|
receiveSubscription: { downstreamSubscription1 = $0 }
|
|
)
|
|
|
|
var currentValue = 0
|
|
tracking1.onValue = { _ in
|
|
XCTAssertEqual(
|
|
testObject.state,
|
|
currentValue,
|
|
"Value of @Published should be updated _after_ sending it downstream"
|
|
)
|
|
}
|
|
|
|
testObject.$state.subscribe(tracking1)
|
|
XCTAssertEqual(tracking1.history, [.subscription("PublishedSubject")])
|
|
downstreamSubscription1?.request(.max(2))
|
|
XCTAssertEqual(tracking1.history, [.subscription("PublishedSubject"),
|
|
.value(0)])
|
|
testObject.state += 1
|
|
currentValue = 1
|
|
testObject.state += 2
|
|
currentValue = 3
|
|
testObject.state += 3
|
|
currentValue = 6
|
|
XCTAssertEqual(tracking1.history, [.subscription("PublishedSubject"),
|
|
.value(0),
|
|
.value(1)])
|
|
downstreamSubscription1?.request(.max(10))
|
|
XCTAssertEqual(tracking1.history, [.subscription("PublishedSubject"),
|
|
.value(0),
|
|
.value(1),
|
|
.value(6)])
|
|
|
|
let tracking2 = TrackingSubscriberBase<Int, Never>(
|
|
receiveSubscription: { $0.request(.unlimited) }
|
|
)
|
|
testObject.$state.subscribe(tracking2)
|
|
XCTAssertEqual(tracking2.history, [.subscription("PublishedSubject"),
|
|
.value(6)])
|
|
|
|
testObject.state = 42
|
|
currentValue = 42
|
|
XCTAssertEqual(tracking1.history, [.subscription("PublishedSubject"),
|
|
.value(0),
|
|
.value(1),
|
|
.value(6),
|
|
.value(42)])
|
|
XCTAssertEqual(tracking2.history, [.subscription("PublishedSubject"),
|
|
.value(6),
|
|
.value(42)])
|
|
|
|
downstreamSubscription1?.cancel()
|
|
testObject.state = -1
|
|
|
|
XCTAssertEqual(tracking1.history, [.subscription("PublishedSubject"),
|
|
.value(0),
|
|
.value(1),
|
|
.value(6),
|
|
.value(42)])
|
|
XCTAssertEqual(tracking2.history, [.subscription("PublishedSubject"),
|
|
.value(6),
|
|
.value(42),
|
|
.value(-1)])
|
|
}
|
|
|
|
func testObservableObjectWithCustomObjectWillChange() {
|
|
let testObject = TestObject()
|
|
var downstreamSubscription: Subscription?
|
|
let tracking1 = TrackingSubscriberBase<Void, Never>(
|
|
receiveSubscription: { downstreamSubscription = $0 }
|
|
)
|
|
testObject.objectWillChange.subscribe(tracking1)
|
|
tracking1.assertHistoryEqual([.subscription("ObservableObjectPublisher")])
|
|
downstreamSubscription?.request(.max(2))
|
|
tracking1.assertHistoryEqual([.subscription("ObservableObjectPublisher")])
|
|
testObject.state = 100
|
|
tracking1.assertHistoryEqual([.subscription("ObservableObjectPublisher")])
|
|
}
|
|
|
|
@available(macOS 11.0, iOS 14.0, *)
|
|
func testAssignToPublished() throws {
|
|
let subscription = CustomSubscription()
|
|
let publisher = CustomPublisherBase<Int, Never>(subscription: subscription)
|
|
let tracking = TrackingSubscriberBase<Int, Never>()
|
|
|
|
do {
|
|
let testObject = TestObject()
|
|
publisher.assign(to: &testObject.$state)
|
|
XCTAssertEqual(subscription.history, [.requested(.unlimited)])
|
|
|
|
testObject.$state.subscribe(tracking)
|
|
XCTAssertEqual(subscription.history, [.requested(.unlimited)])
|
|
XCTAssertEqual(tracking.history, [.subscription("PublishedSubject")])
|
|
|
|
XCTAssertEqual(publisher.send(1), .none)
|
|
XCTAssertEqual(subscription.history, [.requested(.unlimited)])
|
|
XCTAssertEqual(tracking.history, [.subscription("PublishedSubject")])
|
|
|
|
try XCTUnwrap(tracking.subscriptions.first).request(.max(3))
|
|
|
|
XCTAssertEqual(publisher.send(2), .none)
|
|
XCTAssertEqual(publisher.send(3), .none)
|
|
XCTAssertEqual(publisher.send(4), .none)
|
|
XCTAssertEqual(subscription.history, [.requested(.unlimited)])
|
|
XCTAssertEqual(tracking.history, [.subscription("PublishedSubject"),
|
|
.value(1),
|
|
.value(2),
|
|
.value(3)])
|
|
|
|
tracking.cancel()
|
|
|
|
XCTAssertEqual(subscription.history, [.requested(.unlimited)])
|
|
}
|
|
|
|
XCTAssertEqual(subscription.history, [.requested(.unlimited),
|
|
.cancelled])
|
|
}
|
|
|
|
@available(macOS 11.0, iOS 14.0, *)
|
|
func testAssignToPublishedFinish() {
|
|
let subscription = CustomSubscription()
|
|
let publisher = CustomPublisherBase<Int, Never>(subscription: subscription)
|
|
let tracking = TrackingSubscriberBase<Int, Never>()
|
|
|
|
let testObject = TestObject()
|
|
publisher.assign(to: &testObject.$state)
|
|
testObject.$state.subscribe(tracking)
|
|
|
|
XCTAssertEqual(subscription.history, [.requested(.unlimited)])
|
|
XCTAssertEqual(tracking.history, [.subscription("PublishedSubject")])
|
|
|
|
publisher.send(completion: .finished)
|
|
|
|
XCTAssertEqual(subscription.history, [.requested(.unlimited)])
|
|
XCTAssertEqual(tracking.history, [.subscription("PublishedSubject")])
|
|
}
|
|
|
|
func testPublishedSubjectDemand() throws {
|
|
let testObject = TestObject()
|
|
var downstreamSubscription: Subscription?
|
|
let tracking = TrackingSubscriberBase<Int, Never>(
|
|
receiveSubscription: { downstreamSubscription = $0 },
|
|
receiveValue: { .max($0) }
|
|
)
|
|
|
|
testObject.$state.subscribe(tracking)
|
|
|
|
try XCTUnwrap(downstreamSubscription).request(.max(2))
|
|
XCTAssertEqual(tracking.history, [.subscription("PublishedSubject"),
|
|
.value(0)])
|
|
// demand = 1
|
|
|
|
testObject.state = 1
|
|
XCTAssertEqual(tracking.history, [.subscription("PublishedSubject"),
|
|
.value(0),
|
|
.value(1)])
|
|
// demand = 1 - 1 + 1 = 1
|
|
|
|
testObject.state = 2
|
|
XCTAssertEqual(tracking.history, [.subscription("PublishedSubject"),
|
|
.value(0),
|
|
.value(1),
|
|
.value(2)])
|
|
// demand = 1 - 1 + 2 = 2
|
|
|
|
testObject.state = 0
|
|
testObject.state = 0
|
|
testObject.state = 0
|
|
XCTAssertEqual(tracking.history, [.subscription("PublishedSubject"),
|
|
.value(0),
|
|
.value(1),
|
|
.value(2),
|
|
.value(0),
|
|
.value(0)])
|
|
// demand = 2 - 2 = 0, value pending
|
|
|
|
try XCTUnwrap(downstreamSubscription).request(.max(2))
|
|
try XCTUnwrap(downstreamSubscription).request(.max(3))
|
|
XCTAssertEqual(tracking.history, [.subscription("PublishedSubject"),
|
|
.value(0),
|
|
.value(1),
|
|
.value(2),
|
|
.value(0),
|
|
.value(0),
|
|
.value(0)])
|
|
// demand = 0 + 2 + 3 - 1 = 4
|
|
|
|
testObject.state = 0
|
|
testObject.state = 0
|
|
testObject.state = 0
|
|
testObject.state = 0
|
|
testObject.state = 1
|
|
XCTAssertEqual(tracking.history, [.subscription("PublishedSubject"),
|
|
.value(0),
|
|
.value(1),
|
|
.value(2),
|
|
.value(0),
|
|
.value(0),
|
|
.value(0),
|
|
.value(0),
|
|
.value(0),
|
|
.value(0),
|
|
.value(0)])
|
|
// demand = 4 - 4 = 0, value pending
|
|
|
|
try XCTUnwrap(downstreamSubscription).request(.max(1))
|
|
testObject.state = 0
|
|
testObject.state = 0
|
|
XCTAssertEqual(tracking.history, [.subscription("PublishedSubject"),
|
|
.value(0),
|
|
.value(1),
|
|
.value(2),
|
|
.value(0),
|
|
.value(0),
|
|
.value(0),
|
|
.value(0),
|
|
.value(0),
|
|
.value(0),
|
|
.value(0),
|
|
.value(1),
|
|
.value(0)])
|
|
// demand = 0
|
|
}
|
|
|
|
func testPublishedSubjectCancelAlreadyCancelled() throws {
|
|
let testObject = TestObject()
|
|
var downstreamSubscription: Subscription?
|
|
let tracking = TrackingSubscriberBase<Int, Never>(
|
|
receiveSubscription: { downstreamSubscription = $0 },
|
|
receiveValue: { _ in .max(1) }
|
|
)
|
|
|
|
testObject.$state.subscribe(tracking)
|
|
|
|
try XCTUnwrap(downstreamSubscription).request(.max(1))
|
|
try XCTUnwrap(downstreamSubscription).cancel()
|
|
try XCTUnwrap(downstreamSubscription).request(.max(100))
|
|
try XCTUnwrap(downstreamSubscription).cancel()
|
|
|
|
testObject.state = 42
|
|
|
|
XCTAssertEqual(tracking.history, [.subscription("PublishedSubject"),
|
|
.value(0)])
|
|
}
|
|
|
|
func testPublishedSubjectConduitReflection() throws {
|
|
let testObject = TestObject()
|
|
try testSubscriptionReflection(
|
|
description: "PublishedSubject",
|
|
customMirror: expectedChildren(
|
|
("parent", .contains("PublishedSubject")),
|
|
("downstream", .contains("TrackingSubscriberBase")),
|
|
("demand", "max(0)"),
|
|
("subject", .contains("PublishedSubject"))
|
|
),
|
|
playgroundDescription: "PublishedSubject",
|
|
sut: testObject.$state
|
|
)
|
|
}
|
|
|
|
@available(macOS 11.0, iOS 14.0, *)
|
|
func testProjectedValueSetter() {
|
|
let testObject1 = TestObject(1)
|
|
let testObject2 = TestObject(2)
|
|
|
|
XCTAssertEqual(testObject1.state, 1)
|
|
XCTAssertEqual(testObject2.state, 2)
|
|
|
|
testObject1.$state = testObject2.$state
|
|
|
|
XCTAssertEqual(testObject1.state, 1)
|
|
XCTAssertEqual(testObject2.state, 2)
|
|
|
|
testObject1.$state = testObject2.$state
|
|
|
|
XCTAssertEqual(testObject1.state, 1)
|
|
XCTAssertEqual(testObject2.state, 2)
|
|
}
|
|
}
|
|
|
|
@available(macOS 10.15, iOS 13.0, *)
|
|
private final class TestObject: ObservableObject {
|
|
|
|
let objectWillChange = ObservableObjectPublisher()
|
|
|
|
@Published var state: Int
|
|
|
|
init(_ initialValue: Int = 0) {
|
|
_state = Published(initialValue: initialValue)
|
|
}
|
|
}
|
|
|
|
#endif
|