Adds RxCocoa unit tests.

This commit is contained in:
Krunoslav Zaher 2015-10-19 00:52:11 +02:00
parent 29b73836b9
commit e22cf3dfb8
14 changed files with 452 additions and 98 deletions

View File

@ -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
}

View File

@ -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
}

View File

@ -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):

View File

@ -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()

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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 }
}
}

View File

@ -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 }
}
}

View File

@ -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)
}
}

View File

@ -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")
}
}

View File

@ -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 */,