SignalLifetimeSpec.swift 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. //
  2. // SignalLifetimeSpec.swift
  3. // ReactiveCocoa
  4. //
  5. // Created by Vadim Yelagin on 2015-12-13.
  6. // Copyright (c) 2015 GitHub. All rights reserved.
  7. //
  8. import Result
  9. import Nimble
  10. import Quick
  11. import ReactiveCocoa
  12. class SignalLifetimeSpec: QuickSpec {
  13. override func spec() {
  14. describe("init") {
  15. var testScheduler: TestScheduler!
  16. beforeEach {
  17. testScheduler = TestScheduler()
  18. }
  19. it("should deallocate") {
  20. weak var signal: Signal<AnyObject, NoError>? = Signal { _ in nil }
  21. expect(signal).to(beNil())
  22. }
  23. it("should deallocate even if it has an observer") {
  24. weak var signal: Signal<AnyObject, NoError>? = {
  25. let signal: Signal<AnyObject, NoError> = Signal { _ in nil }
  26. return signal
  27. }()
  28. expect(signal).to(beNil())
  29. }
  30. it("should deallocate even if it has an observer with retained disposable") {
  31. var disposable: Disposable? = nil
  32. weak var signal: Signal<AnyObject, NoError>? = {
  33. let signal: Signal<AnyObject, NoError> = Signal { _ in nil }
  34. disposable = signal.observe(Observer())
  35. return signal
  36. }()
  37. expect(signal).to(beNil())
  38. disposable?.dispose()
  39. expect(signal).to(beNil())
  40. }
  41. it("should deallocate after erroring") {
  42. weak var signal: Signal<AnyObject, TestError>? = Signal { observer in
  43. testScheduler.schedule {
  44. observer.sendFailed(TestError.Default)
  45. }
  46. return nil
  47. }
  48. var errored = false
  49. signal?.observeFailed { _ in errored = true }
  50. expect(errored) == false
  51. expect(signal).toNot(beNil())
  52. testScheduler.run()
  53. expect(errored) == true
  54. expect(signal).to(beNil())
  55. }
  56. it("should deallocate after completing") {
  57. weak var signal: Signal<AnyObject, NoError>? = Signal { observer in
  58. testScheduler.schedule {
  59. observer.sendCompleted()
  60. }
  61. return nil
  62. }
  63. var completed = false
  64. signal?.observeCompleted { completed = true }
  65. expect(completed) == false
  66. expect(signal).toNot(beNil())
  67. testScheduler.run()
  68. expect(completed) == true
  69. expect(signal).to(beNil())
  70. }
  71. it("should deallocate after interrupting") {
  72. weak var signal: Signal<AnyObject, NoError>? = Signal { observer in
  73. testScheduler.schedule {
  74. observer.sendInterrupted()
  75. }
  76. return nil
  77. }
  78. var interrupted = false
  79. signal?.observeInterrupted {
  80. interrupted = true
  81. }
  82. expect(interrupted) == false
  83. expect(signal).toNot(beNil())
  84. testScheduler.run()
  85. expect(interrupted) == true
  86. expect(signal).to(beNil())
  87. }
  88. }
  89. describe("Signal.pipe") {
  90. it("should deallocate") {
  91. weak var signal = Signal<(), NoError>.pipe().0
  92. expect(signal).to(beNil())
  93. }
  94. it("should deallocate after erroring") {
  95. let testScheduler = TestScheduler()
  96. weak var weakSignal: Signal<(), TestError>?
  97. // Use an inner closure to help ARC deallocate things as we
  98. // expect.
  99. let test = {
  100. let (signal, observer) = Signal<(), TestError>.pipe()
  101. weakSignal = signal
  102. testScheduler.schedule {
  103. observer.sendFailed(TestError.Default)
  104. }
  105. }
  106. test()
  107. expect(weakSignal).toNot(beNil())
  108. testScheduler.run()
  109. expect(weakSignal).to(beNil())
  110. }
  111. it("should deallocate after completing") {
  112. let testScheduler = TestScheduler()
  113. weak var weakSignal: Signal<(), TestError>?
  114. // Use an inner closure to help ARC deallocate things as we
  115. // expect.
  116. let test = {
  117. let (signal, observer) = Signal<(), TestError>.pipe()
  118. weakSignal = signal
  119. testScheduler.schedule {
  120. observer.sendCompleted()
  121. }
  122. }
  123. test()
  124. expect(weakSignal).toNot(beNil())
  125. testScheduler.run()
  126. expect(weakSignal).to(beNil())
  127. }
  128. it("should deallocate after interrupting") {
  129. let testScheduler = TestScheduler()
  130. weak var weakSignal: Signal<(), NoError>?
  131. let test = {
  132. let (signal, observer) = Signal<(), NoError>.pipe()
  133. weakSignal = signal
  134. testScheduler.schedule {
  135. observer.sendInterrupted()
  136. }
  137. }
  138. test()
  139. expect(weakSignal).toNot(beNil())
  140. testScheduler.run()
  141. expect(weakSignal).to(beNil())
  142. }
  143. }
  144. describe("testTransform") {
  145. it("should deallocate") {
  146. weak var signal: Signal<AnyObject, NoError>? = Signal { _ in nil }.testTransform()
  147. expect(signal).to(beNil())
  148. }
  149. it("should deallocate even if it has an observer") {
  150. weak var signal: Signal<AnyObject, NoError>? = {
  151. let signal: Signal<AnyObject, NoError> = Signal { _ in nil }.testTransform()
  152. signal.observe(Observer())
  153. return signal
  154. }()
  155. expect(signal).to(beNil())
  156. }
  157. it("should deallocate even if it has an observer with retained disposable") {
  158. var disposable: Disposable? = nil
  159. weak var signal: Signal<AnyObject, NoError>? = {
  160. let signal: Signal<AnyObject, NoError> = Signal { _ in nil }.testTransform()
  161. disposable = signal.observe(Observer())
  162. return signal
  163. }()
  164. expect(signal).to(beNil())
  165. disposable?.dispose()
  166. expect(signal).to(beNil())
  167. }
  168. }
  169. describe("observe") {
  170. var signal: Signal<Int, TestError>!
  171. var observer: Signal<Int, TestError>.Observer!
  172. var token: NSObject? = nil
  173. weak var weakToken: NSObject?
  174. func expectTokenNotDeallocated() {
  175. expect(weakToken).toNot(beNil())
  176. }
  177. func expectTokenDeallocated() {
  178. expect(weakToken).to(beNil())
  179. }
  180. beforeEach {
  181. let (signalTemp, observerTemp) = Signal<Int, TestError>.pipe()
  182. signal = signalTemp
  183. observer = observerTemp
  184. token = NSObject()
  185. weakToken = token
  186. signal.observe { [token = token] _ in
  187. _ = token!.description
  188. }
  189. }
  190. it("should deallocate observe handler when signal completes") {
  191. expectTokenNotDeallocated()
  192. observer.sendNext(1)
  193. expectTokenNotDeallocated()
  194. token = nil
  195. expectTokenNotDeallocated()
  196. observer.sendNext(2)
  197. expectTokenNotDeallocated()
  198. observer.sendCompleted()
  199. expectTokenDeallocated()
  200. }
  201. it("should deallocate observe handler when signal fails") {
  202. expectTokenNotDeallocated()
  203. observer.sendNext(1)
  204. expectTokenNotDeallocated()
  205. token = nil
  206. expectTokenNotDeallocated()
  207. observer.sendNext(2)
  208. expectTokenNotDeallocated()
  209. observer.sendFailed(.Default)
  210. expectTokenDeallocated()
  211. }
  212. }
  213. }
  214. }
  215. private extension SignalType {
  216. func testTransform() -> Signal<Value, Error> {
  217. return Signal { observer in
  218. return self.observe(observer.action)
  219. }
  220. }
  221. }