Files
swift-composable-architectu…/Tests/ComposableArchitectureTests/CompatibilityTests.swift
Brandon Williams 2c93195c23 Prerelease 1.0 (#1929)
* Converted voice memos back to identified array

* update deps

* update docs for DismissEffect

* wip

* Add Sendable conformance to PresentationState (#2086)

* wip

* swift-format

* wip

* wip

* fix some warnings

* docs

* wip

* wip

* Catch some typos in Articles (#2088)

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* docs

* wip

* wip

* docs

* wip

* wip

* wip

* wip

* docs

* docs

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* Fix invalid states count for 3 optionals and typos (#2094)

* wip

* wip

* more dismisseffect docs

* fixed some references

* navigation doc corrections

* more nav docs

* fix cancellation tests in release mode

* wrap some tests in #if DEBUG since they are testing expected failures

* update UUIDs in tests to use shorter initializer

* fixed a todo

* wip

* fix merge errors

* wip

* fix

* wip

* wip

* fixing a bunch of todos

* get rid of rawvalue in StackElementID

* more todos

* NavLinkStore docs

* fix swift 5.6 stuff

* fix some standups tests

* fix

* clean up

* docs fix

* fixes

* wip

* 5.6 fix

* wip

* wip

* dont parallelize tests

* updated demo readmes

* wip

* Use ObservedObject instead of StateObject for alert/dialog modifiers.

* integration tests for bad dismissal behavior

* check for runtime warnings in every integration test

* wip

* wip

* wip

* fix

* wip

* wip

* wip

* wip

* wip

* wip

* Drop a bunch of Hashables.

* some nav bug fixes

* wip

* wip

* wip

* fix

* fix

* wip

* wip

* Simplify recording test.

* add concurrent async test

* fix

* wip

* Refact how detail dismisses itself.

* fix

* 5.6 fix

* wip

* wip

* wip

* wip

* Add TestStore.assert.

* Revert "Add TestStore.assert."

This reverts commit a892cccc66.

* add Ukrainian Readme.md (#2121)

* Add TestStore.assert. (#2123)

* Add TestStore.assert.

* wip

* Update Sources/ComposableArchitecture/TestStore.swift

Co-authored-by: Stephen Celis <stephen@stephencelis.com>

* Update Sources/ComposableArchitecture/Documentation.docc/Extensions/TestStore.md

Co-authored-by: Stephen Celis <stephen@stephencelis.com>

* fix tests

---------

Co-authored-by: Stephen Celis <stephen@stephencelis.com>

* Run swift-format

* push for store.finish and presentation

* wip

* move docs around

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* Add case subscripts

* wip

* wip

* wip

* 5.7-only

* wip

* wip

* wip

* wip

* fix

* revert store.finish task cancellation

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* add test for presentation scope

* wip

* wip

* wip

* wip

* wip

* cleanup

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* Rename ReducerProtocol.swift to Reducer.swift (#2206)

* Hard-deprecate old SwitchStore initializers/overloads

* wip

* wip

* Resolve CaseStudies crash (#2258)

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* Bump timeout for CI

* wip

* wip

---------

Co-authored-by: Jackson Utsch <jutechs@gmail.com>
Co-authored-by: Stephen Celis <stephen@stephencelis.com>
Co-authored-by: 유재호 <y73447jh@gmail.com>
Co-authored-by: Dmytro <barabashdmyto@gmail.com>
Co-authored-by: mbrandonw <mbrandonw@users.noreply.github.com>
2023-07-27 17:35:07 -07:00

123 lines
3.2 KiB
Swift

import Combine
import ComposableArchitecture
import XCTest
@MainActor
final class CompatibilityTests: BaseTCATestCase {
var cancellables: Set<AnyCancellable> = []
// Actions can be re-entrantly sent into the store if an action is sent that holds an object
// which sends an action on deinit. In order to prevent a simultaneous access exception for this
// case we need to use `withExtendedLifetime` on the buffered actions when clearing them out.
func testCaseStudy_ActionReentranceFromClearedBufferCausingDeinitAction() {
let cancelID = UUID()
struct State: Equatable {}
enum Action: Equatable {
case start
case kickOffAction
case actionSender(OnDeinit)
case stop
var description: String {
switch self {
case .start:
return "start"
case .kickOffAction:
return "kickOffAction"
case .actionSender:
return "actionSender"
case .stop:
return "stop"
}
}
}
let passThroughSubject = PassthroughSubject<Action, Never>()
var handledActions: [String] = []
let reducer = Reduce<State, Action> { state, action in
handledActions.append(action.description)
switch action {
case .start:
return .publisher { passThroughSubject }.cancellable(id: cancelID)
case .kickOffAction:
return .send(.actionSender(OnDeinit { passThroughSubject.send(.stop) }))
case .actionSender:
return .none
case .stop:
return .cancel(id: cancelID)
}
}
let store = Store(initialState: .init()) {
reducer
}
let viewStore = ViewStore(store, observe: { $0 })
viewStore.send(.start)
viewStore.send(.kickOffAction)
XCTAssertEqual(
handledActions,
[
"start",
"kickOffAction",
"actionSender",
"stop",
]
)
}
// Actions can be re-entrantly sent into the store while observing changes to the store's state.
// In such cases we need to take special care that those re-entrant actions are handled _after_
// the original action.
//
// In particular, this means that in the implementation of `Store.send` we need to flip
// `isSending` to false _after_ the store's state mutation is made so that re-entrant actions
// are buffered rather than immediately handled.
func testCaseStudy_ActionReentranceFromStateObservation() {
let store = Store<Int, Int>(initialState: 0) {
Reduce { state, action in
state = action
return .none
}
}
let viewStore = ViewStore(store, observe: { $0 })
viewStore.publisher
.sink { value in
if value == 1 {
viewStore.send(0)
}
}
.store(in: &self.cancellables)
var stateChanges: [Int] = []
viewStore.publisher
.sink {
stateChanges.append($0)
}
.store(in: &self.cancellables)
XCTAssertEqual(stateChanges, [0])
viewStore.send(1)
XCTAssertEqual(stateChanges, [0, 1, 0])
}
}
private final class OnDeinit: Equatable {
private let onDeinit: () -> Void
init(onDeinit: @escaping () -> Void) {
self.onDeinit = onDeinit
}
deinit { self.onDeinit() }
static func == (lhs: OnDeinit, rhs: OnDeinit) -> Bool { true }
}