Skip to content

Commit 3b758ad

Browse files
authored
Get some test coverage on child/parent effect cancellation behavior. (pointfreeco#1970)
* Get some test coverage on child/parent effect cancellation behavior. * 5.6 fix * wip * rename
1 parent 3e8eee1 commit 3b758ad

File tree

5 files changed

+89
-5
lines changed

5 files changed

+89
-5
lines changed

ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.resolved

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ let package = Package(
2626
.package(url: "https://github.com/pointfreeco/swift-dependencies", from: "0.2.0"),
2727
.package(url: "https://github.com/pointfreeco/swift-identified-collections", from: "0.7.0"),
2828
.package(url: "https://github.com/pointfreeco/swiftui-navigation", from: "0.7.0"),
29-
.package(url: "https://github.com/pointfreeco/xctest-dynamic-overlay", from: "0.5.0"),
29+
.package(url: "https://github.com/pointfreeco/xctest-dynamic-overlay", from: "0.8.3"),
3030
],
3131
targets: [
3232
.target(

Tests/ComposableArchitectureTests/StoreTests.swift

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,80 @@ final class StoreTests: XCTestCase {
682682
await store.send(.tap)?.value
683683
XCTAssertEqual(store.state.value.count, testStore.state.count)
684684
}
685+
686+
#if swift(>=5.7)
687+
func testChidlParentEffectCancellation() async throws {
688+
struct Child: ReducerProtocol {
689+
struct State: Equatable {}
690+
enum Action: Equatable {
691+
case task
692+
case didFinish
693+
}
694+
695+
func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
696+
switch action {
697+
case .task:
698+
return .run { send in await send(.didFinish) }
699+
case .didFinish:
700+
return .none
701+
}
702+
}
703+
}
704+
struct Parent: ReducerProtocol {
705+
struct State: Equatable {
706+
var count = 0
707+
var child: Child.State?
708+
}
709+
enum Action: Equatable {
710+
case child(Child.Action)
711+
case delay
712+
}
713+
@Dependency(\.mainQueue) var mainQueue
714+
var body: Reduce<State, Action> {
715+
Reduce { state, action in
716+
switch action {
717+
case .child(.didFinish):
718+
state.child = nil
719+
return .task {
720+
try await self.mainQueue.sleep(for: .seconds(1))
721+
return .delay
722+
}
723+
case .child:
724+
return .none
725+
case .delay:
726+
state.count += 1
727+
return .none
728+
}
729+
}
730+
.ifLet(\.child, action: /Action.child) {
731+
Child()
732+
}
733+
}
734+
}
735+
736+
let mainQueue = DispatchQueue.test
737+
let store = Store(
738+
initialState: Parent.State(child: Child.State()),
739+
reducer: Parent()
740+
) {
741+
$0.mainQueue = mainQueue.eraseToAnyScheduler()
742+
}
743+
let viewStore = ViewStore(store)
744+
745+
let childTask = viewStore.send(.child(.task))
746+
try await Task.sleep(nanoseconds: 100_000_000)
747+
XCTAssertEqual(viewStore.child, nil)
748+
749+
await childTask.cancel()
750+
await mainQueue.advance(by: 1)
751+
try await Task.sleep(nanoseconds: 100_000_000)
752+
XCTTODO(
753+
"""
754+
This fails because cancelling a child task will cancel all parent effects too.
755+
""")
756+
XCTAssertEqual(viewStore.count, 1)
757+
}
758+
#endif
685759
}
686760

687761
private struct Count: TestDependencyKey {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import XCTest
2+
3+
@available(
4+
*,
5+
deprecated,
6+
message: "This is a test that currently fails but should not in the future."
7+
)
8+
func XCTTODO(_ message: String) {
9+
XCTExpectFailure(message)
10+
}

0 commit comments

Comments
 (0)