Adds RxCocoa unit tests.
This commit is contained in:
parent
29b73836b9
commit
e22cf3dfb8
|
|
@ -30,7 +30,7 @@ class ControlTarget: RxTarget {
|
|||
|
||||
let selector: Selector = "eventHandler:"
|
||||
|
||||
unowned let control: Control
|
||||
weak var control: Control?
|
||||
#if os(iOS) || os(tvOS)
|
||||
let controlEvents: UIControlEvents
|
||||
#endif
|
||||
|
|
@ -72,7 +72,7 @@ class ControlTarget: RxTarget {
|
|||
#endif
|
||||
|
||||
func eventHandler(sender: Control!) {
|
||||
if let callback = self.callback {
|
||||
if let callback = self.callback, control = self.control {
|
||||
callback(control)
|
||||
}
|
||||
}
|
||||
|
|
@ -80,10 +80,10 @@ class ControlTarget: RxTarget {
|
|||
override func dispose() {
|
||||
super.dispose()
|
||||
#if os(iOS) || os(tvOS)
|
||||
self.control.removeTarget(self, action: self.selector, forControlEvents: self.controlEvents)
|
||||
self.control?.removeTarget(self, action: self.selector, forControlEvents: self.controlEvents)
|
||||
#elseif os(OSX)
|
||||
self.control.target = nil
|
||||
self.control.action = nil
|
||||
self.control?.target = nil
|
||||
self.control?.action = nil
|
||||
#endif
|
||||
self.callback = nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,11 +19,26 @@ class RxTarget : NSObject
|
|||
override init() {
|
||||
super.init()
|
||||
self.retainSelf = self
|
||||
|
||||
#if TRACE_RESOURCES
|
||||
OSAtomicIncrement32(&resourceCount)
|
||||
#endif
|
||||
|
||||
#if DEBUG
|
||||
MainScheduler.ensureExecutingOnScheduler()
|
||||
#endif
|
||||
}
|
||||
|
||||
func dispose() {
|
||||
#if DEBUG
|
||||
MainScheduler.ensureExecutingOnScheduler()
|
||||
#endif
|
||||
self.retainSelf = nil
|
||||
}
|
||||
|
||||
#if TRACE_RESOURCES
|
||||
deinit {
|
||||
OSAtomicDecrement32(&resourceCount)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
@ -12,36 +12,74 @@ import Cocoa
|
|||
import RxSwift
|
||||
#endif
|
||||
|
||||
var rx_value_key: UInt8 = 0
|
||||
var rx_control_events_key: UInt8 = 0
|
||||
|
||||
extension NSControl {
|
||||
|
||||
/**
|
||||
Reactive wrapper for control event.
|
||||
*/
|
||||
public var rx_controlEvents: ControlEvent<Void> {
|
||||
let source: Observable<Void> = AnonymousObservable { observer in
|
||||
MainScheduler.ensureExecutingOnScheduler()
|
||||
|
||||
let observer = ControlTarget(control: self) { control in
|
||||
observer.on(.Next())
|
||||
}
|
||||
|
||||
return observer
|
||||
}.takeUntil(rx_deallocated)
|
||||
MainScheduler.ensureExecutingOnScheduler()
|
||||
|
||||
let source = rx_lazyInstanceObservable(&rx_control_events_key) { () -> Observable<Void> in
|
||||
AnonymousObservable { [weak self] observer in
|
||||
MainScheduler.ensureExecutingOnScheduler()
|
||||
|
||||
guard let control = self else {
|
||||
observer.on(.Completed)
|
||||
return NopDisposable.instance
|
||||
}
|
||||
|
||||
let observer = ControlTarget(control: control) { control in
|
||||
observer.on(.Next())
|
||||
}
|
||||
|
||||
return observer
|
||||
}.takeUntil(self.rx_deallocated)
|
||||
}
|
||||
|
||||
return ControlEvent(source: source)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Helper to make sure that `Observable` returned from `createCachedObservable` is only created once.
|
||||
This is important because on OSX there is only one `target` and `action` properties on `NSControl`.
|
||||
*/
|
||||
func rx_lazyInstanceObservable<T: AnyObject>(key: UnsafePointer<Void>, createCachedObservable: () -> T) -> T {
|
||||
if let value = objc_getAssociatedObject(self, key) {
|
||||
return value as! T
|
||||
}
|
||||
|
||||
let observable = createCachedObservable()
|
||||
|
||||
objc_setAssociatedObject(self, key, observable, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
|
||||
return observable
|
||||
}
|
||||
|
||||
func rx_value<T>(getter getter: () -> T, setter: T -> Void) -> ControlProperty<T> {
|
||||
let source: Observable<T> = AnonymousObservable { observer in
|
||||
observer.on(.Next(getter()))
|
||||
|
||||
let observer = ControlTarget(control: self) { control in
|
||||
MainScheduler.ensureExecutingOnScheduler()
|
||||
|
||||
let source = rx_lazyInstanceObservable(&rx_value_key) { () -> Observable<T> in
|
||||
return AnonymousObservable { [weak self] observer in
|
||||
guard let control = self else {
|
||||
observer.on(.Completed)
|
||||
return NopDisposable.instance
|
||||
}
|
||||
|
||||
observer.on(.Next(getter()))
|
||||
}
|
||||
|
||||
return observer
|
||||
}.takeUntil(rx_deallocated)
|
||||
|
||||
|
||||
let observer = ControlTarget(control: control) { control in
|
||||
observer.on(.Next(getter()))
|
||||
}
|
||||
|
||||
return observer
|
||||
}.takeUntil(self.rx_deallocated)
|
||||
}
|
||||
|
||||
|
||||
return ControlProperty(source: source, observer: AnyObserver { event in
|
||||
switch event {
|
||||
case .Next(let value):
|
||||
|
|
|
|||
|
|
@ -15,13 +15,12 @@ import RxSwift
|
|||
class RxTextFieldDelegate : DelegateProxy
|
||||
, NSTextFieldDelegate
|
||||
, DelegateProxyType {
|
||||
let textField: NSTextField
|
||||
let textSubject = ReplaySubject<String>.create(bufferSize: 1)
|
||||
let textSubject = PublishSubject<String>()
|
||||
|
||||
required init(parentObject: AnyObject) {
|
||||
self.textField = parentObject as! NSTextField
|
||||
let textField = parentObject as! NSTextField
|
||||
super.init(parentObject: parentObject)
|
||||
self.textSubject.on(.Next(self.textField.stringValue))
|
||||
self.textSubject.on(.Next(textField.stringValue))
|
||||
}
|
||||
|
||||
override func controlTextDidChange(notification: NSNotification) {
|
||||
|
|
@ -59,7 +58,9 @@ extension NSTextField {
|
|||
public var rx_text: ControlProperty<String> {
|
||||
let delegate = proxyForObject(self) as RxTextFieldDelegate
|
||||
|
||||
let source = delegate.textSubject
|
||||
let source = deferred { [weak self] in
|
||||
delegate.textSubject.startWith(self?.stringValue ?? "")
|
||||
}.takeUntil(rx_deallocated)
|
||||
|
||||
return ControlProperty(source: source, observer: AnyObserver { [weak self] event in
|
||||
MainScheduler.ensureExecutingOnScheduler()
|
||||
|
|
|
|||
|
|
@ -38,8 +38,14 @@ extension UIBarButtonItem {
|
|||
Reactive wrapper for target action pattern on `self`.
|
||||
*/
|
||||
public var rx_tap: ControlEvent<Void> {
|
||||
let source: Observable<Void> = AnonymousObservable { observer in
|
||||
let target = BarButtonItemTarget(barButtonItem: self) {
|
||||
let source: Observable<Void> = AnonymousObservable { [weak self] observer in
|
||||
|
||||
guard let control = self else {
|
||||
observer.on(.Completed)
|
||||
return NopDisposable.instance
|
||||
}
|
||||
|
||||
let target = BarButtonItemTarget(barButtonItem: control) {
|
||||
observer.on(.Next())
|
||||
}
|
||||
return target
|
||||
|
|
@ -52,7 +58,7 @@ extension UIBarButtonItem {
|
|||
|
||||
|
||||
@objc
|
||||
class BarButtonItemTarget: NSObject, Disposable {
|
||||
class BarButtonItemTarget: RxTarget {
|
||||
typealias Callback = () -> Void
|
||||
|
||||
weak var barButtonItem: UIBarButtonItem?
|
||||
|
|
@ -66,12 +72,11 @@ class BarButtonItemTarget: NSObject, Disposable {
|
|||
barButtonItem.action = Selector("action:")
|
||||
}
|
||||
|
||||
deinit {
|
||||
dispose()
|
||||
}
|
||||
|
||||
func dispose() {
|
||||
override func dispose() {
|
||||
super.dispose()
|
||||
#if DEBUG
|
||||
MainScheduler.ensureExecutingOnScheduler()
|
||||
#endif
|
||||
|
||||
barButtonItem?.target = nil
|
||||
barButtonItem?.action = nil
|
||||
|
|
|
|||
|
|
@ -143,10 +143,13 @@ extension UICollectionView {
|
|||
|
||||
*/
|
||||
public func rx_modelSelected<T>() -> ControlEvent<T> {
|
||||
let source: Observable<T> = rx_itemSelected .map { indexPath in
|
||||
let dataSource: RxCollectionViewReactiveArrayDataSource<T> = castOrFatalError(self.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_itemsWith*` methods was used.")
|
||||
let source: Observable<T> = rx_itemSelected.flatMap { [weak self] indexPath -> Observable<T> in
|
||||
guard let view = self else {
|
||||
return empty()
|
||||
}
|
||||
let dataSource: RxCollectionViewReactiveArrayDataSource<T> = castOrFatalError(view.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_itemsWith*` methods was used.")
|
||||
|
||||
return dataSource.modelAtIndex(indexPath.item)!
|
||||
return just(dataSource.modelAtIndex(indexPath.item)!)
|
||||
}
|
||||
|
||||
return ControlEvent(source: source)
|
||||
|
|
|
|||
|
|
@ -41,10 +41,15 @@ extension UIControl {
|
|||
- parameter controlEvents: Filter for observed event types.
|
||||
*/
|
||||
public func rx_controlEvents(controlEvents: UIControlEvents) -> ControlEvent<Void> {
|
||||
let source: Observable<Void> = AnonymousObservable { observer in
|
||||
let source: Observable<Void> = AnonymousObservable { [weak self] observer in
|
||||
MainScheduler.ensureExecutingOnScheduler()
|
||||
|
||||
let controlTarget = ControlTarget(control: self, controlEvents: controlEvents) {
|
||||
|
||||
guard let control = self else {
|
||||
observer.on(.Completed)
|
||||
return NopDisposable.instance
|
||||
}
|
||||
|
||||
let controlTarget = ControlTarget(control: control, controlEvents: controlEvents) {
|
||||
control in
|
||||
observer.on(.Next())
|
||||
}
|
||||
|
|
@ -58,11 +63,15 @@ extension UIControl {
|
|||
}
|
||||
|
||||
func rx_value<T>(getter getter: () -> T, setter: T -> Void) -> ControlProperty<T> {
|
||||
let source: Observable<T> = AnonymousObservable { observer in
|
||||
|
||||
let source: Observable<T> = AnonymousObservable { [weak self] observer in
|
||||
guard let control = self else {
|
||||
observer.on(.Completed)
|
||||
return NopDisposable.instance
|
||||
}
|
||||
|
||||
observer.on(.Next(getter()))
|
||||
|
||||
let controlTarget = ControlTarget(control: self, controlEvents: [.EditingChanged, .ValueChanged]) { control in
|
||||
|
||||
let controlTarget = ControlTarget(control: control, controlEvents: [.EditingChanged, .ValueChanged]) { control in
|
||||
observer.on(.Next(getter()))
|
||||
}
|
||||
|
||||
|
|
@ -73,7 +82,7 @@ extension UIControl {
|
|||
|
||||
return ControlProperty<T>(source: source, observer: AnyObserver { event in
|
||||
MainScheduler.ensureExecutingOnScheduler()
|
||||
|
||||
|
||||
switch event {
|
||||
case .Next(let value):
|
||||
setter(value)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class GestureTarget: RxTarget {
|
|||
|
||||
let selector = Selector("eventHandler:")
|
||||
|
||||
unowned let gestureRecognizer: UIGestureRecognizer
|
||||
weak var gestureRecognizer: UIGestureRecognizer?
|
||||
var callback: Callback?
|
||||
|
||||
init(_ gestureRecognizer: UIGestureRecognizer, callback: Callback) {
|
||||
|
|
@ -38,15 +38,15 @@ class GestureTarget: RxTarget {
|
|||
}
|
||||
|
||||
func eventHandler(sender: UIGestureRecognizer!) {
|
||||
if let callback = self.callback {
|
||||
callback(self.gestureRecognizer)
|
||||
if let callback = self.callback, gestureRecognizer = self.gestureRecognizer {
|
||||
callback(gestureRecognizer)
|
||||
}
|
||||
}
|
||||
|
||||
override func dispose() {
|
||||
super.dispose()
|
||||
|
||||
self.gestureRecognizer.removeTarget(self, action: self.selector)
|
||||
self.gestureRecognizer?.removeTarget(self, action: self.selector)
|
||||
self.callback = nil
|
||||
}
|
||||
}
|
||||
|
|
@ -59,10 +59,15 @@ extension UIGestureRecognizer {
|
|||
public var rx_event: ControlEvent<UIGestureRecognizer> {
|
||||
let source: Observable<UIGestureRecognizer> = AnonymousObservable { [weak self] observer in
|
||||
MainScheduler.ensureExecutingOnScheduler()
|
||||
|
||||
guard let control = self else {
|
||||
observer.on(.Completed)
|
||||
return NopDisposable.instance
|
||||
}
|
||||
|
||||
let observer = GestureTarget(self!) {
|
||||
let observer = GestureTarget(control) {
|
||||
control in
|
||||
observer.on(.Next(self!))
|
||||
observer.on(.Next(control))
|
||||
}
|
||||
|
||||
return observer
|
||||
|
|
|
|||
|
|
@ -187,10 +187,14 @@ extension UITableView {
|
|||
|
||||
*/
|
||||
public func rx_modelSelected<T>() -> ControlEvent<T> {
|
||||
let source: Observable<T> = rx_itemSelected.map { ip in
|
||||
let dataSource: RxTableViewReactiveArrayDataSource<T> = castOrFatalError(self.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_subscribeItemsTo` methods was used.")
|
||||
let source: Observable<T> = rx_itemSelected.flatMap { [weak self] ip -> Observable<T> in
|
||||
guard let view = self else {
|
||||
return empty()
|
||||
}
|
||||
|
||||
let dataSource: RxTableViewReactiveArrayDataSource<T> = castOrFatalError(view.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_subscribeItemsTo` methods was used.")
|
||||
|
||||
return dataSource.modelAtIndex(ip.item)!
|
||||
return just(dataSource.modelAtIndex(ip.item)!)
|
||||
}
|
||||
|
||||
return ControlEvent(source: source)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
//
|
||||
// Control+RxTests+Cocoa.swift
|
||||
// RxTests
|
||||
//
|
||||
// Created by Krunoslav Zaher on 10/19/15.
|
||||
//
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
import XCTest
|
||||
|
||||
// NSTextField
|
||||
extension ControlTests {
|
||||
func testTextField_TextCompletesOnDealloc() {
|
||||
let createView: () -> NSTextField = { NSTextField(frame: CGRectMake(0, 0, 1, 1)) }
|
||||
ensurePropertyDeallocated(createView, "a") { (view: NSTextField) in view.rx_text }
|
||||
}
|
||||
}
|
||||
|
||||
// NSControl
|
||||
extension ControlTests {
|
||||
func testControl_DelegateEventCompletesOnDealloc() {
|
||||
let createView: () -> NSControl = { NSControl(frame: CGRectMake(0, 0, 1, 1)) }
|
||||
ensureEventDeallocated(createView) { (view: NSControl) in view.rx_controlEvents }
|
||||
}
|
||||
}
|
||||
|
||||
// NSSlider
|
||||
extension ControlTests {
|
||||
func testCollectionView_DelegateEventCompletesOnDealloc() {
|
||||
let createView: () -> NSSlider = { NSSlider(frame: CGRectMake(0, 0, 1, 1)) }
|
||||
ensurePropertyDeallocated(createView, 0.3) { (view: NSSlider) in view.rx_value }
|
||||
}
|
||||
}
|
||||
|
||||
// NSButton
|
||||
extension ControlTests {
|
||||
func testButton_DelegateEventCompletesOnDealloc() {
|
||||
let createView: () -> NSButton = { NSButton(frame: CGRectMake(0, 0, 1, 1)) }
|
||||
ensureEventDeallocated(createView) { (view: NSButton) in view.rx_tap }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
//
|
||||
// Control+RxTests+UIKit.swift
|
||||
// RxTests
|
||||
//
|
||||
// Created by Ash Furrow on 2015-07-04.
|
||||
//
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
import XCTest
|
||||
|
||||
extension ControlTests {
|
||||
func testSubscribeEnabledToTrue() {
|
||||
let subject = UIControl()
|
||||
let enabledSequence = Variable<Bool>(false)
|
||||
enabledSequence.subscribe(subject.rx_enabled)
|
||||
|
||||
enabledSequence.value = true
|
||||
XCTAssert(subject.enabled == true, "Expected enabled set to true")
|
||||
}
|
||||
|
||||
func testSubscribeEnabledToFalse() {
|
||||
let subject = UIControl()
|
||||
let enabledSequence = Variable<Bool>(true)
|
||||
enabledSequence.subscribe(subject.rx_enabled)
|
||||
|
||||
enabledSequence.value = false
|
||||
XCTAssert(subject.enabled == false, "Expected enabled set to false")
|
||||
}
|
||||
}
|
||||
|
||||
// UITextField
|
||||
extension ControlTests {
|
||||
func testTextField_TextCompletesOnDealloc() {
|
||||
ensurePropertyDeallocated({ UITextField() }, "a") { (view: UITextField) in view.rx_text }
|
||||
}
|
||||
}
|
||||
|
||||
// Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior (<UIAlertController: 0x7fc6820309c0>)
|
||||
// Don't know why can't use ActionSheet and AlertView inside unit tests
|
||||
|
||||
|
||||
// UIActionSheet
|
||||
extension ControlTests {
|
||||
func testActionSheet_DelegateEventCompletesOnDealloc() {
|
||||
let createActionSheet: () -> UIActionSheet = { UIActionSheet(title: "", delegate: nil, cancelButtonTitle: "", destructiveButtonTitle: "") }
|
||||
ensureEventDeallocated(createActionSheet) { (view: UIActionSheet) in view.rx_clickedButtonAtIndex }
|
||||
ensureEventDeallocated(createActionSheet) { (view: UIActionSheet) in view.rx_didDismissWithButtonIndex }
|
||||
ensureEventDeallocated(createActionSheet) { (view: UIActionSheet) in view.rx_willDismissWithButtonIndex }
|
||||
}
|
||||
}
|
||||
|
||||
// UIAlertView
|
||||
extension ControlTests {
|
||||
func testAlertView_DelegateEventCompletesOnDealloc() {
|
||||
let createAlertView: () -> UIAlertView = { UIAlertView(title: "", message: "", delegate: nil, cancelButtonTitle: nil) }
|
||||
ensureEventDeallocated(createAlertView) { (view: UIAlertView) in view.rx_clickedButtonAtIndex }
|
||||
ensureEventDeallocated(createAlertView) { (view: UIAlertView) in view.rx_didDismissWithButtonIndex }
|
||||
ensureEventDeallocated(createAlertView) { (view: UIAlertView) in view.rx_willDismissWithButtonIndex }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// UIBarButtonItem
|
||||
extension ControlTests {
|
||||
func testBarButtonItem_DelegateEventCompletesOnDealloc() {
|
||||
ensureEventDeallocated({ UIBarButtonItem() }) { (view: UIBarButtonItem) in view.rx_tap }
|
||||
}
|
||||
}
|
||||
|
||||
// UICollectionView
|
||||
extension ControlTests {
|
||||
func testCollectionView_DelegateEventCompletesOnDealloc() {
|
||||
let layout = UICollectionViewFlowLayout()
|
||||
let createView: () -> UICollectionView = { UICollectionView(frame: CGRectMake(0, 0, 1, 1), collectionViewLayout: layout) }
|
||||
ensureEventDeallocated(createView) { (view: UICollectionView) in view.rx_itemSelected }
|
||||
ensureEventDeallocated(createView) { (view: UICollectionView) in view.rx_modelSelected() as ControlEvent<Int> }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// UITableView
|
||||
extension ControlTests {
|
||||
func testTableView_DelegateEventCompletesOnDealloc() {
|
||||
let createView: () -> UITableView = { UITableView(frame: CGRectMake(0, 0, 1, 1)) }
|
||||
ensureEventDeallocated(createView) { (view: UITableView) in view.rx_itemSelected }
|
||||
ensureEventDeallocated(createView) { (view: UITableView) in view.rx_modelSelected() as ControlEvent<Int> }
|
||||
ensureEventDeallocated(createView) { (view: UITableView) in view.rx_itemDeleted }
|
||||
ensureEventDeallocated(createView) { (view: UITableView) in view.rx_itemMoved }
|
||||
ensureEventDeallocated(createView) { (view: UITableView) in view.rx_itemInserted }
|
||||
}
|
||||
}
|
||||
|
||||
// UIControl
|
||||
extension ControlTests {
|
||||
func testControl_DelegateEventCompletesOnDealloc() {
|
||||
let createView: () -> UIControl = { UIControl(frame: CGRectMake(0, 0, 1, 1)) }
|
||||
ensureEventDeallocated(createView) { (view: UIControl) in view.rx_controlEvents(.AllEditingEvents) }
|
||||
}
|
||||
}
|
||||
|
||||
// UIDatePicker
|
||||
extension ControlTests {
|
||||
func testDatePicker_DelegateEventCompletesOnDealloc() {
|
||||
let createView: () -> UIDatePicker = { UIDatePicker(frame: CGRectMake(0, 0, 1, 1)) }
|
||||
ensurePropertyDeallocated(createView, NSDate()) { (view: UIDatePicker) in view.rx_date }
|
||||
}
|
||||
}
|
||||
|
||||
// UIGestureRecognizer
|
||||
extension ControlTests {
|
||||
func testGestureRecognizer_DelegateEventCompletesOnDealloc() {
|
||||
let createView: () -> UIGestureRecognizer = { UIGestureRecognizer(target: nil, action: "s") }
|
||||
ensureEventDeallocated(createView) { (view: UIGestureRecognizer) in view.rx_event }
|
||||
}
|
||||
}
|
||||
|
||||
// UIScrollView
|
||||
extension ControlTests {
|
||||
func testScrollView_DelegateEventCompletesOnDealloc() {
|
||||
let createView: () -> UIScrollView = { UIScrollView(frame: CGRectMake(0, 0, 1, 1)) }
|
||||
ensurePropertyDeallocated(createView, CGPoint(x: 1, y: 1)) { (view: UIScrollView) in view.rx_contentOffset }
|
||||
}
|
||||
}
|
||||
|
||||
// UISearchBar
|
||||
extension ControlTests {
|
||||
func testSearchBar_DelegateEventCompletesOnDealloc() {
|
||||
let createView: () -> UISearchBar = { UISearchBar(frame: CGRectMake(0, 0, 1, 1)) }
|
||||
ensurePropertyDeallocated(createView, "a") { (view: UISearchBar) in view.rx_text }
|
||||
}
|
||||
}
|
||||
|
||||
// UISegmentedControl
|
||||
extension ControlTests {
|
||||
func testSegmentedControl_DelegateEventCompletesOnDealloc() {
|
||||
let createView: () -> UISegmentedControl = { UISegmentedControl(items: ["a", "b", "c"]) }
|
||||
ensurePropertyDeallocated(createView, 1) { (view: UISegmentedControl) in view.rx_value }
|
||||
}
|
||||
}
|
||||
|
||||
// UISlider
|
||||
extension ControlTests {
|
||||
func testSlider_DelegateEventCompletesOnDealloc() {
|
||||
let createView: () -> UISlider = { UISlider(frame: CGRectMake(0, 0, 1, 1)) }
|
||||
ensurePropertyDeallocated(createView, 0.5) { (view: UISlider) in view.rx_value }
|
||||
}
|
||||
}
|
||||
|
||||
// UIStepper
|
||||
extension ControlTests {
|
||||
func testStepper_DelegateEventCompletesOnDealloc() {
|
||||
let createView: () -> UIStepper = { UIStepper(frame: CGRectMake(0, 0, 1, 1)) }
|
||||
ensurePropertyDeallocated(createView, 1) { (view: UIStepper) in view.rx_value }
|
||||
}
|
||||
}
|
||||
|
||||
// UISwitch
|
||||
extension ControlTests {
|
||||
func testSwitch_DelegateEventCompletesOnDealloc() {
|
||||
let createView: () -> UISwitch = { UISwitch(frame: CGRectMake(0, 0, 1, 1)) }
|
||||
ensurePropertyDeallocated(createView, true) { (view: UISwitch) in view.rx_value }
|
||||
}
|
||||
}
|
||||
|
||||
// UITextView
|
||||
extension ControlTests {
|
||||
func testText_DelegateEventCompletesOnDealloc() {
|
||||
let createView: () -> UITextView = { UITextView(frame: CGRectMake(0, 0, 1, 1)) }
|
||||
ensurePropertyDeallocated(createView, "text") { (view: UITextView) in view.rx_text }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// Control+RxTests.swift
|
||||
// RxTests
|
||||
//
|
||||
// Created by Krunoslav Zaher on 10/18/15.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RxCocoa
|
||||
import RxSwift
|
||||
import XCTest
|
||||
|
||||
class ControlTests : RxTest {
|
||||
|
||||
func ensurePropertyDeallocated<C, T: Equatable where C: NSObject>(createControl: () -> C, _ initialValue: T, _ propertySelector: C -> ControlProperty<T>) {
|
||||
let variable = Variable(initialValue)
|
||||
|
||||
|
||||
var completed = false
|
||||
var deallocated = false
|
||||
var lastReturnedPropertyValue: T!
|
||||
|
||||
autoreleasepool {
|
||||
var control: C! = createControl()
|
||||
|
||||
let property = propertySelector(control)
|
||||
|
||||
let disposable = variable.bindTo(property)
|
||||
|
||||
_ = property.subscribe(onNext: { n in
|
||||
lastReturnedPropertyValue = n
|
||||
}, onCompleted: {
|
||||
completed = true
|
||||
disposable.dispose()
|
||||
})
|
||||
|
||||
|
||||
_ = control.rx_deallocated.subscribeNext { _ in
|
||||
deallocated = true
|
||||
}
|
||||
|
||||
control = nil
|
||||
}
|
||||
|
||||
XCTAssertTrue(deallocated)
|
||||
XCTAssertTrue(completed)
|
||||
XCTAssertEqual(initialValue, lastReturnedPropertyValue)
|
||||
}
|
||||
|
||||
func ensureEventDeallocated<C, T where C: NSObject>(createControl: () -> C, _ eventSelector: C -> ControlEvent<T>) {
|
||||
var completed = false
|
||||
var deallocated = false
|
||||
|
||||
autoreleasepool {
|
||||
var control: C! = createControl()
|
||||
let eventObservable = eventSelector(control)
|
||||
|
||||
_ = eventObservable.subscribe(onNext: { n in
|
||||
|
||||
}, onCompleted: {
|
||||
completed = true
|
||||
})
|
||||
|
||||
_ = control.rx_deallocated.subscribeNext { _ in
|
||||
deallocated = true
|
||||
}
|
||||
|
||||
control = nil
|
||||
}
|
||||
|
||||
|
||||
XCTAssertTrue(deallocated)
|
||||
XCTAssertTrue(completed)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
//
|
||||
// UIControl+RxTests.swift
|
||||
// RxTests
|
||||
//
|
||||
// Created by Ash Furrow on 2015-07-04.
|
||||
//
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
import XCTest
|
||||
|
||||
class UIControlRxTests : RxTest {
|
||||
func testSubscribeEnabledToTrue() {
|
||||
let subject = UIControl()
|
||||
let enabledSequence = Variable<Bool>(false)
|
||||
enabledSequence.subscribe(subject.rx_enabled)
|
||||
|
||||
enabledSequence.value = true
|
||||
XCTAssert(subject.enabled == true, "Expected enabled set to true")
|
||||
}
|
||||
|
||||
func testSubscribeEnabledToFalse() {
|
||||
let subject = UIControl()
|
||||
let enabledSequence = Variable<Bool>(true)
|
||||
enabledSequence.subscribe(subject.rx_enabled)
|
||||
|
||||
enabledSequence.value = false
|
||||
XCTAssert(subject.enabled == false, "Expected enabled set to false")
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
5E5D10BB1B48355200432B25 /* UIControl+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E5D10BA1B48355200432B25 /* UIControl+RxTests.swift */; };
|
||||
5E5D10BB1B48355200432B25 /* Control+RxTests+UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E5D10BA1B48355200432B25 /* Control+RxTests+UIKit.swift */; };
|
||||
C69B65001BA88FAC00A7FA73 /* ObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C69B64FF1BA88FAC00A7FA73 /* ObserverTests.swift */; };
|
||||
C69B65011BA8957C00A7FA73 /* ObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C69B64FF1BA88FAC00A7FA73 /* ObserverTests.swift */; };
|
||||
C801EB5A1B97951100C4D8C4 /* Observable+CreationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C801EB591B97951100C4D8C4 /* Observable+CreationTest.swift */; };
|
||||
|
|
@ -112,6 +112,10 @@
|
|||
C8E3812B1B2083C2008CDC33 /* PrimitiveMockObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E3812A1B2083C2008CDC33 /* PrimitiveMockObserver.swift */; };
|
||||
C8E3812C1B2083C2008CDC33 /* PrimitiveMockObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E3812A1B2083C2008CDC33 /* PrimitiveMockObserver.swift */; };
|
||||
C8E3813A1B21B77E008CDC33 /* Observable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E381221B2063CC008CDC33 /* Observable+Extensions.swift */; };
|
||||
C8E9D2BD1BD422D80079D0DB /* Control+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E9D2BC1BD422D80079D0DB /* Control+RxTests.swift */; settings = {ASSET_TAGS = (); }; };
|
||||
C8E9D2BE1BD422D80079D0DB /* Control+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E9D2BC1BD422D80079D0DB /* Control+RxTests.swift */; settings = {ASSET_TAGS = (); }; };
|
||||
C8E9D2BF1BD422D80079D0DB /* Control+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E9D2BC1BD422D80079D0DB /* Control+RxTests.swift */; settings = {ASSET_TAGS = (); }; };
|
||||
C8E9D2C41BD452650079D0DB /* Control+RxTests+Cocoa.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E9D2C01BD4525B0079D0DB /* Control+RxTests+Cocoa.swift */; settings = {ASSET_TAGS = (); }; };
|
||||
C8EA2D371BD02E1900FB22AC /* EquatableArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8EA2D361BD02E1900FB22AC /* EquatableArray.swift */; };
|
||||
C8EA2D381BD02E1900FB22AC /* EquatableArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8EA2D361BD02E1900FB22AC /* EquatableArray.swift */; };
|
||||
C8EA2D391BD02E1900FB22AC /* EquatableArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8EA2D361BD02E1900FB22AC /* EquatableArray.swift */; };
|
||||
|
|
@ -122,7 +126,7 @@
|
|||
D203C4EE1BB9C22800D02D00 /* KVOObservableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8633AE41B0A9FF300375D60 /* KVOObservableTests.swift */; };
|
||||
D203C4EF1BB9C22800D02D00 /* RxCocoaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B5BEA01B4A6A82000D732C /* RxCocoaTests.swift */; };
|
||||
D203C4F01BB9C22800D02D00 /* NSObject+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C81CC92A1B513FD400915606 /* NSObject+RxTests.swift */; };
|
||||
D203C5141BB9C54A00D02D00 /* UIControl+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E5D10BA1B48355200432B25 /* UIControl+RxTests.swift */; };
|
||||
D203C5141BB9C54A00D02D00 /* Control+RxTests+UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E5D10BA1B48355200432B25 /* Control+RxTests+UIKit.swift */; };
|
||||
D251ED291BB9BF90002D0E36 /* RxCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C8A468CC1B8A897800BF917B /* RxCocoa.framework */; };
|
||||
D2AF91971BD2EBB900A008C1 /* MockDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2AF91961BD2EBB900A008C1 /* MockDisposable.swift */; };
|
||||
D2AF91991BD3D9C600A008C1 /* MockDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2AF91961BD2EBB900A008C1 /* MockDisposable.swift */; };
|
||||
|
|
@ -172,7 +176,7 @@
|
|||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
5E5D10BA1B48355200432B25 /* UIControl+RxTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIControl+RxTests.swift"; sourceTree = "<group>"; };
|
||||
5E5D10BA1B48355200432B25 /* Control+RxTests+UIKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Control+RxTests+UIKit.swift"; sourceTree = "<group>"; };
|
||||
C69B64FF1BA88FAC00A7FA73 /* ObserverTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObserverTests.swift; sourceTree = "<group>"; };
|
||||
C801EB591B97951100C4D8C4 /* Observable+CreationTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+CreationTest.swift"; sourceTree = "<group>"; };
|
||||
C80DDEDB1BCE9A03006A1832 /* Driver+Test.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Driver+Test.swift"; sourceTree = "<group>"; };
|
||||
|
|
@ -231,6 +235,8 @@
|
|||
C8E381221B2063CC008CDC33 /* Observable+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Extensions.swift"; sourceTree = "<group>"; };
|
||||
C8E381271B207D03008CDC33 /* PrimitiveHotObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrimitiveHotObservable.swift; sourceTree = "<group>"; };
|
||||
C8E3812A1B2083C2008CDC33 /* PrimitiveMockObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrimitiveMockObserver.swift; sourceTree = "<group>"; };
|
||||
C8E9D2BC1BD422D80079D0DB /* Control+RxTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Control+RxTests.swift"; sourceTree = "<group>"; };
|
||||
C8E9D2C01BD4525B0079D0DB /* Control+RxTests+Cocoa.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Control+RxTests+Cocoa.swift"; sourceTree = "<group>"; };
|
||||
C8EA2D361BD02E1900FB22AC /* EquatableArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EquatableArray.swift; sourceTree = "<group>"; };
|
||||
C8FDC5F71B2B5B7E0065F8D9 /* ElementIndexPair.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ElementIndexPair.swift; sourceTree = "<group>"; };
|
||||
D2AF91961BD2EBB900A008C1 /* MockDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockDisposable.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -324,12 +330,14 @@
|
|||
C811082A1AF50E2A001C13E4 /* RxCocoaTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C811082B1AF50E2A001C13E4 /* NSNotificationCenterTests.swift */,
|
||||
C814CEA61AF642D600E98087 /* UI+RxTests.swift */,
|
||||
5E5D10BA1B48355200432B25 /* UIControl+RxTests.swift */,
|
||||
C8E9D2BC1BD422D80079D0DB /* Control+RxTests.swift */,
|
||||
5E5D10BA1B48355200432B25 /* Control+RxTests+UIKit.swift */,
|
||||
C8E9D2C01BD4525B0079D0DB /* Control+RxTests+Cocoa.swift */,
|
||||
C8633AE41B0A9FF300375D60 /* KVOObservableTests.swift */,
|
||||
C8B5BEA01B4A6A82000D732C /* RxCocoaTests.swift */,
|
||||
C811082B1AF50E2A001C13E4 /* NSNotificationCenterTests.swift */,
|
||||
C81CC92A1B513FD400915606 /* NSObject+RxTests.swift */,
|
||||
C8B5BEA01B4A6A82000D732C /* RxCocoaTests.swift */,
|
||||
C814CEA61AF642D600E98087 /* UI+RxTests.swift */,
|
||||
);
|
||||
path = RxCocoaTests;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
|
|
@ -608,10 +616,11 @@
|
|||
C84B8FC21B89D0D500C9CCCF /* BagTest.swift in Sources */,
|
||||
C81108511AF50E2A001C13E4 /* HotObservable.swift in Sources */,
|
||||
C81108541AF50E2A001C13E4 /* Observable.Extensions.swift in Sources */,
|
||||
C8E9D2BD1BD422D80079D0DB /* Control+RxTests.swift in Sources */,
|
||||
C897EC3B1B10E000009C2CB0 /* BehaviorSubjectTest.swift in Sources */,
|
||||
C81108681AF50E2A001C13E4 /* QueueTests.swift in Sources */,
|
||||
C81108651AF50E2A001C13E4 /* Observable+SingleTest.swift in Sources */,
|
||||
5E5D10BB1B48355200432B25 /* UIControl+RxTests.swift in Sources */,
|
||||
5E5D10BB1B48355200432B25 /* Control+RxTests+UIKit.swift in Sources */,
|
||||
C897EC4A1B1123DA009C2CB0 /* Observable+MultipleTest+Zip.swift in Sources */,
|
||||
D2AF91971BD2EBB900A008C1 /* MockDisposable.swift in Sources */,
|
||||
C81108571AF50E2A001C13E4 /* Recorded.swift in Sources */,
|
||||
|
|
@ -660,6 +669,7 @@
|
|||
C80DDEDD1BCE9A03006A1832 /* Driver+Test.swift in Sources */,
|
||||
C88BB89F1B07E64B0064D411 /* DisposableTest.swift in Sources */,
|
||||
C88BB8A01B07E64B0064D411 /* Observable+StandardSequenceOperatorsTest.swift in Sources */,
|
||||
C8E9D2C41BD452650079D0DB /* Control+RxTests+Cocoa.swift in Sources */,
|
||||
C88BB8A11B07E64B0064D411 /* Observable+MultipleTest.swift in Sources */,
|
||||
C81CC92C1B513FD400915606 /* NSObject+RxTests.swift in Sources */,
|
||||
D2AF91991BD3D9C600A008C1 /* MockDisposable.swift in Sources */,
|
||||
|
|
@ -683,6 +693,7 @@
|
|||
C88BB8A91B07E64B0064D411 /* Observable+AggregateTest.swift in Sources */,
|
||||
C88BB8AA1B07E64B0064D411 /* MockObserver.swift in Sources */,
|
||||
C80DDEE11BCEE898006A1832 /* MainThreadPrimitiveHotObservable.swift in Sources */,
|
||||
C8E9D2BE1BD422D80079D0DB /* Control+RxTests.swift in Sources */,
|
||||
C8AF26F01B499E5C00131C03 /* DelegateProxyTest.swift in Sources */,
|
||||
C69B65011BA8957C00A7FA73 /* ObserverTests.swift in Sources */,
|
||||
C8633AE61B0AA0ED00375D60 /* KVOObservableTests.swift in Sources */,
|
||||
|
|
@ -729,8 +740,9 @@
|
|||
D2EBEB5F1BB9B7DB003A27DC /* Subscription.swift in Sources */,
|
||||
D2EBEB691BB9B7EF003A27DC /* DelegateProxyTest.swift in Sources */,
|
||||
D2EBEB701BB9B7EF003A27DC /* Observable+MultipleTest.swift in Sources */,
|
||||
D203C5141BB9C54A00D02D00 /* UIControl+RxTests.swift in Sources */,
|
||||
D203C5141BB9C54A00D02D00 /* Control+RxTests+UIKit.swift in Sources */,
|
||||
D2EBEB581BB9B7CC003A27DC /* TestObservable.swift in Sources */,
|
||||
C8E9D2BF1BD422D80079D0DB /* Control+RxTests.swift in Sources */,
|
||||
D2EBEB661BB9B7EF003A27DC /* BehaviorSubjectTest.swift in Sources */,
|
||||
D2EBEB671BB9B7EF003A27DC /* CompositeObserverTest.swift in Sources */,
|
||||
D2EBEB7A1BB9B804003A27DC /* PerformanceTools.swift in Sources */,
|
||||
|
|
|
|||
Loading…
Reference in New Issue