@@ -9,14 +9,22 @@ import Foundation
99import os
1010
1111/// A stub for a function.
12+ ///
13+ /// - Note: ``Stub`` and `ThrowingStub` are `Observable`, but the only property that is tracked is ``calls``. Changes to
14+ /// dependent properties like ``callArguments`` and ``callResults`` can also be tracked, but changes to
15+ /// ``resultQueue`` and ``defaultResult`` are not.
16+ @Observable
1217public final class ThrowingStub < Arguments, ReturnType, ErrorType> where ErrorType: Error {
1318 /// A recorded call to the stub.
14- public struct Call {
19+ ///
20+ /// This type conforms to Sendable, but both properties are `nonisolated(unsafe)`. It is the consumer’s
21+ /// responsibility to ensure that the type is used in a safe way.
22+ public struct Call : Sendable {
1523 /// The call’s arguments.
16- public let arguments : Arguments
24+ public nonisolated ( unsafe ) let arguments: Arguments
1725
1826 /// The result of the call.
19- public let result : Result < ReturnType , ErrorType >
27+ public nonisolated ( unsafe ) let result: Result < ReturnType , ErrorType >
2028 }
2129
2230
@@ -88,13 +96,16 @@ public final class ThrowingStub<Arguments, ReturnType, ErrorType> where ErrorTyp
8896 ///
8997 /// If you just need the call’s arguments, you can use ``callArguments`` instead.
9098 public var calls : [ Call ] {
99+ access ( keyPath: \. calls)
91100 return mutableProperties. withLockUnchecked { $0. calls }
92101 }
93102
94103
95104 /// Clears the stub’s recorded calls.
96105 public func clearCalls( ) {
97- mutableProperties. withLockUnchecked { $0. calls = [ ] }
106+ withMutation ( keyPath: \. calls) {
107+ mutableProperties. withLockUnchecked { $0. calls = [ ] }
108+ }
98109 }
99110
100111
@@ -105,19 +116,21 @@ public final class ThrowingStub<Arguments, ReturnType, ErrorType> where ErrorTyp
105116 ///
106117 /// - Parameter arguments: The arguments with which to call the stub.
107118 public func callAsFunction( _ arguments: Arguments ) throws ( ErrorType) -> ReturnType {
108- let result = mutableProperties. withLockUnchecked { ( properties) in
109- let result =
110- if properties. resultQueue. isEmpty {
111- properties. defaultResult
112- } else {
113- properties. resultQueue. removeFirst ( )
114- }
115-
116- properties. calls. append (
117- . init( arguments: arguments, result: result)
118- )
119-
120- return result
119+ let result = withMutation ( keyPath: \. calls) {
120+ mutableProperties. withLockUnchecked { ( properties) in
121+ let result =
122+ if properties. resultQueue. isEmpty {
123+ properties. defaultResult
124+ } else {
125+ properties. resultQueue. removeFirst ( )
126+ }
127+
128+ properties. calls. append (
129+ . init( arguments: arguments, result: result)
130+ )
131+
132+ return result
133+ }
121134 }
122135
123136 return try result. get ( )
0 commit comments