Files
swift-composable-architectu…/Tests/ComposableArchitectureTests/RuntimeWarningTests.swift
Stephen Celis 195284b94b The Composable Architecture 1.0 (#2318)
* 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

* Remove old deprecations

* Simplify test store

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* images for tutorials

* wip

* wip

* Remove deprecated alert APIs

* Bump dependencies

* wip

---------

Co-authored-by: Brandon Williams <mbrandonw@hey.com>
Co-authored-by: 유재호 <y73447jh@gmail.com>
Co-authored-by: Dmytro <barabashdmyto@gmail.com>
Co-authored-by: Brandon Williams <135203+mbrandonw@users.noreply.github.com>
Co-authored-by: mbrandonw <mbrandonw@users.noreply.github.com>
2023-07-30 14:58:40 -07:00

245 lines
8.0 KiB
Swift

#if DEBUG
import Combine
import ComposableArchitecture
import XCTest
final class RuntimeWarningTests: BaseTCATestCase {
func testStoreCreationMainThread() {
uncheckedUseMainSerialExecutor = false
XCTExpectFailure {
$0.compactDescription == """
A store initialized on a non-main thread. …
The "Store" class is not thread-safe, and so all interactions with an instance of "Store" \
(including all of its scopes and derived view stores) must be done on the main thread.
"""
}
Task {
_ = Store<Int, Void>(initialState: 0) {}
}
_ = XCTWaiter.wait(for: [.init()], timeout: 0.5)
}
func testEffectFinishedMainThread() {
XCTExpectFailure {
$0.compactDescription == """
An effect completed on a non-main thread. …
Effect returned from:
RuntimeWarningTests.Action.tap
Make sure to use ".receive(on:)" on any effects that execute on background threads to \
receive their output on the main thread.
The "Store" class is not thread-safe, and so all interactions with an instance of "Store" \
(including all of its scopes and derived view stores) must be done on the main thread.
"""
}
enum Action { case tap, response }
let store = Store(initialState: 0) {
Reduce<Int, Action> { state, action in
switch action {
case .tap:
return .publisher {
Empty()
.receive(on: DispatchQueue(label: "background"))
}
case .response:
return .none
}
}
}
store.send(.tap)
_ = XCTWaiter.wait(for: [.init()], timeout: 0.5)
}
func testStoreScopeMainThread() {
uncheckedUseMainSerialExecutor = false
XCTExpectFailure {
[
"""
"Store.scope" was called on a non-main thread. …
The "Store" class is not thread-safe, and so all interactions with an instance of \
"Store" (including all of its scopes and derived view stores) must be done on the main \
thread.
""",
"""
A store initialized on a non-main thread. …
The "Store" class is not thread-safe, and so all interactions with an instance of "Store" \
(including all of its scopes and derived view stores) must be done on the main thread.
""",
].contains($0.compactDescription)
}
let store = Store<Int, Void>(initialState: 0) {}
Task {
_ = store.scope(state: { $0 }, action: { $0 })
}
_ = XCTWaiter.wait(for: [.init()], timeout: 0.5)
}
func testViewStoreSendMainThread() {
uncheckedUseMainSerialExecutor = false
XCTExpectFailure {
[
"""
"ViewStore.send" was called on a non-main thread with: () …
The "Store" class is not thread-safe, and so all interactions with an instance of \
"Store" (including all of its scopes and derived view stores) must be done on the main \
thread.
""",
"""
An effect completed on a non-main thread. …
Effect returned from:
()
Make sure to use ".receive(on:)" on any effects that execute on background threads to \
receive their output on the main thread.
The "Store" class is not thread-safe, and so all interactions with an instance of "Store" \
(including all of its scopes and derived view stores) must be done on the main thread.
""",
].contains($0.compactDescription)
}
let store = Store<Int, Void>(initialState: 0) {}
Task {
store.send(())
}
_ = XCTWaiter.wait(for: [.init()], timeout: 0.5)
}
#if os(macOS)
@MainActor
func testEffectEmitMainThread() async throws {
try XCTSkipIf(ProcessInfo.processInfo.environment["CI"] != nil)
XCTExpectFailure {
[
"""
An effect completed on a non-main thread. …
Effect returned from:
RuntimeWarningTests.Action.response
Make sure to use ".receive(on:)" on any effects that execute on background threads to \
receive their output on the main thread.
The "Store" class is not thread-safe, and so all interactions with an instance of \
"Store" (including all of its scopes and derived view stores) must be done on the main \
thread.
""",
"""
An effect completed on a non-main thread. …
Effect returned from:
RuntimeWarningTests.Action.tap
Make sure to use ".receive(on:)" on any effects that execute on background threads to \
receive their output on the main thread.
The "Store" class is not thread-safe, and so all interactions with an instance of \
"Store" (including all of its scopes and derived view stores) must be done on the main \
thread.
""",
"""
An effect published an action on a non-main thread. …
Effect published:
RuntimeWarningTests.Action.response
Effect returned from:
RuntimeWarningTests.Action.tap
Make sure to use ".receive(on:)" on any effects that execute on background threads to \
receive their output on the main thread.
The "Store" class is not thread-safe, and so all interactions with an instance of \
"Store" (including all of its scopes and derived view stores) must be done on the main \
thread.
""",
]
.contains($0.compactDescription)
}
enum Action { case tap, response }
let store = Store(initialState: 0) {
Reduce<Int, Action> { state, action in
switch action {
case .tap:
return .publisher {
Future { callback in
Thread.detachNewThread {
XCTAssertFalse(Thread.isMainThread, "Effect should send on non-main thread.")
callback(.success(.response))
}
}
}
case .response:
return .none
}
}
}
await ViewStore(store, observe: { $0 }).send(.tap).finish()
}
#endif
@MainActor
func testBindingUnhandledAction() {
let line = #line + 2
struct State: Equatable {
@BindingState var value = 0
}
enum Action: BindableAction, Equatable {
case binding(BindingAction<State>)
}
let store = Store<State, Action>(initialState: State()) {}
XCTExpectFailure {
ViewStore(store, observe: { $0 }).$value.wrappedValue = 42
} issueMatcher: {
$0.compactDescription == """
A binding action sent from a view store for binding state defined at \
"\(#fileID):\(line)" was not handled. …
Action:
RuntimeWarningTests.Action.binding(.set(_, 42))
To fix this, invoke "BindingReducer()" from your feature reducer's "body".
"""
}
}
@MainActor
func testBindingUnhandledAction_BindingState() {
struct State: Equatable {
@BindingState var value = 0
}
let line = #line - 2
enum Action: BindableAction, Equatable {
case binding(BindingAction<State>)
}
let store = Store<State, Action>(initialState: State()) {}
XCTExpectFailure {
ViewStore(store, observe: { $0 }).$value.wrappedValue = 42
} issueMatcher: {
$0.compactDescription == """
A binding action sent from a view store for binding state defined at \
"\(#fileID):\(line)" was not handled. …
Action:
RuntimeWarningTests.Action.binding(.set(_, 42))
To fix this, invoke "BindingReducer()" from your feature reducer's "body".
"""
}
}
}
#endif