Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infrastructure: Address concurrency warnings in tests #3438

Merged
merged 2 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Sources/ComposableArchitecture/Effect.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import Combine
@preconcurrency import Combine
import Foundation
import SwiftUI

public struct Effect<Action> {
public struct Effect<Action>: Sendable {
@usableFromInline
enum Operation {
enum Operation: Sendable {
case none
case publisher(AnyPublisher<Action, Never>)
case run(TaskPriority? = nil, @Sendable (_ send: Send<Action>) async -> Void)
Expand Down
1 change: 1 addition & 0 deletions Tests/ComposableArchitectureTests/BindingLocalTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
@testable import ComposableArchitecture

final class BindingLocalTests: BaseTCATestCase {
@MainActor
public func testBindingLocalIsActive() {
XCTAssertFalse(BindingLocal.isActive)

Expand Down
2 changes: 2 additions & 0 deletions Tests/ComposableArchitectureTests/CompatibilityTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ final class CompatibilityTests: BaseTCATestCase {
// 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.
@MainActor
func testCaseStudy_ActionReentranceFromClearedBufferCausingDeinitAction() {
let cancelID = UUID()

Expand Down Expand Up @@ -78,6 +79,7 @@ final class CompatibilityTests: BaseTCATestCase {
// 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.
@MainActor
func testCaseStudy_ActionReentranceFromStateObservation() {
var cancellables: Set<AnyCancellable> = []
defer { _ = cancellables }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

await withTaskGroup(of: Void.self) { group in
for index in 1...1_000 {
group.addTask {
group.addTask { @Sendable in
subject.send(index)
}
}
Expand Down
8 changes: 4 additions & 4 deletions Tests/ComposableArchitectureTests/EffectTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ final class EffectTests: BaseTCATestCase {
}

func testConcatenateOneEffect() async {
await withMainSerialExecutor {
await withMainSerialExecutor { [mainQueue] in
let values = LockIsolated<[Int]>([])

let effect = Effect<Int>.concatenate(
.publisher { Just(1).delay(for: 1, scheduler: self.mainQueue) }
.publisher { Just(1).delay(for: 1, scheduler: mainQueue) }
)

let task = Task {
Expand All @@ -62,10 +62,10 @@ final class EffectTests: BaseTCATestCase {

XCTAssertEqual(values.value, [])

await self.mainQueue.advance(by: 1)
await mainQueue.advance(by: 1)
XCTAssertEqual(values.value, [1])

await self.mainQueue.run()
await mainQueue.run()
XCTAssertEqual(values.value, [1])

await task.value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ final class BindingTests: BaseTCATestCase {
)
}

@MainActor
func testViewEquality() {
struct Feature: Reducer {
struct State: Equatable {
Expand Down
5 changes: 5 additions & 0 deletions Tests/ComposableArchitectureTests/ScopeCacheTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ final class ScopeCacheTests: BaseTCATestCase {
store.send(.child(.dismiss))
}

@MainActor
func testOptionalScope_CachedStore() {
#if DEBUG
let store = StoreOf<Feature>(initialState: Feature.State(child: Feature.State())) {
Expand All @@ -46,6 +47,7 @@ final class ScopeCacheTests: BaseTCATestCase {
#endif
}

@MainActor
func testOptionalScope_StoreIfLet() {
#if DEBUG
let store = StoreOf<Feature>(initialState: Feature.State(child: Feature.State())) {
Expand All @@ -62,6 +64,7 @@ final class ScopeCacheTests: BaseTCATestCase {
}

@available(*, deprecated)
@MainActor
func testOptionalScope_StoreIfLet_UncachedStore() {
let store = StoreOf<Feature>(initialState: Feature.State(child: Feature.State())) {
}
Expand Down Expand Up @@ -93,6 +96,7 @@ final class ScopeCacheTests: BaseTCATestCase {
}
}

@MainActor
func testIdentifiedArrayScope_CachedStore() {
#if DEBUG
let store = StoreOf<Feature>(initialState: Feature.State(rows: [Feature.State()])) {
Expand All @@ -108,6 +112,7 @@ final class ScopeCacheTests: BaseTCATestCase {
}

@available(*, deprecated)
@MainActor
func testIdentifiedArrayScope_UncachedStore() {
let store = StoreOf<Feature>(initialState: Feature.State(rows: [Feature.State()])) {
Feature()
Expand Down
15 changes: 8 additions & 7 deletions Tests/ComposableArchitectureTests/SharedTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,11 @@ final class SharedTests: XCTestCase {
XCTAssertEqual(store.state.sharedCount, 2)
}

@MainActor
func testMultiSharing() async {
@Shared(Stats()) var stats

let store = await TestStore(
let store = TestStore(
initialState: SharedFeature.State(
profile: Shared(Profile(stats: $stats)),
sharedCount: Shared(0),
Expand Down Expand Up @@ -721,7 +722,7 @@ final class SharedTests: XCTestCase {

func testSharedDefaults_Used() {
let didAccess = LockIsolated(false)
let logDefault: () -> Bool = {
let logDefault: @Sendable () -> Bool = {
didAccess.setValue(true)
return true
}
Expand All @@ -732,7 +733,7 @@ final class SharedTests: XCTestCase {

func testSharedDefaults_Unused() {
let didAccess = LockIsolated(false)
let logDefault: () -> Bool = {
let logDefault: @Sendable () -> Bool = {
didAccess.setValue(true)
return true
}
Expand Down Expand Up @@ -761,7 +762,7 @@ final class SharedTests: XCTestCase {
func testSharedOverrideDefault() {
let accessedActive1 = LockIsolated(false)
let accessedDefault = LockIsolated(false)
let logDefault: () -> Bool = {
let logDefault: @Sendable () -> Bool = {
accessedDefault.setValue(true)
return true
}
Expand Down Expand Up @@ -796,7 +797,7 @@ final class SharedTests: XCTestCase {
func testSharedReaderOverrideDefault() {
let accessedActive1 = LockIsolated(false)
let accessedDefault = LockIsolated(false)
let logDefault: () -> Bool = {
let logDefault: @Sendable () -> Bool = {
accessedDefault.setValue(true)
return true
}
Expand Down Expand Up @@ -1141,7 +1142,7 @@ private struct SimpleFeature {
}

@Perceptible
class SharedObject {
class SharedObject: @unchecked Sendable {
var count = 0
}

Expand Down Expand Up @@ -1244,7 +1245,7 @@ extension PersistenceReaderKey where Self == PersistenceKeyDefault<AppStorageKey
PersistenceKeyDefault(.appStorage("isOn"), false)
}

static func isActive(default keyDefault: @escaping () -> Bool) -> Self {
static func isActive(default keyDefault: @escaping @Sendable () -> Bool) -> Self {
PersistenceKeyDefault(.appStorage("isActive"), keyDefault())
}
}
Expand Down
1 change: 1 addition & 0 deletions Tests/ComposableArchitectureTests/StoreFilterTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Combine
import XCTest

final class StoreInvalidationTests: BaseTCATestCase {
@MainActor
func testInvalidation() {
var cancellables: Set<AnyCancellable> = []

Expand Down
3 changes: 3 additions & 0 deletions Tests/ComposableArchitectureTests/StoreLifetimeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import XCTest

final class StoreLifetimeTests: BaseTCATestCase {
@available(*, deprecated)
@MainActor
func testStoreCaching() {
let grandparentStore = Store(initialState: Grandparent.State()) {
Grandparent()
Expand All @@ -21,6 +22,7 @@ final class StoreLifetimeTests: BaseTCATestCase {
}

@available(*, deprecated)
@MainActor
func testStoreInvalidation() {
let grandparentStore = Store(initialState: Grandparent.State()) {
Grandparent()
Expand Down Expand Up @@ -48,6 +50,7 @@ final class StoreLifetimeTests: BaseTCATestCase {
}

#if DEBUG
@MainActor
func testStoreDeinit() {
Logger.shared.isEnabled = true
do {
Expand Down
15 changes: 13 additions & 2 deletions Tests/ComposableArchitectureTests/StoreTests.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Combine
@preconcurrency import Combine
@_spi(Internals) import ComposableArchitecture
import XCTest

Expand Down Expand Up @@ -47,6 +47,7 @@ final class StoreTests: BaseTCATestCase {
}

@available(*, deprecated)
@MainActor
func testScopedStoreReceivesUpdatesFromParent() {
let counterReducer = Reduce<Int, Void>({ state, _ in
state += 1
Expand All @@ -71,6 +72,7 @@ final class StoreTests: BaseTCATestCase {
}

@available(*, deprecated)
@MainActor
func testParentStoreReceivesUpdatesFromChild() {
let counterReducer = Reduce<Int, Void>({ state, _ in
state += 1
Expand All @@ -95,6 +97,7 @@ final class StoreTests: BaseTCATestCase {
}

@available(*, deprecated)
@MainActor
func testScopeCallCount_OneLevel_NoSubscription() {
var numCalls1 = 0
let store = Store<Int, Void>(initialState: 0) {}
Expand All @@ -112,6 +115,7 @@ final class StoreTests: BaseTCATestCase {
}

@available(*, deprecated)
@MainActor
func testScopeCallCount_OneLevel_Subscribing() {
var numCalls1 = 0
let store = Store<Int, Void>(initialState: 0) {}
Expand All @@ -130,6 +134,7 @@ final class StoreTests: BaseTCATestCase {
}

@available(*, deprecated)
@MainActor
func testScopeCallCount_TwoLevels_Subscribing() {
var numCalls1 = 0
var numCalls2 = 0
Expand Down Expand Up @@ -158,6 +163,7 @@ final class StoreTests: BaseTCATestCase {
}

@available(*, deprecated)
@MainActor
func testScopeCallCount_ThreeLevels_ViewStoreSubscribing() {
var numCalls1 = 0
var numCalls2 = 0
Expand Down Expand Up @@ -280,6 +286,7 @@ final class StoreTests: BaseTCATestCase {
XCTAssertEqual(values, [1, 2, 3, 4])
}

@MainActor
func testLotsOfSynchronousActions() {
enum Action { case incr, noop }
let reducer = Reduce<Int, Action>({ state, action in
Expand Down Expand Up @@ -350,6 +357,7 @@ final class StoreTests: BaseTCATestCase {
XCTAssertEqual(outputs, [nil, 1, nil, 1, nil, 1, nil])
}

@MainActor
func testIfLetTwo() {
let parentStore = Store(initialState: 0) {
Reduce<Int?, Bool> { state, action in
Expand Down Expand Up @@ -382,6 +390,7 @@ final class StoreTests: BaseTCATestCase {
.store(in: &self.cancellables)
}

@MainActor
func testActionQueuing() async {
let subject = PassthroughSubject<Void, Never>()

Expand All @@ -391,7 +400,7 @@ final class StoreTests: BaseTCATestCase {
case doIncrement
}

let store = await TestStore(initialState: 0) {
let store = TestStore(initialState: 0) {
Reduce<Int, Action> { state, action in
switch action {
case .incrementTapped:
Expand Down Expand Up @@ -420,6 +429,7 @@ final class StoreTests: BaseTCATestCase {
subject.send(completion: .finished)
}

@MainActor
func testCoalesceSynchronousActions() {
let store = Store(initialState: 0) {
Reduce<Int, Int> { state, action in
Expand Down Expand Up @@ -451,6 +461,7 @@ final class StoreTests: BaseTCATestCase {
}

@available(*, deprecated)
@MainActor
func testBufferedActionProcessing() {
struct ChildState: Equatable {
var count: Int?
Expand Down
8 changes: 5 additions & 3 deletions Tests/ComposableArchitectureTests/TestStoreTests.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Combine
@preconcurrency import Combine
import ComposableArchitecture
import XCTest

Expand Down Expand Up @@ -396,6 +396,7 @@ final class TestStoreTests: BaseTCATestCase {
}
}

@MainActor
func testPrepareDependenciesCalledOnce() {
var count = 0
let store = TestStore(initialState: 0) {
Expand Down Expand Up @@ -469,6 +470,7 @@ final class TestStoreTests: BaseTCATestCase {
}
}

@MainActor
func testSubscribeReceiveCombineScheduler() async {
let subject = PassthroughSubject<Void, Never>()
let scheduler = DispatchQueue.test
Expand All @@ -482,7 +484,7 @@ final class TestStoreTests: BaseTCATestCase {
case start
}

let store = await TestStore(initialState: State()) {
let store = TestStore(initialState: State()) {
Reduce<State, Action> { state, action in
switch action {
case .start:
Expand Down Expand Up @@ -659,7 +661,7 @@ final class TestStoreTests: BaseTCATestCase {
}

private struct Client: DependencyKey {
var fetch: () -> Int
var fetch: @Sendable () -> Int
static let liveValue = Client(fetch: { 42 })
}
extension DependencyValues {
Expand Down
Loading
Loading