TRAVIS_OS_NAME=osx default= <<<<<< ENV codecov.yml .jazzy.json .scripts/doc-preprocessor .scripts/generate-docs BuildPhases/run-swiftlint Cartfile.resolved Docs/img/reswift_concept.graffle Docs/jazzy-theme/assets/css/highlight.css.scss Docs/jazzy-theme/assets/css/jazzy.css.scss Docs/jazzy-theme/assets/js/jazzy.js Docs/jazzy-theme/assets/js/jquery.min.js Docs/jazzy-theme/templates/doc.mustache Docs/jazzy-theme/templates/footer.mustache Docs/jazzy-theme/templates/header.mustache Docs/jazzy-theme/templates/nav.mustache Docs/jazzy-theme/templates/parameter.mustache Docs/jazzy-theme/templates/task.mustache Docs/jazzy-theme/templates/tasks.mustache Package.swift Package@swift-5.0.swift Podfile Podfile.lock ReSwift.podspec ReSwift.xcodeproj/project.pbxproj ReSwift.xcodeproj/project.xcworkspace/contents.xcworkspacedata ReSwift.xcodeproj/xcshareddata/xcbaselines/25DBCF861C30C4DB00D63A58.xcbaseline/8A5AF011-A17C-4780-8B4D-183154BA1DDA.plist ReSwift.xcodeproj/xcshareddata/xcbaselines/25DBCF861C30C4DB00D63A58.xcbaseline/CA98BC4B-4A03-4B07-BF9E-12C3F6A52195.plist ReSwift.xcodeproj/xcshareddata/xcbaselines/25DBCF861C30C4DB00D63A58.xcbaseline/Info.plist ReSwift.xcodeproj/xcshareddata/xcschemes/ReSwift-iOS.xcscheme ReSwift.xcodeproj/xcshareddata/xcschemes/ReSwift-macOS.xcscheme ReSwift.xcodeproj/xcshareddata/xcschemes/ReSwift-tvOS.xcscheme ReSwift.xcodeproj/xcshareddata/xcschemes/ReSwift-watchOS.xcscheme ReSwift.xcworkspace/contents.xcworkspacedata ReSwift.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ReSwift/CoreTypes/Action.swift ReSwift/CoreTypes/DispatchingStoreType.swift ReSwift/CoreTypes/Middleware.swift ReSwift/CoreTypes/Reducer.swift ReSwift/CoreTypes/State.swift ReSwift/CoreTypes/Store.swift ReSwift/CoreTypes/StoreSubscriber.swift ReSwift/CoreTypes/StoreType.swift ReSwift/CoreTypes/Subscription.swift ReSwift/Info.plist ReSwift/ReSwift.h ReSwift/Utils/Assertions.swift ReSwift/Utils/AtomicBool.swift ReSwift/Utils/Coding.swift ReSwift/Utils/TypeHelper.swift ReSwiftTests/AutomaticallySkipRepeatsTests.swift ReSwiftTests/Info.plist ReSwiftTests/PerformanceTests.swift ReSwiftTests/StoreDispatchTests.swift ReSwiftTests/StoreMiddlewareTests.swift ReSwiftTests/StoreSubscriberTests.swift ReSwiftTests/StoreSubscriptionTests.swift ReSwiftTests/StoreTests.swift ReSwiftTests/TestFakes.swift ReSwiftTests/TypeHelperTests.swift ReSwiftTests/XCTest+Assertions.swift ReSwiftTests/XCTest+Compatibility.swift SwiftLintIntegration/Info.plist SwiftLintIntegration/SwiftLintIntegration.swift <<<<<< network # path=ReSwift.framework.coverage.txt /Users/travis/build/ReSwift/ReSwift/ReSwift/CoreTypes/Store.swift: 1| |// 2| |// Store.swift 3| |// ReSwift 4| |// 5| |// Created by Benjamin Encz on 11/11/15. 6| |// Copyright © 2015 ReSwift Community. All rights reserved. 7| |// 8| | 9| |/** 10| | This class is the default implementation of the `StoreType` protocol. You will use this store in most 11| | of your applications. You shouldn't need to implement your own store. 12| | You initialize the store with a reducer and an initial application state. If your app has multiple 13| | reducers you can combine them by initializng a `MainReducer` with all of your reducers as an 14| | argument. 15| | */ 16| |open class Store: StoreType { 17| | 18| | typealias SubscriptionType = SubscriptionBox 19| | 20| | private(set) public var state: State! { 21| 68| didSet { 22| 30.0k| subscriptions.forEach { 23| 30.0k| if $0.subscriber == nil { 24| 2| subscriptions.remove($0) 25| 30.0k| } else { 26| 30.0k| $0.newValues(oldState: oldValue, newState: state) 27| 30.0k| } 28| 30.0k| } 29| 68| } 30| | } 31| | 32| | public lazy var dispatchFunction: DispatchFunction! = createDispatchFunction() 33| | 34| | private var reducer: Reducer 35| | 36| | var subscriptions: Set = [] 37| | 38| | private var isDispatching = AtomicBool() 39| | 40| | /// Indicates if new subscriptions attempt to apply `skipRepeats` 41| | /// by default. 42| | fileprivate let subscriptionsAutomaticallySkipRepeats: Bool 43| | 44| | public var middleware: [Middleware] { 45| 2| didSet { 46| 2| dispatchFunction = createDispatchFunction() 47| 2| } 48| | } 49| | 50| | /// Initializes the store with a reducer, an initial state and a list of middleware. 51| | /// 52| | /// Middleware is applied in the order in which it is passed into this constructor. 53| | /// 54| | /// - parameter reducer: Main reducer that processes incoming actions. 55| | /// - parameter state: Initial state, if any. Can be `nil` and will be 56| | /// provided by the reducer in that case. 57| | /// - parameter middleware: Ordered list of action pre-processors, acting 58| | /// before the root reducer. 59| | /// - parameter automaticallySkipsRepeats: If `true`, the store will attempt 60| | /// to skip idempotent state updates when a subscriber's state type 61| | /// implements `Equatable`. Defaults to `true`. 62| | public required init( 63| | reducer: @escaping Reducer, 64| | state: State?, 65| | middleware: [Middleware] = [], 66| | automaticallySkipsRepeats: Bool = true 67| 56| ) { 68| 56| self.subscriptionsAutomaticallySkipRepeats = automaticallySkipsRepeats 69| 56| self.reducer = reducer 70| 56| self.middleware = middleware 71| 56| 72| 56| if let state = state { 73| 55| self.state = state 74| 56| } else { 75| 1| dispatch(ReSwiftInit()) 76| 56| } 77| 56| } 78| | 79| 40| private func createDispatchFunction() -> DispatchFunction! { 80| 40| // Wrap the dispatch function with all middlewares 81| 40| return middleware 82| 40| .reversed() 83| 40| .reduce( 84| 69| { [unowned self] action in 85| 69| self._defaultDispatch(action: action) }, 86| 40| { dispatchFunction, middleware in 87| 7| // If the store get's deinitialized before the middleware is complete; drop 88| 7| // the action without dispatching. 89| 7| let dispatch: (Action) -> Void = { [weak self] in self?.dispatch($0) } 90| 7| let getState = { [weak self] in self?.state } 91| 7| return middleware(dispatch, getState)(dispatchFunction) 92| 7| }) 93| 40| } 94| | 95| | fileprivate func _subscribe( 96| | _ subscriber: S, originalSubscription: Subscription, 97| | transformedSubscription: Subscription?) 98| | where S.StoreSubscriberStateType == SelectedState 99| 33.0k| { 100| 33.0k| let subscriptionBox = self.subscriptionBox( 101| 33.0k| originalSubscription: originalSubscription, 102| 33.0k| transformedSubscription: transformedSubscription, 103| 33.0k| subscriber: subscriber 104| 33.0k| ) 105| 33.0k| 106| 33.0k| subscriptions.update(with: subscriptionBox) 107| 33.0k| 108| 33.0k| if let state = self.state { 109| 33.0k| originalSubscription.newValues(oldState: nil, newState: state) 110| 33.0k| } 111| 33.0k| } 112| | 113| | open func subscribe(_ subscriber: S) 114| 33.0k| where S.StoreSubscriberStateType == State { 115| 33.0k| _ = subscribe(subscriber, transform: nil) 116| 33.0k| } 117| | 118| | open func subscribe( 119| | _ subscriber: S, transform: ((Subscription) -> Subscription)? 120| | ) where S.StoreSubscriberStateType == SelectedState 121| 33.0k| { 122| 33.0k| // Create a subscription for the new subscriber. 123| 33.0k| let originalSubscription = Subscription() 124| 33.0k| // Call the optional transformation closure. This allows callers to modify 125| 33.0k| // the subscription, e.g. in order to subselect parts of the store's state. 126| 33.0k| let transformedSubscription = transform?(originalSubscription) 127| 33.0k| 128| 33.0k| _subscribe(subscriber, originalSubscription: originalSubscription, 129| 33.0k| transformedSubscription: transformedSubscription) 130| 33.0k| } 131| | 132| | func subscriptionBox( 133| | originalSubscription: Subscription, 134| | transformedSubscription: Subscription?, 135| | subscriber: AnyStoreSubscriber 136| 33.0k| ) -> SubscriptionBox { 137| 33.0k| 138| 33.0k| return SubscriptionBox( 139| 33.0k| originalSubscription: originalSubscription, 140| 33.0k| transformedSubscription: transformedSubscription, 141| 33.0k| subscriber: subscriber 142| 33.0k| ) 143| 33.0k| } 144| | 145| 3| open func unsubscribe(_ subscriber: AnyStoreSubscriber) { 146| 3| #if swift(>=5.0) 147| 3| if let index = subscriptions.firstIndex(where: { return $0.subscriber === subscriber }) { 148| 3| subscriptions.remove(at: index) 149| 3| } 150| 3| #else 151| 3| if let index = subscriptions.index(where: { return $0.subscriber === subscriber }) { 152| 3| subscriptions.remove(at: index) 153| 3| } 154| 3| #endif 155| 3| } 156| | 157| | // swiftlint:disable:next identifier_name 158| 69| open func _defaultDispatch(action: Action) { 159| 69| guard !isDispatching.value else { 160| 1| raiseFatalError( 161| 1| "ReSwift:ConcurrentMutationError- Action has been dispatched while" + 162| 1| " a previous action is action is being processed. A reducer" + 163| 1| " is dispatching an action, or ReSwift is used in a concurrent context" + 164| 1| " (e.g. from multiple threads)." 165| 1| ) 166| 69| } 167| 69| 168| 69| isDispatching.value = true 169| 69| let newState = reducer(action, state) 170| 69| isDispatching.value = false 171| 69| 172| 69| state = newState 173| 69| } 174| | 175| 70| open func dispatch(_ action: Action) { 176| 70| dispatchFunction(action) 177| 70| } 178| | 179| | @available(*, deprecated, message: "Deprecated in favor of https://github.com/ReSwift/ReSwift-Thunk") 180| 1| open func dispatch(_ actionCreatorProvider: @escaping ActionCreator) { 181| 1| if let action = actionCreatorProvider(state, self) { 182| 1| dispatch(action) 183| 1| } 184| 1| } 185| | 186| | @available(*, deprecated, message: "Deprecated in favor of https://github.com/ReSwift/ReSwift-Thunk") 187| 1| open func dispatch(_ asyncActionCreatorProvider: @escaping AsyncActionCreator) { 188| 1| dispatch(asyncActionCreatorProvider, callback: nil) 189| 1| } 190| | 191| | @available(*, deprecated, message: "Deprecated in favor of https://github.com/ReSwift/ReSwift-Thunk") 192| | open func dispatch(_ actionCreatorProvider: @escaping AsyncActionCreator, 193| 2| callback: DispatchCallback?) { 194| 2| actionCreatorProvider(state, self) { actionProvider in 195| 2| let action = actionProvider(self.state, self) 196| 2| 197| 2| if let action = action { 198| 2| self.dispatch(action) 199| 2| callback?(self.state) 200| 2| } 201| 2| } 202| 2| } 203| | 204| | public typealias DispatchCallback = (State) -> Void 205| | 206| | @available(*, deprecated, message: "Deprecated in favor of https://github.com/ReSwift/ReSwift-Thunk") 207| | public typealias ActionCreator = (_ state: State, _ store: Store) -> Action? 208| | 209| | @available(*, deprecated, message: "Deprecated in favor of https://github.com/ReSwift/ReSwift-Thunk") 210| | public typealias AsyncActionCreator = ( 211| | _ state: State, 212| | _ store: Store, 213| | _ actionCreatorCallback: @escaping ((ActionCreator) -> Void) 214| | ) -> Void 215| |} 216| | 217| |// MARK: Skip Repeats for Equatable States 218| | 219| |extension Store { 220| | open func subscribe( 221| | _ subscriber: S, transform: ((Subscription) -> Subscription)? 222| | ) where S.StoreSubscriberStateType == SelectedState 223| 15| { 224| 15| let originalSubscription = Subscription() 225| 15| 226| 15| var transformedSubscription = transform?(originalSubscription) 227| 15| if subscriptionsAutomaticallySkipRepeats { 228| 11| transformedSubscription = transformedSubscription?.skipRepeats() 229| 15| } 230| 15| _subscribe(subscriber, originalSubscription: originalSubscription, 231| 15| transformedSubscription: transformedSubscription) 232| 15| } 233| |} 234| | 235| |extension Store where State: Equatable { 236| | open func subscribe(_ subscriber: S) 237| 4| where S.StoreSubscriberStateType == State { 238| 4| guard subscriptionsAutomaticallySkipRepeats else { 239| 1| _ = subscribe(subscriber, transform: nil) 240| 1| return 241| 3| } 242| 3| _ = subscribe(subscriber, transform: { $0.skipRepeats() }) 243| 3| } 244| |} /Users/travis/build/ReSwift/ReSwift/ReSwift/CoreTypes/StoreSubscriber.swift: 1| |// 2| |// StoreSubscriber.swift 3| |// ReSwift 4| |// 5| |// Created by Benjamin Encz on 12/14/15. 6| |// Copyright © 2015 ReSwift Community. All rights reserved. 7| |// 8| | 9| |public protocol AnyStoreSubscriber: AnyObject { 10| | // swiftlint:disable:next identifier_name 11| | func _newState(state: Any) 12| |} 13| | 14| |public protocol StoreSubscriber: AnyStoreSubscriber { 15| | associatedtype StoreSubscriberStateType 16| | 17| | func newState(state: StoreSubscriberStateType) 18| |} 19| | 20| |extension StoreSubscriber { 21| | // swiftlint:disable:next identifier_name 22| 63.0k| public func _newState(state: Any) { 23| 63.0k| if let typedState = state as? StoreSubscriberStateType { 24| 63.0k| newState(state: typedState) 25| 63.0k| } 26| 63.0k| } 27| |} /Users/travis/build/ReSwift/ReSwift/ReSwift/CoreTypes/Subscription.swift: 1| |// 2| |// SubscriberWrapper.swift 3| |// ReSwift 4| |// 5| |// Created by Virgilio Favero Neto on 4/02/2016. 6| |// Copyright © 2016 ReSwift Community. All rights reserved. 7| |// 8| | 9| |/// A box around subscriptions and subscribers. 10| |/// 11| |/// Acts as a type-erasing wrapper around a subscription and its transformed subscription. 12| |/// The transformed subscription has a type argument that matches the selected substate of the 13| |/// subscriber; however that type cannot be exposed to the store. 14| |/// 15| |/// The box subscribes either to the original subscription, or if available to the transformed 16| |/// subscription and passes any values that come through this subscriptions to the subscriber. 17| |class SubscriptionBox: Hashable { 18| | 19| | private let originalSubscription: Subscription 20| | weak var subscriber: AnyStoreSubscriber? 21| | private let objectIdentifier: ObjectIdentifier 22| | 23| | #if swift(>=5.0) 24| | func hash(into hasher: inout Hasher) { 25| | hasher.combine(self.objectIdentifier) 26| | } 27| | #elseif swift(>=4.2) 28| | #if compiler(>=5.0) 29| 39.2k| func hash(into hasher: inout Hasher) { 30| 39.2k| hasher.combine(self.objectIdentifier) 31| 39.2k| } 32| | #else 33| | var hashValue: Int { 34| | return self.objectIdentifier.hashValue 35| | } 36| | #endif 37| | #else 38| | var hashValue: Int { 39| | return self.objectIdentifier.hashValue 40| | } 41| | #endif 42| | 43| | init( 44| | originalSubscription: Subscription, 45| | transformedSubscription: Subscription?, 46| | subscriber: AnyStoreSubscriber 47| 33.0k| ) { 48| 33.0k| self.originalSubscription = originalSubscription 49| 33.0k| self.subscriber = subscriber 50| 33.0k| self.objectIdentifier = ObjectIdentifier(subscriber) 51| 33.0k| 52| 33.0k| // If we received a transformed subscription, we subscribe to that subscription 53| 33.0k| // and forward all new values to the subscriber. 54| 33.0k| if let transformedSubscription = transformedSubscription { 55| 28| transformedSubscription.observer = { [unowned self] _, newState in 56| 28| self.subscriber?._newState(state: newState as Any) 57| 28| } 58| 28| // If we haven't received a transformed subscription, we forward all values 59| 28| // from the original subscription. 60| 33.0k| } else { 61| 33.0k| originalSubscription.observer = { [unowned self] _, newState in 62| 33.0k| self.subscriber?._newState(state: newState as Any) 63| 33.0k| } 64| 33.0k| } 65| 33.0k| } 66| | 67| 30.0k| func newValues(oldState: State, newState: State) { 68| 30.0k| // We pass all new values through the original subscription, which accepts 69| 30.0k| // values of type ``. If present, transformed subscriptions will 70| 30.0k| // receive this update and transform it before passing it on to the subscriber. 71| 30.0k| self.originalSubscription.newValues(oldState: oldState, newState: newState) 72| 30.0k| } 73| | 74| 79.1k| static func == (left: SubscriptionBox, right: SubscriptionBox) -> Bool { 75| 79.1k| return left.objectIdentifier == right.objectIdentifier 76| 79.1k| } 77| |} 78| | 79| |/// Represents a subscription of a subscriber to the store. The subscription determines which new 80| |/// values from the store are forwarded to the subscriber, and how they are transformed. 81| |/// The subscription acts as a very-light weight signal/observable that you might know from 82| |/// reactive programming libraries. 83| |public class Subscription { 84| | 85| | private func _select( 86| | _ selector: @escaping (State) -> Substate 87| | ) -> Subscription 88| 20| { 89| 20| return Subscription { sink in 90| 43| self.observer = { oldState, newState in 91| 43| sink(oldState.map(selector) ?? nil, selector(newState)) 92| 43| } 93| 20| } 94| 20| } 95| | 96| | // MARK: Public Interface 97| | 98| | /// Initializes a subscription with a sink closure. The closure provides a way to send 99| | /// new values over this subscription. 100| 47| public init(sink: @escaping (@escaping (State?, State) -> Void) -> Void) { 101| 47| // Provide the caller with a closure that will forward all values 102| 47| // to observers of this subscription. 103| 80| sink { old, new in 104| 80| self.newValues(oldState: old, newState: new) 105| 80| } 106| 47| } 107| | 108| | /// Provides a subscription that selects a substate of the state of the original subscription. 109| | /// - parameter selector: A closure that maps a state to a selected substate 110| | public func select( 111| | _ selector: @escaping (State) -> Substate 112| | ) -> Subscription 113| 11| { 114| 11| return self._select(selector) 115| 11| } 116| | 117| | /// Provides a subscription that selects a substate of the state of the original subscription. 118| | /// - parameter keyPath: A key path from a state to a substate 119| | public func select( 120| | _ keyPath: KeyPath 121| | ) -> Subscription 122| 9| { 123| 29| return self._select { $0[keyPath: keyPath] } 124| 9| } 125| | 126| | /// Provides a subscription that skips certain state updates of the original subscription. 127| | /// - parameter isRepeat: A closure that determines whether a given state update is a repeat and 128| | /// thus should be skipped and not forwarded to subscribers. 129| | /// - parameter oldState: The store's old state, before the action is reduced. 130| | /// - parameter newState: The store's new state, after the action has been reduced. 131| | public func skipRepeats(_ isRepeat: @escaping (_ oldState: State, _ newState: State) -> Bool) 132| 27| -> Subscription { 133| 27| return Subscription { sink in 134| 56| self.observer = { oldState, newState in 135| 56| switch (oldState, newState) { 136| 56| case let (old?, new): 137| 29| if !isRepeat(old, new) { 138| 10| sink(oldState, newState) 139| 29| } else { 140| 19| return 141| 56| } 142| 56| default: 143| 27| sink(oldState, newState) 144| 56| } 145| 56| } 146| 27| } 147| 27| } 148| | 149| | /// The closure called with changes from the store. 150| | /// This closure can be written to for use in extensions to Subscription similar to `skipRepeats` 151| | public var observer: ((State?, State) -> Void)? 152| | 153| | // MARK: Internals 154| | 155| 33.0k| init() {} 156| | 157| | /// Sends new values over this subscription. Observers will be notified of these new values. 158| 63.1k| func newValues(oldState: State?, newState: State) { 159| 63.1k| self.observer?(oldState, newState) 160| 63.1k| } 161| |} 162| | 163| |extension Subscription where State: Equatable { 164| 16| public func skipRepeats() -> Subscription{ 165| 16| return self.skipRepeats(==) 166| 16| } 167| |} 168| | 169| |/// Subscription skipping convenience methods 170| |extension Subscription { 171| | 172| | /// Provides a subscription that skips certain state updates of the original subscription. 173| | /// 174| | /// This is identical to `skipRepeats` and is provided simply for convenience. 175| | /// - parameter when: A closure that determines whether a given state update is a repeat and 176| | /// thus should be skipped and not forwarded to subscribers. 177| | /// - parameter oldState: The store's old state, before the action is reduced. 178| | /// - parameter newState: The store's new state, after the action has been reduced. 179| 3| public func skip(when: @escaping (_ oldState: State, _ newState: State) -> Bool) -> Subscription { 180| 3| return self.skipRepeats(when) 181| 3| } 182| | 183| | /// Provides a subscription that only updates for certain state changes. 184| | /// 185| | /// This is effectively the inverse of `skip(when:)` / `skipRepeats(:)` 186| | /// - parameter when: A closure that determines whether a given state update should notify 187| | /// - parameter oldState: The store's old state, before the action is reduced. 188| | /// - parameter newState: The store's new state, after the action has been reduced. 189| | /// the subscriber. 190| 3| public func only(when: @escaping (_ oldState: State, _ newState: State) -> Bool) -> Subscription { 191| 4| return self.skipRepeats { oldState, newState in 192| 4| return !when(oldState, newState) 193| 4| } 194| 3| } 195| |} /Users/travis/build/ReSwift/ReSwift/ReSwift/Utils/Assertions.swift: 1| |// 2| |// Assertions 3| |// Copyright © 2015 mohamede1945. All rights reserved. 4| |// https://github.com/mohamede1945/AssertionsTestingExample 5| |// 6| | 7| |import Foundation 8| | 9| |/// drop-in fatalError replacement for testing 10| | 11| |/** 12| | Swift.fatalError wrapper for catching in tests 13| | 14| | - parameter message: Message to be wrapped 15| | - parameter file: Calling file 16| | - parameter line: Calling line 17| | */ 18| 0|func raiseFatalError(_ message: @autoclosure () -> String = "", 19| 1| file: StaticString = #file, line: UInt = #line) -> Never { 20| 1| Assertions.fatalErrorClosure(message(), file, line) 21| 84.4k| repeat { 22| 84.4k| RunLoop.current.run() 23| 84.4k| } while (true) 24| 1|} 25| | 26| |/// Stores custom assertions closures, by default it points to Swift functions. But test target can 27| |/// override them. 28| |class Assertions { 29| | static var fatalErrorClosure = swiftFatalErrorClosure 30| | static let swiftFatalErrorClosure: (String, StaticString, UInt) -> Void 31| | = { Swift.fatalError($0, file: $1, line: $2) } 32| |} /Users/travis/build/ReSwift/ReSwift/ReSwift/Utils/AtomicBool.swift: 1| |// 2| |// AtomicBool.swift 3| |// ReSwift-iOS 4| |// 5| |// Created by 钟武 on 2018/5/2. 6| |// Copyright © 2015 ReSwift Community. All rights reserved. 7| |// 8| | 9| |import Foundation 10| |/** 11| | Struct is only used internally in ReSwift to implements atomic bool operation. 12| | */ 13| |internal struct AtomicBool { 14| | private var flag: UInt8 = 0 15| | internal var value: Bool { 16| 69| get { return flag != 0} 17| 136| set { 18| 136| if newValue { 19| 68| OSAtomicTestAndSet(7, &flag) 20| 136| } else { 21| 68| OSAtomicTestAndClear(7, &flag) 22| 136| } 23| 136| } 24| | } 25| |} /Users/travis/build/ReSwift/ReSwift/ReSwift/Utils/TypeHelper.swift: 1| |// 2| |// TypeHelper.swift 3| |// ReSwift 4| |// 5| |// Created by Benjamin Encz on 11/27/15. 6| |// Copyright © 2015 ReSwift Community. All rights reserved. 7| |// 8| | 9| |/** 10| | Method is only used internally in ReSwift to cast the generic `StateType` to a specific 11| | type expected by reducers / store subscribers. 12| | 13| | - parameter action: An action that will be passed to `handleAction`. 14| | - parameter state: A generic state type that will be casted to `SpecificStateType`. 15| | - parameter function: The `handleAction` method. 16| | - returns: A `StateType` from `handleAction` or the original `StateType` if it cannot be 17| | casted to `SpecificStateType`. 18| | */ 19| |@discardableResult 20| |func withSpecificTypes( 21| | _ action: Action, 22| | state genericStateType: StateType?, 23| | function: (_ action: Action, _ state: SpecificStateType?) -> SpecificStateType 24| 3| ) -> StateType { 25| 3| guard let genericStateType = genericStateType else { 26| 1| return function(action, nil) as! StateType 27| 2| } 28| 2| 29| 2| guard let specificStateType = genericStateType as? SpecificStateType else { 30| 1| return genericStateType 31| 1| } 32| 1| 33| 1| return function(action, specificStateType) as! StateType 34| 2|} <<<<<< EOF # path=ReSwift-macOSTests.xctest.coverage.txt /Users/travis/build/ReSwift/ReSwift/ReSwiftTests/PerformanceTests.swift: 1| |// Copyright © 2019 ReSwift Community. All rights reserved. 2| | 3| |import XCTest 4| |import ReSwift 5| | 6| |final class PerformanceTests: XCTestCase { 7| | struct MockState: StateType {} 8| | struct MockAction: Action {} 9| | 10| 6.00k| let subscribers: [MockSubscriber] = (0..<3000).map { _ in MockSubscriber() } 11| | let store = Store( 12| 10| reducer: { _, state in return state ?? MockState() }, 13| | state: MockState(), 14| | automaticallySkipsRepeats: false 15| | ) 16| | 17| | class MockSubscriber: StoreSubscriber { 18| 63.0k| func newState(state: MockState) { 19| 63.0k| // Do nothing 20| 63.0k| } 21| | } 22| | 23| 1| func testNotify() { 24| 1| self.subscribers.forEach(self.store.subscribe) 25| 10| self.measure { 26| 10| self.store.dispatch(MockAction()) 27| 10| } 28| 1| } 29| | 30| 1| func testSubscribe() { 31| 10| self.measure { 32| 10| self.subscribers.forEach(self.store.subscribe) 33| 10| } 34| 1| } 35| |} /Users/travis/build/ReSwift/ReSwift/ReSwiftTests/StoreDispatchTests.swift: 1| |// 2| |// StoreDispatchTests.swift 3| |// ReSwift 4| |// 5| |// Created by Karl Bowden on 20/07/2016. 6| |// Copyright © 2016 ReSwift Community. All rights reserved. 7| |// 8| | 9| |import XCTest 10| |@testable import ReSwift 11| | 12| |class StoreDispatchTests: XCTestCase { 13| | 14| | typealias TestSubscriber = TestStoreSubscriber 15| | typealias CallbackSubscriber = CallbackStoreSubscriber 16| | 17| | var store: Store! 18| | var reducer: TestReducer! 19| | 20| 4| override func setUp() { 21| 4| super.setUp() 22| 4| reducer = TestReducer() 23| 4| store = Store(reducer: reducer.handleAction, state: TestAppState()) 24| 4| } 25| | 26| | /** 27| | it throws an exception when a reducer dispatches an action 28| | */ 29| 1| func testThrowsExceptionWhenReducersDispatch() { 30| 1| // Expectation lives in the `DispatchingReducer` class 31| 1| let reducer = DispatchingReducer() 32| 1| store = Store(reducer: reducer.handleAction, state: TestAppState()) 33| 1| reducer.store = store 34| 1| store.dispatch(SetValueAction(10)) 35| 1| } 36| | 37| | /** 38| | it accepts action creators 39| | */ 40| | @available(*, deprecated, message: "Deprecated in favor of https://github.com/ReSwift/ReSwift-Thunk") 41| 1| func testAcceptsActionCreators() { 42| 1| store.dispatch(SetValueAction(5)) 43| 1| 44| 1| let doubleValueActionCreator: Store.ActionCreator = { state, store in 45| 1| return SetValueAction(state.testValue! * 2) 46| 1| } 47| 1| 48| 1| store.dispatch(doubleValueActionCreator) 49| 1| 50| 1| XCTAssertEqual(store.state.testValue, 10) 51| 1| } 52| | 53| | /** 54| | it accepts async action creators 55| | */ 56| | @available(*, deprecated, message: "Deprecated in favor of https://github.com/ReSwift/ReSwift-Thunk") 57| 1| func testAcceptsAsyncActionCreators() { 58| 1| 59| 1| let asyncExpectation = futureExpectation( 60| 1| withDescription: "It accepts async action creators") 61| 1| 62| 1| let asyncActionCreator: Store.AsyncActionCreator = { _, _, callback in 63| 1| dispatchAsync { 64| 1| // Provide the callback with an action creator 65| 1| callback { _, _ in 66| 1| return SetValueAction(5) 67| 1| } 68| 1| } 69| 1| } 70| 1| 71| 2| let subscriber = CallbackSubscriber { [unowned self] state in 72| 2| if self.store.state.testValue != nil { 73| 1| XCTAssertEqual(self.store.state.testValue, 5) 74| 1| asyncExpectation.fulfill() 75| 2| } 76| 2| } 77| 1| 78| 1| store.subscribe(subscriber) 79| 1| store.dispatch(asyncActionCreator) 80| 1| waitForFutureExpectations(withTimeout: 1) { error in 81| 1| if let error = error { 82| 0| XCTFail("waitForExpectationsWithTimeout errored: \(error)") 83| 1| } 84| 1| } 85| 1| } 86| | 87| | /** 88| | it calls the callback once state update from async action is complete 89| | */ 90| | @available(*, deprecated, message: "Deprecated in favor of https://github.com/ReSwift/ReSwift-Thunk") 91| 1| func testCallsCalbackOnce() { 92| 1| let asyncExpectation = futureExpectation(withDescription: 93| 1| "It calls the callback once state update from async action is complete") 94| 1| 95| 1| let asyncActionCreator: Store.AsyncActionCreator = { _, _, callback in 96| 1| dispatchAsync { 97| 1| // Provide the callback with an action creator 98| 1| callback { _, _ in 99| 1| return SetValueAction(5) 100| 1| } 101| 1| } 102| 1| } 103| 1| 104| 1| store.dispatch(asyncActionCreator) { newState in 105| 1| XCTAssertEqual(self.store.state.testValue, 5) 106| 1| if newState.testValue == 5 { 107| 1| asyncExpectation.fulfill() 108| 1| } 109| 1| } 110| 1| 111| 1| waitForFutureExpectations(withTimeout: 1) { error in 112| 1| if let error = error { 113| 0| XCTFail("waitForExpectationsWithTimeout errored: \(error)") 114| 1| } 115| 1| } 116| 1| } 117| |} 118| | 119| |// Needs to be class so that shared reference can be modified to inject store 120| |class DispatchingReducer: XCTestCase { 121| | var store: Store? 122| | 123| 1| func handleAction(action: Action, state: TestAppState?) -> TestAppState { 124| 1| expectFatalError { 125| 1| self.store?.dispatch(SetValueAction(20)) 126| 1| } 127| 1| return state ?? TestAppState() 128| 1| } 129| |} /Users/travis/build/ReSwift/ReSwift/ReSwiftTests/StoreMiddlewareTests.swift: 1| |// 2| |// StoreMiddlewareTests.swift 3| |// ReSwift 4| |// 5| |// Created by Benjamin Encz on 12/24/15. 6| |// Copyright © 2015 ReSwift Community. All rights reserved. 7| |// 8| | 9| |import XCTest 10| |@testable import ReSwift 11| | 12| 2|let firstMiddleware: Middleware = { dispatch, getState in 13| 2| return { next in 14| 3| return { action in 15| 3| 16| 3| if var action = action as? SetValueStringAction { 17| 2| action.value += " First Middleware" 18| 2| next(action) 19| 3| } else { 20| 1| next(action) 21| 3| } 22| 3| } 23| 2| } 24| 2|} 25| | 26| 2|let secondMiddleware: Middleware = { dispatch, getState in 27| 2| return { next in 28| 3| return { action in 29| 3| 30| 3| if var action = action as? SetValueStringAction { 31| 2| action.value += " Second Middleware" 32| 2| next(action) 33| 3| } else { 34| 1| next(action) 35| 3| } 36| 3| } 37| 2| } 38| 2|} 39| | 40| 1|let dispatchingMiddleware: Middleware = { dispatch, getState in 41| 1| return { next in 42| 2| return { action in 43| 2| 44| 2| if var action = action as? SetValueAction { 45| 1| dispatch(SetValueStringAction("\(action.value ?? 0)")) 46| 2| } 47| 2| 48| 2| next(action) 49| 2| } 50| 1| } 51| 1|} 52| | 53| 1|let stateAccessingMiddleware: Middleware = { dispatch, getState in 54| 1| return { next in 55| 2| return { action in 56| 2| 57| 2| let appState = getState(), 58| 2| stringAction = action as? SetValueStringAction 59| 2| 60| 2| // avoid endless recursion by checking if we've dispatched exactly this action 61| 2| if appState?.testValue == "OK" && stringAction?.value != "Not OK" { 62| 1| // dispatch a new action 63| 1| dispatch(SetValueStringAction("Not OK")) 64| 1| 65| 1| // and swallow the current one 66| 1| next(NoOpAction()) 67| 2| } else { 68| 1| next(action) 69| 2| } 70| 2| } 71| 1| } 72| 1|} 73| | 74| 1|func middleware(executing block: @escaping () -> Void) -> Middleware { 75| 1| return { dispatch, getState in 76| 1| return { next in 77| 1| return { action in 78| 1| block() 79| 1| } 80| 1| } 81| 1| } 82| 1|} 83| | 84| |class StoreMiddlewareTests: XCTestCase { 85| | 86| | /** 87| | it can decorate dispatch function 88| | */ 89| 1| func testDecorateDispatch() { 90| 1| let reducer = TestValueStringReducer() 91| 1| // Swift 4.1 fails to cast this from Middleware to Middleware 92| 1| // as expected during runtime, see: 93| 1| let middleware: [Middleware] = [ 94| 1| firstMiddleware, 95| 1| secondMiddleware 96| 1| ] 97| 1| let store = Store(reducer: reducer.handleAction, 98| 1| state: TestStringAppState(), 99| 1| middleware: middleware) 100| 1| 101| 1| let subscriber = TestStoreSubscriber() 102| 1| store.subscribe(subscriber) 103| 1| 104| 1| let action = SetValueStringAction("OK") 105| 1| store.dispatch(action) 106| 1| 107| 1| XCTAssertEqual(store.state.testValue, "OK First Middleware Second Middleware") 108| 1| } 109| | 110| | /** 111| | it can dispatch actions 112| | */ 113| 1| func testCanDispatch() { 114| 1| let reducer = TestValueStringReducer() 115| 1| // Swift 4.1 fails to cast this from Middleware to Middleware 116| 1| // as expected during runtime, see: 117| 1| let middleware: [Middleware] = [ 118| 1| firstMiddleware, 119| 1| secondMiddleware, 120| 1| dispatchingMiddleware 121| 1| ] 122| 1| let store = Store(reducer: reducer.handleAction, 123| 1| state: TestStringAppState(), 124| 1| middleware: middleware) 125| 1| 126| 1| let subscriber = TestStoreSubscriber() 127| 1| store.subscribe(subscriber) 128| 1| 129| 1| let action = SetValueAction(10) 130| 1| store.dispatch(action) 131| 1| 132| 1| XCTAssertEqual(store.state.testValue, "10 First Middleware Second Middleware") 133| 1| } 134| | 135| | /** 136| | it middleware can access the store's state 137| | */ 138| 1| func testMiddlewareCanAccessState() { 139| 1| let reducer = TestValueStringReducer() 140| 1| var state = TestStringAppState() 141| 1| state.testValue = "OK" 142| 1| 143| 1| let store = Store(reducer: reducer.handleAction, state: state, 144| 1| middleware: [stateAccessingMiddleware]) 145| 1| 146| 1| store.dispatch(SetValueStringAction("Action That Won't Go Through")) 147| 1| 148| 1| XCTAssertEqual(store.state.testValue, "Not OK") 149| 1| } 150| | 151| 1| func testCanMutateMiddlewareAfterInit() { 152| 1| 153| 1| let reducer = TestValueStringReducer() 154| 1| let state = TestStringAppState() 155| 1| let store = Store(reducer: reducer.handleAction, state: state, 156| 1| middleware: []) 157| 1| 158| 1| // Adding 159| 1| var added = false 160| 1| store.middleware.append(middleware(executing: { added = true })) 161| 1| store.dispatch(SetValueStringAction("")) 162| 1| XCTAssertTrue(added) 163| 1| 164| 1| // Removing 165| 1| added = false 166| 1| store.middleware = [] 167| 1| store.dispatch(SetValueStringAction("")) 168| 1| XCTAssertFalse(added) 169| 1| } 170| |} /Users/travis/build/ReSwift/ReSwift/ReSwiftTests/StoreSubscriberTests.swift: 1| |// 2| |// StoreSubscriberTests.swift 3| |// ReSwift 4| |// 5| |// Created by Benjamin Encz on 1/23/16. 6| |// Copyright © 2016 ReSwift Community. All rights reserved. 7| |// 8| | 9| |import XCTest 10| |import ReSwift 11| | 12| |class StoreSubscriberTests: XCTestCase { 13| | 14| | /** 15| | it allows to pass a state selector closure 16| | */ 17| 1| func testAllowsSelectorClosure() { 18| 1| let reducer = TestReducer() 19| 1| let store = Store(reducer: reducer.handleAction, state: TestAppState()) 20| 1| let subscriber = TestFilteredSubscriber() 21| 1| 22| 1| store.subscribe(subscriber) { 23| 5| $0.select { $0.testValue } 24| 1| } 25| 1| 26| 1| store.dispatch(SetValueAction(3)) 27| 1| 28| 1| XCTAssertEqual(subscriber.receivedValue, 3) 29| 1| 30| 1| store.dispatch(SetValueAction(nil)) 31| 1| 32| 1| XCTAssertEqual(subscriber.receivedValue, .some(.none)) 33| 1| } 34| | 35| | /** 36| | it allows to pass a state selector key path 37| | */ 38| 1| func testAllowsSelectorKeyPath() { 39| 1| let reducer = TestReducer() 40| 1| let store = Store(reducer: reducer.handleAction, state: TestAppState()) 41| 1| let subscriber = TestFilteredSubscriber() 42| 1| 43| 1| store.subscribe(subscriber) { 44| 1| $0.select(\.testValue) 45| 1| } 46| 1| 47| 1| store.dispatch(SetValueAction(3)) 48| 1| 49| 1| XCTAssertEqual(subscriber.receivedValue, 3) 50| 1| 51| 1| store.dispatch(SetValueAction(nil)) 52| 1| 53| 1| #if swift(>=4.1) 54| 1| XCTAssertEqual(subscriber.receivedValue, .some(.none)) 55| 1| #else 56| 1| XCTAssertEqual(subscriber.receivedValue, nil) 57| 1| #endif 58| 1| } 59| | 60| | /** 61| | it supports complex state selector closures 62| | */ 63| 1| func testComplexStateSelector() { 64| 1| let reducer = TestComplexAppStateReducer() 65| 1| let store = Store(reducer: reducer.handleAction, state: TestComplexAppState()) 66| 1| let subscriber = TestSelectiveSubscriber() 67| 1| 68| 1| store.subscribe(subscriber) { 69| 5| $0.select { 70| 5| ($0.testValue, $0.otherState?.name) 71| 5| } 72| 1| } 73| 1| store.dispatch(SetValueAction(5)) 74| 1| store.dispatch(SetOtherStateAction( 75| 1| otherState: OtherState(name: "TestName", age: 99) 76| 1| )) 77| 1| 78| 1| XCTAssertEqual(subscriber.receivedValue.0, 5) 79| 1| XCTAssertEqual(subscriber.receivedValue.1, "TestName") 80| 1| } 81| | 82| | /** 83| | it does not notify subscriber for unchanged substate state when using `skipRepeats`. 84| | */ 85| 1| func testUnchangedStateWithRegularSubstateSelection() { 86| 1| let reducer = TestReducer() 87| 1| var state = TestAppState() 88| 1| state.testValue = 3 89| 1| let store = Store(reducer: reducer.handleAction, state: state) 90| 1| let subscriber = TestFilteredSubscriber() 91| 1| 92| 1| store.subscribe(subscriber) { 93| 1| $0 94| 3| .select { $0.testValue } 95| 1| .skipRepeats { $0 == $1 } 96| 1| } 97| 1| 98| 1| XCTAssertEqual(subscriber.receivedValue, 3) 99| 1| 100| 1| store.dispatch(SetValueAction(3)) 101| 1| 102| 1| XCTAssertEqual(subscriber.receivedValue, 3) 103| 1| XCTAssertEqual(subscriber.newStateCallCount, 1) 104| 1| } 105| | 106| 1| func testUnchangedStateWithKeyPath() { 107| 1| let reducer = TestReducer() 108| 1| var state = TestAppState() 109| 1| state.testValue = 3 110| 1| let store = Store(reducer: reducer.handleAction, state: state) 111| 1| let subscriber = TestFilteredSubscriber() 112| 1| 113| 1| store.subscribe(subscriber) { 114| 1| $0 115| 1| .select(\.testValue) 116| 1| .skipRepeats { $0 == $1 } 117| 1| } 118| 1| 119| 1| XCTAssertEqual(subscriber.receivedValue, 3) 120| 1| 121| 1| store.dispatch(SetValueAction(3)) 122| 1| 123| 1| XCTAssertEqual(subscriber.receivedValue, 3) 124| 1| XCTAssertEqual(subscriber.newStateCallCount, 1) 125| 1| } 126| | 127| | /** 128| | it does not notify subscriber for unchanged substate state when using the default 129| | `skipRepeats` implementation. 130| | */ 131| 1| func testUnchangedStateDefaultSkipRepeatsWithRegularSubstateSelection() { 132| 1| let reducer = TestValueStringReducer() 133| 1| let state = TestStringAppState() 134| 1| let store = Store(reducer: reducer.handleAction, state: state) 135| 1| let subscriber = TestFilteredSubscriber() 136| 1| 137| 1| store.subscribe(subscriber) { 138| 1| $0 139| 3| .select { $0.testValue } 140| 1| .skipRepeats() 141| 1| } 142| 1| 143| 1| XCTAssertEqual(subscriber.receivedValue, "Initial") 144| 1| 145| 1| store.dispatch(SetValueStringAction("Initial")) 146| 1| 147| 1| XCTAssertEqual(subscriber.receivedValue, "Initial") 148| 1| XCTAssertEqual(subscriber.newStateCallCount, 1) 149| 1| } 150| | 151| 1| func testUnchangedStateDefaultSkipRepeatsWithKeyPath() { 152| 1| let reducer = TestValueStringReducer() 153| 1| let state = TestStringAppState() 154| 1| let store = Store(reducer: reducer.handleAction, state: state) 155| 1| let subscriber = TestFilteredSubscriber() 156| 1| 157| 1| store.subscribe(subscriber) { 158| 1| $0 159| 1| .select(\.testValue) 160| 1| .skipRepeats() 161| 1| } 162| 1| 163| 1| XCTAssertEqual(subscriber.receivedValue, "Initial") 164| 1| 165| 1| store.dispatch(SetValueStringAction("Initial")) 166| 1| 167| 1| XCTAssertEqual(subscriber.receivedValue, "Initial") 168| 1| XCTAssertEqual(subscriber.newStateCallCount, 1) 169| 1| } 170| | 171| | /** 172| | it skips repeated state values by when `skipRepeats` returns `true`. 173| | */ 174| 1| func testSkipsStateUpdatesForCustomEqualityChecksWithRegularSubstateSelection() { 175| 1| let reducer = TestCustomAppStateReducer() 176| 1| let state = TestCustomAppState(substateValue: 5) 177| 1| let store = Store(reducer: reducer.handleAction, state: state) 178| 1| let subscriber = TestFilteredSubscriber() 179| 1| 180| 1| store.subscribe(subscriber) { 181| 1| $0 182| 3| .select { $0.substate } 183| 1| .skipRepeats { $0.value == $1.value } 184| 1| } 185| 1| 186| 1| XCTAssertEqual(subscriber.receivedValue.value, 5) 187| 1| 188| 1| store.dispatch(SetCustomSubstateAction(5)) 189| 1| 190| 1| XCTAssertEqual(subscriber.receivedValue.value, 5) 191| 1| XCTAssertEqual(subscriber.newStateCallCount, 1) 192| 1| } 193| | 194| 1| func testSkipsStateUpdatesForCustomEqualityChecksWithKeyPath() { 195| 1| let reducer = TestCustomAppStateReducer() 196| 1| let state = TestCustomAppState(substateValue: 5) 197| 1| let store = Store(reducer: reducer.handleAction, state: state) 198| 1| let subscriber = TestFilteredSubscriber() 199| 1| 200| 1| store.subscribe(subscriber) { 201| 1| $0 202| 1| .select(\.substate) 203| 1| .skipRepeats { $0.value == $1.value } 204| 1| } 205| 1| 206| 1| XCTAssertEqual(subscriber.receivedValue.value, 5) 207| 1| 208| 1| store.dispatch(SetCustomSubstateAction(5)) 209| 1| 210| 1| XCTAssertEqual(subscriber.receivedValue.value, 5) 211| 1| XCTAssertEqual(subscriber.newStateCallCount, 1) 212| 1| } 213| | 214| 1| func testPassesOnDuplicateSubstateUpdatesByDefaultWithRegularSubstateSelection() { 215| 1| let reducer = TestNonEquatableReducer() 216| 1| let state = TestNonEquatable() 217| 1| let store = Store(reducer: reducer.handleAction, state: state) 218| 1| let subscriber = TestFilteredSubscriber() 219| 1| 220| 1| store.subscribe(subscriber) { 221| 3| $0.select { $0.testValue } 222| 1| } 223| 1| 224| 1| XCTAssertEqual(subscriber.receivedValue.testValue, "Initial") 225| 1| 226| 1| store.dispatch(SetNonEquatableAction(NonEquatable())) 227| 1| 228| 1| XCTAssertEqual(subscriber.receivedValue.testValue, "Initial") 229| 1| XCTAssertEqual(subscriber.newStateCallCount, 2) 230| 1| } 231| | 232| 1| func testPassesOnDuplicateSubstateUpdatesByDefaultWithKeyPath() { 233| 1| let reducer = TestNonEquatableReducer() 234| 1| let state = TestNonEquatable() 235| 1| let store = Store(reducer: reducer.handleAction, state: state) 236| 1| let subscriber = TestFilteredSubscriber() 237| 1| 238| 1| store.subscribe(subscriber) { 239| 1| $0.select(\.testValue) 240| 1| } 241| 1| 242| 1| XCTAssertEqual(subscriber.receivedValue.testValue, "Initial") 243| 1| 244| 1| store.dispatch(SetNonEquatableAction(NonEquatable())) 245| 1| 246| 1| XCTAssertEqual(subscriber.receivedValue.testValue, "Initial") 247| 1| XCTAssertEqual(subscriber.newStateCallCount, 2) 248| 1| } 249| | 250| 1| func testPassesOnDuplicateSubstateWhenSkipsFalseWithRegularSubstateSelection() { 251| 1| let reducer = TestValueStringReducer() 252| 1| let state = TestStringAppState() 253| 1| let store = Store(reducer: reducer.handleAction, state: state, middleware: [], automaticallySkipsRepeats: false) 254| 1| let subscriber = TestFilteredSubscriber() 255| 1| 256| 1| store.subscribe(subscriber) { 257| 3| $0.select { $0.testValue } 258| 1| } 259| 1| 260| 1| XCTAssertEqual(subscriber.receivedValue, "Initial") 261| 1| 262| 1| store.dispatch(SetValueStringAction("Initial")) 263| 1| 264| 1| XCTAssertEqual(subscriber.receivedValue, "Initial") 265| 1| XCTAssertEqual(subscriber.newStateCallCount, 2) 266| 1| } 267| | 268| 1| func testPassesOnDuplicateSubstateWhenSkipsFalseWithKeyPath() { 269| 1| let reducer = TestValueStringReducer() 270| 1| let state = TestStringAppState() 271| 1| let store = Store(reducer: reducer.handleAction, state: state, middleware: [], automaticallySkipsRepeats: false) 272| 1| let subscriber = TestFilteredSubscriber() 273| 1| 274| 1| store.subscribe(subscriber) { 275| 1| $0.select(\.testValue) 276| 1| } 277| 1| 278| 1| XCTAssertEqual(subscriber.receivedValue, "Initial") 279| 1| 280| 1| store.dispatch(SetValueStringAction("Initial")) 281| 1| 282| 1| XCTAssertEqual(subscriber.receivedValue, "Initial") 283| 1| XCTAssertEqual(subscriber.newStateCallCount, 2) 284| 1| } 285| | 286| 1| func testSkipsStateUpdatesForEquatableStateByDefault() { 287| 1| let reducer = TestValueStringReducer() 288| 1| let state = TestStringAppState() 289| 1| let store = Store(reducer: reducer.handleAction, state: state, middleware: []) 290| 1| let subscriber = TestFilteredSubscriber() 291| 1| 292| 1| store.subscribe(subscriber) 293| 1| 294| 1| XCTAssertEqual(subscriber.receivedValue.testValue, "Initial") 295| 1| 296| 1| store.dispatch(SetValueStringAction("Initial")) 297| 1| 298| 1| XCTAssertEqual(subscriber.receivedValue.testValue, "Initial") 299| 1| XCTAssertEqual(subscriber.newStateCallCount, 1) 300| 1| } 301| | 302| 1| func testSkipsStateUpdatesForEquatableSubStateByDefaultWithRegularSubstateSelection() { 303| 1| let reducer = TestNonEquatableReducer() 304| 1| let state = TestNonEquatable() 305| 1| let store = Store(reducer: reducer.handleAction, state: state) 306| 1| let subscriber = TestFilteredSubscriber() 307| 1| 308| 1| store.subscribe(subscriber) { 309| 3| $0.select { $0.testValue.testValue } 310| 1| } 311| 1| 312| 1| XCTAssertEqual(subscriber.receivedValue, "Initial") 313| 1| 314| 1| store.dispatch(SetValueStringAction("Initial")) 315| 1| 316| 1| XCTAssertEqual(subscriber.receivedValue, "Initial") 317| 1| XCTAssertEqual(subscriber.newStateCallCount, 1) 318| 1| } 319| | 320| 1| func testSkipsStateUpdatesForEquatableSubStateByDefaultWithKeyPath() { 321| 1| let reducer = TestNonEquatableReducer() 322| 1| let state = TestNonEquatable() 323| 1| let store = Store(reducer: reducer.handleAction, state: state) 324| 1| let subscriber = TestFilteredSubscriber() 325| 1| 326| 1| store.subscribe(subscriber) { 327| 1| $0.select(\.testValue.testValue) 328| 1| } 329| 1| 330| 1| XCTAssertEqual(subscriber.receivedValue, "Initial") 331| 1| 332| 1| store.dispatch(SetValueStringAction("Initial")) 333| 1| 334| 1| XCTAssertEqual(subscriber.receivedValue, "Initial") 335| 1| XCTAssertEqual(subscriber.newStateCallCount, 1) 336| 1| } 337| | 338| 1| func testPassesOnDuplicateStateUpdatesInCustomizedStore() { 339| 1| let reducer = TestValueStringReducer() 340| 1| let state = TestStringAppState() 341| 1| let store = Store(reducer: reducer.handleAction, state: state, middleware: [], automaticallySkipsRepeats: false) 342| 1| let subscriber = TestFilteredSubscriber() 343| 1| 344| 1| store.subscribe(subscriber) 345| 1| 346| 1| XCTAssertEqual(subscriber.receivedValue.testValue, "Initial") 347| 1| 348| 1| store.dispatch(SetValueStringAction("Initial")) 349| 1| 350| 1| XCTAssertEqual(subscriber.receivedValue.testValue, "Initial") 351| 1| XCTAssertEqual(subscriber.newStateCallCount, 2) 352| 1| } 353| | 354| 1| func testSkipWhenWithRegularSubstateSelection() { 355| 1| let reducer = TestCustomAppStateReducer() 356| 1| let state = TestCustomAppState(substateValue: 5) 357| 1| let store = Store(reducer: reducer.handleAction, state: state) 358| 1| let subscriber = TestFilteredSubscriber() 359| 1| 360| 1| store.subscribe(subscriber) { 361| 1| $0 362| 3| .select { $0.substate } 363| 1| .skip { $0.value == $1.value } 364| 1| } 365| 1| 366| 1| XCTAssertEqual(subscriber.receivedValue.value, 5) 367| 1| 368| 1| store.dispatch(SetCustomSubstateAction(5)) 369| 1| 370| 1| XCTAssertEqual(subscriber.receivedValue.value, 5) 371| 1| XCTAssertEqual(subscriber.newStateCallCount, 1) 372| 1| } 373| | 374| 1| func testSkipWhenWithKeyPath() { 375| 1| let reducer = TestCustomAppStateReducer() 376| 1| let state = TestCustomAppState(substateValue: 5) 377| 1| let store = Store(reducer: reducer.handleAction, state: state) 378| 1| let subscriber = TestFilteredSubscriber() 379| 1| 380| 1| store.subscribe(subscriber) { 381| 1| $0 382| 1| .select(\.substate) 383| 1| .skip { $0.value == $1.value } 384| 1| } 385| 1| 386| 1| XCTAssertEqual(subscriber.receivedValue.value, 5) 387| 1| 388| 1| store.dispatch(SetCustomSubstateAction(5)) 389| 1| 390| 1| XCTAssertEqual(subscriber.receivedValue.value, 5) 391| 1| XCTAssertEqual(subscriber.newStateCallCount, 1) 392| 1| } 393| | 394| 1| func testOnlyWhenWithRegularSubstateSelection() { 395| 1| let reducer = TestCustomAppStateReducer() 396| 1| let state = TestCustomAppState(substateValue: 5) 397| 1| let store = Store(reducer: reducer.handleAction, state: state) 398| 1| let subscriber = TestFilteredSubscriber() 399| 1| 400| 1| store.subscribe(subscriber) { 401| 1| $0 402| 3| .select { $0.substate } 403| 1| .only { $0.value != $1.value } 404| 1| } 405| 1| 406| 1| XCTAssertEqual(subscriber.receivedValue.value, 5) 407| 1| 408| 1| store.dispatch(SetCustomSubstateAction(5)) 409| 1| 410| 1| XCTAssertEqual(subscriber.receivedValue.value, 5) 411| 1| XCTAssertEqual(subscriber.newStateCallCount, 1) 412| 1| } 413| | 414| 1| func testOnlyWhenWithKeyPath() { 415| 1| let reducer = TestCustomAppStateReducer() 416| 1| let state = TestCustomAppState(substateValue: 5) 417| 1| let store = Store(reducer: reducer.handleAction, state: state) 418| 1| let subscriber = TestFilteredSubscriber() 419| 1| 420| 1| store.subscribe(subscriber) { 421| 1| $0 422| 1| .select(\.substate) 423| 1| .only { $0.value != $1.value } 424| 1| } 425| 1| 426| 1| XCTAssertEqual(subscriber.receivedValue.value, 5) 427| 1| 428| 1| store.dispatch(SetCustomSubstateAction(5)) 429| 1| 430| 1| XCTAssertEqual(subscriber.receivedValue.value, 5) 431| 1| XCTAssertEqual(subscriber.newStateCallCount, 1) 432| 1| } 433| |} 434| | 435| |class TestFilteredSubscriber: StoreSubscriber { 436| | var receivedValue: T! 437| | var newStateCallCount = 0 438| | 439| 29| func newState(state: T) { 440| 29| receivedValue = state 441| 29| newStateCallCount += 1 442| 29| } 443| | 444| |} 445| | 446| |/** 447| | Example of how you can select a substate. The return value from 448| | `selectSubstate` and the argument for `newState` need to match up. 449| | */ 450| |class TestSelectiveSubscriber: StoreSubscriber { 451| | var receivedValue: (Int?, String?) 452| | 453| 3| func newState(state: (Int?, String?)) { 454| 3| receivedValue = state 455| 3| } 456| |} 457| | 458| |struct TestComplexAppState: StateType { 459| | var testValue: Int? 460| | var otherState: OtherState? 461| |} 462| | 463| |struct OtherState { 464| | var name: String? 465| | var age: Int? 466| |} 467| | 468| |struct TestComplexAppStateReducer { 469| 2| func handleAction(action: Action, state: TestComplexAppState?) -> TestComplexAppState { 470| 2| var state = state ?? TestComplexAppState() 471| 2| 472| 2| switch action { 473| 2| case let action as SetValueAction: 474| 1| state.testValue = action.value 475| 1| return state 476| 2| case let action as SetOtherStateAction: 477| 1| state.otherState = action.otherState 478| 2| default: 479| 0| break 480| 2| } 481| 2| 482| 2| return state 483| 2| } 484| |} 485| | 486| |struct SetOtherStateAction: Action { 487| | var otherState: OtherState 488| |} /Users/travis/build/ReSwift/ReSwift/ReSwiftTests/StoreSubscriptionTests.swift: 1| |// 2| |// StoreSubscriptionTests.swift 3| |// ReSwift 4| |// 5| |// Created by Benjamin Encz on 11/27/15. 6| |// Copyright © 2015 ReSwift Community. All rights reserved. 7| |// 8| | 9| |import XCTest 10| |/** 11| | @testable import for testing of `Store.subscriptions` 12| | */ 13| |@testable import ReSwift 14| | 15| |class StoreSubscriptionTests: XCTestCase { 16| | 17| | typealias TestSubscriber = TestStoreSubscriber 18| | 19| | var store: Store! 20| | var reducer: TestReducer! 21| | 22| 11| override func setUp() { 23| 11| super.setUp() 24| 11| reducer = TestReducer() 25| 11| store = Store(reducer: reducer.handleAction, state: TestAppState()) 26| 11| } 27| | 28| | /** 29| | It does not strongly capture an observer 30| | */ 31| 1| func testDoesNotCaptureStrongly() { 32| 1| store = Store(reducer: reducer.handleAction, state: TestAppState()) 33| 1| var subscriber: TestSubscriber? = TestSubscriber() 34| 1| 35| 1| store.subscribe(subscriber!) 36| 1| XCTAssertEqual(store.subscriptions.compactMap({ $0.subscriber }).count, 1) 37| 1| 38| 1| subscriber = nil 39| 1| XCTAssertEqual(store.subscriptions.compactMap({ $0.subscriber }).count, 0) 40| 1| } 41| | 42| | /** 43| | it removes deferenced subscribers before notifying state changes 44| | */ 45| 1| func testRemoveSubscribers() { 46| 1| store = Store(reducer: reducer.handleAction, state: TestAppState()) 47| 1| var subscriber1: TestSubscriber? = TestSubscriber() 48| 1| var subscriber2: TestSubscriber? = TestSubscriber() 49| 1| 50| 1| store.subscribe(subscriber1!) 51| 1| store.subscribe(subscriber2!) 52| 1| store.dispatch(SetValueAction(3)) 53| 1| XCTAssertEqual(store.subscriptions.count, 2) 54| 1| XCTAssertEqual(subscriber1?.receivedStates.last?.testValue, 3) 55| 1| XCTAssertEqual(subscriber2?.receivedStates.last?.testValue, 3) 56| 1| 57| 1| subscriber1 = nil 58| 1| store.dispatch(SetValueAction(5)) 59| 1| XCTAssertEqual(store.subscriptions.count, 1) 60| 1| XCTAssertEqual(subscriber2?.receivedStates.last?.testValue, 5) 61| 1| 62| 1| subscriber2 = nil 63| 1| store.dispatch(SetValueAction(8)) 64| 1| XCTAssertEqual(store.subscriptions.count, 0) 65| 1| } 66| | 67| | /** 68| | it replaces the subscription of an existing subscriber with the new one. 69| | */ 70| 1| func testDuplicateSubscription() { 71| 1| store = Store(reducer: reducer.handleAction, state: TestAppState()) 72| 1| let subscriber = TestSubscriber() 73| 1| 74| 1| // Initial subscription. 75| 1| store.subscribe(subscriber) 76| 1| // Subsequent subscription that skips repeated updates. 77| 4| store.subscribe(subscriber) { $0.skipRepeats { $0.testValue == $1.testValue } } 78| 1| 79| 1| // One initial state update for every subscription. 80| 1| XCTAssertEqual(subscriber.receivedStates.count, 2) 81| 1| 82| 1| store.dispatch(SetValueAction(3)) 83| 1| store.dispatch(SetValueAction(3)) 84| 1| store.dispatch(SetValueAction(3)) 85| 1| store.dispatch(SetValueAction(3)) 86| 1| 87| 1| // Only a single further state update, since latest subscription skips repeated values. 88| 1| XCTAssertEqual(subscriber.receivedStates.count, 3) 89| 1| } 90| | /** 91| | it dispatches initial value upon subscription 92| | */ 93| 1| func testDispatchInitialValue() { 94| 1| store = Store(reducer: reducer.handleAction, state: TestAppState()) 95| 1| let subscriber = TestSubscriber() 96| 1| 97| 1| store.subscribe(subscriber) 98| 1| store.dispatch(SetValueAction(3)) 99| 1| 100| 1| XCTAssertEqual(subscriber.receivedStates.last?.testValue, 3) 101| 1| } 102| | 103| | /** 104| | it allows dispatching from within an observer 105| | */ 106| 1| func testAllowDispatchWithinObserver() { 107| 1| store = Store(reducer: reducer.handleAction, state: TestAppState()) 108| 1| let subscriber = DispatchingSubscriber(store: store) 109| 1| 110| 1| store.subscribe(subscriber) 111| 1| store.dispatch(SetValueAction(2)) 112| 1| 113| 1| XCTAssertEqual(store.state.testValue, 5) 114| 1| } 115| | 116| | /** 117| | it does not dispatch value after subscriber unsubscribes 118| | */ 119| 1| func testDontDispatchToUnsubscribers() { 120| 1| store = Store(reducer: reducer.handleAction, state: TestAppState()) 121| 1| let subscriber = TestSubscriber() 122| 1| 123| 1| store.dispatch(SetValueAction(5)) 124| 1| store.subscribe(subscriber) 125| 1| store.dispatch(SetValueAction(10)) 126| 1| 127| 1| store.unsubscribe(subscriber) 128| 1| // Following value is missed due to not being subscribed: 129| 1| store.dispatch(SetValueAction(15)) 130| 1| store.dispatch(SetValueAction(25)) 131| 1| 132| 1| store.subscribe(subscriber) 133| 1| 134| 1| store.dispatch(SetValueAction(20)) 135| 1| 136| 1| XCTAssertEqual(subscriber.receivedStates.count, 4) 137| 1| XCTAssertEqual(subscriber.receivedStates[subscriber.receivedStates.count - 4].testValue, 5) 138| 1| XCTAssertEqual(subscriber.receivedStates[subscriber.receivedStates.count - 3].testValue, 10) 139| 1| XCTAssertEqual(subscriber.receivedStates[subscriber.receivedStates.count - 2].testValue, 25) 140| 1| XCTAssertEqual(subscriber.receivedStates[subscriber.receivedStates.count - 1].testValue, 20) 141| 1| } 142| | 143| | /** 144| | it ignores identical subscribers 145| | */ 146| 1| func testIgnoreIdenticalSubscribers() { 147| 1| store = Store(reducer: reducer.handleAction, state: TestAppState()) 148| 1| let subscriber = TestSubscriber() 149| 1| 150| 1| store.subscribe(subscriber) 151| 1| store.subscribe(subscriber) 152| 1| 153| 1| XCTAssertEqual(store.subscriptions.count, 1) 154| 1| } 155| | 156| | /** 157| | it ignores identical subscribers that provide substate selectors 158| | */ 159| 1| func testIgnoreIdenticalSubstateSubscribers() { 160| 1| store = Store(reducer: reducer.handleAction, state: TestAppState()) 161| 1| let subscriber = TestSubscriber() 162| 1| 163| 1| store.subscribe(subscriber) { $0 } 164| 1| store.subscribe(subscriber) { $0 } 165| 1| 166| 1| XCTAssertEqual(store.subscriptions.count, 1) 167| 1| } 168| | 169| 1| func testNewStateModifyingSubscriptionsDoesNotDiscardNewSubscription() { 170| 1| // This was built as a failing test due to a bug introduced by #325 171| 1| // The bug occured by adding a subscriber during `newState` 172| 1| // The bug was caused by creating a copy of `subscriptions` before calling 173| 1| // `newState`, and then assigning that copy back to `subscriptions`, losing 174| 1| // the mutation that occured during `newState` 175| 1| 176| 1| store = Store(reducer: reducer.handleAction, state: TestAppState()) 177| 1| 178| 1| let subscriber2 = BlockSubscriber { _ in 179| 1| self.store.dispatch(SetValueAction(2)) 180| 1| } 181| 1| 182| 2| let subscriber1 = BlockSubscriber { [unowned self] state in 183| 2| if state.testValue == 1 { 184| 1| self.store.subscribe(subscriber2) { 185| 1| $0.skip(when: { _, _ in return true }) 186| 1| } 187| 2| } 188| 2| } 189| 1| 190| 1| store.subscribe(subscriber1) { 191| 2| $0.only(when: { _, new in new.testValue.map { $0 == 1 } ?? false }) 192| 1| } 193| 1| 194| 1| store.dispatch(SetValueAction(1)) 195| 1| 196| 1| XCTAssertTrue(store.subscriptions.contains(where: { 197| 1| guard let subscriber = $0.subscriber else { 198| 0| XCTFail("expecting non-nil subscriber") 199| 0| return false 200| 1| } 201| 1| return subscriber === subscriber1 202| 1| })) 203| 2| XCTAssertTrue(store.subscriptions.contains(where: { 204| 2| guard let subscriber = $0.subscriber else { 205| 0| XCTFail("expecting non-nil subscriber") 206| 0| return false 207| 2| } 208| 2| return subscriber === subscriber2 209| 2| })) 210| 1| 211| 1| // Have a subscriber (#1) 212| 1| // #1 adds sub #2 in newState 213| 1| // #1 dispatches in newState 214| 1| // Test that store.subscribers == [#1, #2] // this should fail 215| 1| } 216| |} 217| | 218| |// MARK: Retain Cycle Detection 219| | 220| |private struct TracerAction: Action { } 221| | 222| |private class TestSubscriptionBox: SubscriptionBox { 223| | override init( 224| | originalSubscription: Subscription, 225| | transformedSubscription: Subscription?, 226| | subscriber: AnyStoreSubscriber 227| 2| ) { 228| 2| super.init(originalSubscription: originalSubscription, 229| 2| transformedSubscription: transformedSubscription, 230| 2| subscriber: subscriber) 231| 2| } 232| | 233| | var didDeinit: (() -> Void)? 234| 2| deinit { 235| 2| didDeinit?() 236| 2| } 237| |} 238| | 239| |private class TestStore: Store { 240| | override func subscriptionBox( 241| | originalSubscription: Subscription, 242| | transformedSubscription: Subscription?, 243| 2| subscriber: AnyStoreSubscriber) -> SubscriptionBox { 244| 2| return TestSubscriptionBox( 245| 2| originalSubscription: originalSubscription, 246| 2| transformedSubscription: transformedSubscription, 247| 2| subscriber: subscriber 248| 2| ) 249| 2| } 250| |} 251| | 252| |extension StoreSubscriptionTests { 253| | 254| 1| func testRetainCycle_OriginalSubscription() { 255| 1| 256| 1| var didDeinit = false 257| 1| 258| 1| autoreleasepool { 259| 1| 260| 1| store = TestStore(reducer: reducer.handleAction, state: TestAppState()) 261| 1| let subscriber: TestSubscriber = TestSubscriber() 262| 1| 263| 1| // Preconditions 264| 1| XCTAssertEqual(subscriber.receivedStates.count, 0) 265| 1| XCTAssertEqual(store.subscriptions.count, 0) 266| 1| 267| 1| autoreleasepool { 268| 1| 269| 1| store.subscribe(subscriber) 270| 1| XCTAssertEqual(subscriber.receivedStates.count, 1) 271| 1| let subscriptionBox = store.subscriptions.first! as! TestSubscriptionBox 272| 1| subscriptionBox.didDeinit = { didDeinit = true } 273| 1| 274| 1| store.dispatch(TracerAction()) 275| 1| XCTAssertEqual(subscriber.receivedStates.count, 2) 276| 1| store.unsubscribe(subscriber) 277| 1| } 278| 1| 279| 1| XCTAssertEqual(store.subscriptions.count, 0) 280| 1| store.dispatch(TracerAction()) 281| 1| XCTAssertEqual(subscriber.receivedStates.count, 2) 282| 1| 283| 1| store = nil 284| 1| } 285| 1| 286| 1| XCTAssertTrue(didDeinit) 287| 1| } 288| | 289| 1| func testRetainCycle_TransformedSubscription() { 290| 1| 291| 1| var didDeinit = false 292| 1| 293| 1| autoreleasepool { 294| 1| 295| 1| store = TestStore(reducer: reducer.handleAction, state: TestAppState(), automaticallySkipsRepeats: false) 296| 1| let subscriber = TestStoreSubscriber() 297| 1| 298| 1| // Preconditions 299| 1| XCTAssertEqual(subscriber.receivedStates.count, 0) 300| 1| XCTAssertEqual(store.subscriptions.count, 0) 301| 1| 302| 1| autoreleasepool { 303| 1| 304| 1| store.subscribe(subscriber, transform: { 305| 3| $0.select { $0.testValue } 306| 1| }) 307| 1| XCTAssertEqual(subscriber.receivedStates.count, 1) 308| 1| let subscriptionBox = store.subscriptions.first! as! TestSubscriptionBox 309| 1| subscriptionBox.didDeinit = { didDeinit = true } 310| 1| 311| 1| store.dispatch(TracerAction()) 312| 1| XCTAssertEqual(subscriber.receivedStates.count, 2) 313| 1| store.unsubscribe(subscriber) 314| 1| } 315| 1| 316| 1| XCTAssertEqual(store.subscriptions.count, 0) 317| 1| store.dispatch(TracerAction()) 318| 1| XCTAssertEqual(subscriber.receivedStates.count, 2) 319| 1| 320| 1| store = nil 321| 1| } 322| 1| 323| 1| XCTAssertTrue(didDeinit) 324| 1| } 325| |} /Users/travis/build/ReSwift/ReSwift/ReSwiftTests/StoreTests.swift: 1| |// 2| |// StoreTests.swift 3| |// ReSwift 4| |// 5| |// Created by Benjamin Encz on 11/27/15. 6| |// Copyright © 2015 ReSwift Community. All rights reserved. 7| |// 8| | 9| |import XCTest 10| |@testable import ReSwift 11| | 12| |class StoreTests: XCTestCase { 13| | 14| | /** 15| | it dispatches an Init action when it doesn't receive an initial state 16| | */ 17| 1| func testInit() { 18| 1| let reducer = MockReducer() 19| 1| _ = Store(reducer: reducer.handleAction, state: nil) 20| 1| 21| 1| XCTAssert(reducer.calledWithAction[0] is ReSwiftInit) 22| 1| } 23| | 24| | /** 25| | it deinitializes when no reference is held 26| | */ 27| 1| func testDeinit() { 28| 1| var deInitCount = 0 29| 1| 30| 1| autoreleasepool { 31| 1| let reducer = TestReducer() 32| 1| _ = DeInitStore( 33| 1| reducer: reducer.handleAction, 34| 1| state: TestAppState(), 35| 1| deInitAction: { deInitCount += 1 }) 36| 1| } 37| 1| 38| 1| XCTAssertEqual(deInitCount, 1) 39| 1| } 40| | 41| |} 42| | 43| |// Used for deinitialization test 44| |class DeInitStore: Store { 45| | var deInitAction: (() -> Void)? 46| | 47| 1| deinit { 48| 1| deInitAction?() 49| 1| } 50| | 51| | required convenience init( 52| | reducer: @escaping Reducer, 53| | state: State?, 54| 1| deInitAction: (() -> Void)?) { 55| 1| self.init( 56| 1| reducer: reducer, 57| 1| state: state, 58| 1| middleware: [], 59| 1| automaticallySkipsRepeats: false) 60| 1| self.deInitAction = deInitAction 61| 1| } 62| | 63| | required init( 64| | reducer: @escaping Reducer, 65| | state: State?, 66| | middleware: [Middleware], 67| 1| automaticallySkipsRepeats: Bool) { 68| 1| super.init( 69| 1| reducer: reducer, 70| 1| state: state, 71| 1| middleware: middleware, 72| 1| automaticallySkipsRepeats: automaticallySkipsRepeats) 73| 1| } 74| |} 75| | 76| |struct CounterState: StateType { 77| | var count: Int = 0 78| |} 79| | 80| |class MockReducer { 81| | 82| | var calledWithAction: [Action] = [] 83| | 84| 1| func handleAction(action: Action, state: CounterState?) -> CounterState { 85| 1| calledWithAction.append(action) 86| 1| 87| 1| return state ?? CounterState() 88| 1| } 89| | 90| |} /Users/travis/build/ReSwift/ReSwift/ReSwiftTests/TestFakes.swift: 1| |// 2| |// TestFakes.swift 3| |// ReSwift 4| |// 5| |// Created by Benjamin Encz on 12/24/15. 6| |// Copyright © 2015 ReSwift Community. All rights reserved. 7| |// 8| | 9| |import Foundation 10| |import ReSwift 11| | 12| |struct TestAppState: StateType { 13| | var testValue: Int? 14| | 15| 32| init() { 16| 32| testValue = nil 17| 32| } 18| |} 19| | 20| |struct TestStringAppState: StateType { 21| | var testValue: String 22| | 23| 10| init() { 24| 10| testValue = "Initial" 25| 10| } 26| |} 27| | 28| |extension TestStringAppState: Equatable { 29| 6| static func == (lhs: TestStringAppState, rhs: TestStringAppState) -> Bool { 30| 6| return lhs.testValue == rhs.testValue 31| 6| } 32| |} 33| | 34| |struct TestNonEquatable: StateType { 35| | var testValue: NonEquatable 36| | 37| 4| init() { 38| 4| testValue = NonEquatable() 39| 4| } 40| |} 41| | 42| |struct NonEquatable { 43| | var testValue: String 44| | 45| 6| init() { 46| 6| testValue = "Initial" 47| 6| } 48| |} 49| | 50| |struct TestCustomAppState: StateType { 51| | var substate: TestCustomSubstate 52| | 53| 0| init(substate: TestCustomSubstate) { 54| 0| self.substate = substate 55| 0| } 56| | 57| 6| init(substateValue value: Int = 0) { 58| 6| self.substate = TestCustomSubstate(value: value) 59| 6| } 60| | 61| | struct TestCustomSubstate { 62| | var value: Int 63| | } 64| |} 65| | 66| |struct NoOpAction: Action {} 67| | 68| |struct SetValueAction: Action { 69| | 70| | let value: Int? 71| | static let type = "SetValueAction" 72| | 73| 32| init (_ value: Int?) { 74| 32| self.value = value 75| 32| } 76| |} 77| | 78| |struct SetValueStringAction: Action { 79| | 80| | var value: String 81| | static let type = "SetValueStringAction" 82| | 83| 14| init (_ value: String) { 84| 14| self.value = value 85| 14| } 86| |} 87| | 88| |struct SetCustomSubstateAction: Action { 89| | 90| | var value: Int 91| | static let type = "SetCustomSubstateAction" 92| | 93| 6| init (_ value: Int) { 94| 6| self.value = value 95| 6| } 96| |} 97| | 98| |struct SetNonEquatableAction: Action { 99| | var value: NonEquatable 100| | static let type = "SetNonEquatableAction" 101| | 102| 2| init (_ value: NonEquatable) { 103| 2| self.value = value 104| 2| } 105| |} 106| | 107| |struct TestReducer { 108| 32| func handleAction(action: Action, state: TestAppState?) -> TestAppState { 109| 32| var state = state ?? TestAppState() 110| 32| 111| 32| switch action { 112| 32| case let action as SetValueAction: 113| 28| state.testValue = action.value 114| 28| return state 115| 32| default: 116| 4| return state 117| 32| } 118| 32| } 119| |} 120| | 121| |struct TestValueStringReducer { 122| 12| func handleAction(action: Action, state: TestStringAppState?) -> TestStringAppState { 123| 12| var state = state ?? TestStringAppState() 124| 12| 125| 12| switch action { 126| 12| case let action as SetValueStringAction: 127| 10| state.testValue = action.value 128| 10| return state 129| 12| default: 130| 2| return state 131| 12| } 132| 12| } 133| |} 134| | 135| |struct TestCustomAppStateReducer { 136| 6| func handleAction(action: Action, state: TestCustomAppState?) -> TestCustomAppState { 137| 6| var state = state ?? TestCustomAppState() 138| 6| 139| 6| switch action { 140| 6| case let action as SetCustomSubstateAction: 141| 6| state.substate.value = action.value 142| 6| return state 143| 6| default: 144| 0| return state 145| 6| } 146| 6| } 147| |} 148| | 149| |struct TestNonEquatableReducer { 150| | func handleAction(action: Action, state: TestNonEquatable?) -> 151| 4| TestNonEquatable { 152| 4| var state = state ?? TestNonEquatable() 153| 4| 154| 4| switch action { 155| 4| case let action as SetNonEquatableAction: 156| 2| state.testValue = action.value 157| 2| return state 158| 4| default: 159| 2| return state 160| 4| } 161| 4| } 162| |} 163| | 164| |class TestStoreSubscriber: StoreSubscriber { 165| | var receivedStates: [T] = [] 166| | 167| 27| func newState(state: T) { 168| 27| receivedStates.append(state) 169| 27| } 170| |} 171| | 172| |class BlockSubscriber: StoreSubscriber { 173| | typealias StoreSubscriberStateType = S 174| | private let block: (S) -> Void 175| | 176| 2| init(block: @escaping (S) -> Void) { 177| 2| self.block = block 178| 2| } 179| | 180| 3| func newState(state: S) { 181| 3| self.block(state) 182| 3| } 183| |} 184| | 185| |class DispatchingSubscriber: StoreSubscriber { 186| | var store: Store 187| | 188| 1| init(store: Store) { 189| 1| self.store = store 190| 1| } 191| | 192| 4| func newState(state: TestAppState) { 193| 4| // Test if we've already dispatched this action to 194| 4| // avoid endless recursion 195| 4| if state.testValue != 5 { 196| 2| self.store.dispatch(SetValueAction(5)) 197| 4| } 198| 4| } 199| |} 200| | 201| |class CallbackStoreSubscriber: StoreSubscriber { 202| | 203| | let handler: (T) -> Void 204| | 205| 1| init(handler: @escaping (T) -> Void) { 206| 1| self.handler = handler 207| 1| } 208| | 209| 2| func newState(state: T) { 210| 2| handler(state) 211| 2| } 212| |} /Users/travis/build/ReSwift/ReSwift/ReSwiftTests/TypeHelperTests.swift: 1| |// 2| |// TypeHelperTests.swift 3| |// ReSwift 4| |// 5| |// Created by Benjamin Encz on 12/20/15. 6| |// Copyright © 2015 ReSwift Community. All rights reserved. 7| |// 8| | 9| |import XCTest 10| |/** 11| | @testable import for testing of `withSpecificTypes` 12| | */ 13| |@testable import ReSwift 14| | 15| |struct AppState1: StateType {} 16| |struct AppState2: StateType {} 17| | 18| |class TypeHelperTests: XCTestCase { 19| | 20| | /** 21| | it calls methods if the source type can be casted into the function signature type 22| | */ 23| 1| func testSourceTypeCasting() { 24| 1| var called = false 25| 1| let reducerFunction: (Action, AppState1?) -> AppState1 = { action, state in 26| 1| called = true 27| 1| 28| 1| return state ?? AppState1() 29| 1| } 30| 1| 31| 1| withSpecificTypes(NoOpAction(), state: AppState1(), function: reducerFunction) 32| 1| 33| 1| XCTAssertTrue(called) 34| 1| } 35| | 36| | /** 37| | it calls the method if the source type is nil 38| | */ 39| 1| func testCallsIfSourceTypeIsNil() { 40| 1| var called = false 41| 1| let reducerFunction: (Action, AppState1?) -> AppState1 = { action, state in 42| 1| called = true 43| 1| 44| 1| return state ?? AppState1() 45| 1| } 46| 1| 47| 1| withSpecificTypes(NoOpAction(), state: nil, function: reducerFunction) 48| 1| 49| 1| XCTAssertTrue(called) 50| 1| } 51| | 52| | /** 53| | it doesn't call if source type can't be casted to function signature type 54| | */ 55| 1| func testDoesntCallIfCastFails() { 56| 1| var called = false 57| 1| let reducerFunction: (Action, AppState1?) -> AppState1 = { action, state in 58| 0| called = true 59| 0| 60| 0| return state ?? AppState1() 61| 0| } 62| 1| 63| 1| withSpecificTypes(NoOpAction(), state: AppState2(), function: reducerFunction) 64| 1| 65| 1| XCTAssertFalse(called) 66| 1| } 67| |} /Users/travis/build/ReSwift/ReSwift/ReSwiftTests/XCTest+Assertions.swift: 1| |// 2| |// Assertions 3| |// Copyright © 2015 mohamede1945. All rights reserved. 4| |// https://github.com/mohamede1945/AssertionsTestingExample 5| |// 6| | 7| |import Foundation 8| |import XCTest 9| |/** 10| | @testable import for testing of `Assertions.fatalErrorClosure` 11| | */ 12| |@testable import ReSwift 13| | 14| |private let noReturnFailureWaitTime = 0.1 15| | 16| |public extension XCTestCase { 17| | /** 18| | Expects an `fatalError` to be called. 19| | If `fatalError` not called, the test case will fail. 20| | 21| | - parameter expectedMessage: The expected message to be asserted to the one passed to the 22| | `fatalError`. If nil, then ignored. 23| | - parameter file: The file name that called the method. 24| | - parameter line: The line number that called the method. 25| | - parameter testCase: The test case to be executed that expected to fire the assertion 26| | method. 27| | */ 28| | func expectFatalError(expectedMessage: String? = nil, file: StaticString = #file, 29| 1| line: UInt = #line, testCase: @escaping () -> Void) { 30| 1| expectAssertionNoReturnFunction( 31| 1| functionName: "fatalError", 32| 1| file: file, 33| 1| line: line, 34| 1| function: { (caller: @escaping (String) -> Void) -> Void in 35| 1| Assertions.fatalErrorClosure = { message, _, _ in caller(message) } 36| 1| }, 37| 1| expectedMessage: expectedMessage, 38| 1| testCase: testCase, 39| 1| cleanUp: { 40| 1| Assertions.fatalErrorClosure = Assertions.swiftFatalErrorClosure 41| 1| }) 42| 1| } 43| | 44| | // MARK: Private Methods 45| | 46| | // swiftlint:disable function_parameter_count 47| | private func expectAssertionNoReturnFunction( 48| | functionName funcName: String, 49| | file: StaticString, 50| | line: UInt, 51| | function: (_ caller: @escaping (String) -> Void) -> Void, 52| | expectedMessage: String? = nil, 53| | testCase: @escaping () -> Void, 54| 1| cleanUp: @escaping () -> Void) { 55| 1| 56| 1| let asyncExpectation = futureExpectation(withDescription: funcName + "-Expectation") 57| 1| var assertionMessage: String? 58| 1| 59| 1| function { (message) -> Void in 60| 1| assertionMessage = message 61| 1| asyncExpectation.fulfill() 62| 1| } 63| 1| 64| 1| // act, perform on separate thead because a call to function runs forever 65| 1| dispatchUserInitiatedAsync(execute: testCase) 66| 1| 67| 1| waitForFutureExpectations(withTimeout: noReturnFailureWaitTime) { _ in 68| 1| defer { cleanUp() } 69| 1| guard let assertionMessage = assertionMessage else { 70| 0| XCTFail(funcName + " is expected to be called.", file: file, line: line) 71| 0| return 72| 1| } 73| 1| if let expectedMessage = expectedMessage { 74| 0| XCTAssertEqual(assertionMessage, expectedMessage, funcName + 75| 0| " called with incorrect message.", file: file, line: line) 76| 1| } 77| 1| } 78| 1| } 79| | // swiftlint:enable function_parameter_count 80| |} /Users/travis/build/ReSwift/ReSwift/ReSwiftTests/XCTest+Compatibility.swift: 1| |// Copyright © 2019 ReSwift Community. All rights reserved. 2| | 3| |import XCTest 4| | 5| 2|func dispatchAsync(execute work: @escaping @convention(block) () -> Swift.Void) { 6| 2| DispatchQueue.global(qos: .default).async(execute: work) 7| 2|} 8| | 9| |func dispatchUserInitiatedAsync 10| 1| (execute work: @escaping @convention(block) () -> Swift.Void) { 11| 1| DispatchQueue.global(qos: .userInitiated).async(execute: work) 12| 1|} 13| | 14| |extension XCTestCase { 15| | 16| 3| func futureExpectation(withDescription description: String) -> XCTestExpectation { 17| 3| return expectation(description: description) 18| 3| } 19| | 20| | func waitForFutureExpectations( 21| | withTimeout timeout: TimeInterval, 22| 3| handler: XCWaitCompletionHandler? = nil) { 23| 3| 24| 3| waitForExpectations(timeout: timeout, handler: handler) 25| 3| } 26| |} <<<<<< EOF # path=fixes ./ReSwift/ReSwift.h:8,10,13,16 <<<<<< EOF