1
+ //
2
+ // RetainCycleTests.swift
3
+ // SwiftTask
4
+ //
5
+ // Created by Yasuhiro Inami on 2014/09/11.
6
+ // Copyright (c) 2014年 Yasuhiro Inami. All rights reserved.
7
+ //
8
+
9
+ import SwiftTask
10
+ import XCTest
11
+
12
+ class Player
13
+ {
14
+ var completionHandler : ( Void -> Void ) ?
15
+
16
+ deinit
17
+ {
18
+ println ( " deinit: Player " )
19
+ }
20
+
21
+ func doSomething( completion: ( Void -> Void ) ? = nil )
22
+ {
23
+ dispatch_after ( dispatch_time ( DISPATCH_TIME_NOW, 100_000_000 ) , dispatch_get_main_queue ( ) ) { /*[weak self] in */
24
+
25
+ // NOTE: callback (either as argument or stored property) must be captured by dispatch_queue
26
+
27
+ if let completion = completion {
28
+ completion ( ) // NOTE: interestingly, self is also captured by just calling completion() if [weak self] is not set
29
+ }
30
+ else {
31
+ self . completionHandler ? ( )
32
+ }
33
+ }
34
+ }
35
+
36
+ func cancel( )
37
+ {
38
+ // no operation, just for capturing test
39
+ }
40
+ }
41
+
42
+ class RetainCycleTests : _TestCase
43
+ {
44
+ typealias Task = SwiftTask . Task < Float , String , ErrorString >
45
+
46
+ // weak properties for inspection
47
+ weak var task : Task ?
48
+ weak var player : Player ?
49
+
50
+ func _testPlayer_completionAsArgument_notConfigured( )
51
+ {
52
+ var expect = self . expectationWithDescription ( __FUNCTION__)
53
+
54
+ //
55
+ // retain cycle:
56
+ // ("x->" = will be released shortly)
57
+ //
58
+ // 1. dispatch_queue x-> task
59
+ // dispatch_queue (via player impl) x-> completion -> fulfill -> task
60
+ //
61
+ // 2. dispatch_queue x-> player
62
+ // dispatch_queue (via player impl) x-> player (via completion capturing)
63
+ //
64
+ self . task = Task { ( progress, fulfill, reject, configure) in
65
+
66
+ let player = Player ( )
67
+ self . player = player
68
+
69
+ // comment-out: no configuration test
70
+ // configure.cancel = { player.cancel() }
71
+
72
+ player. doSomething {
73
+ fulfill ( " OK " )
74
+ }
75
+
76
+ }
77
+
78
+ XCTAssertNotNil ( self . task, " self.task (weak) should NOT be nil because of retain cycle: task <- dispatch_queue. " )
79
+ XCTAssertNotNil ( self . player, " self.player (weak) should NOT nil because player is not retained by dispatch_queue. " )
80
+
81
+ println ( " then " )
82
+
83
+ self . task!. then { ( value: String ) -> Void in
84
+
85
+ XCTAssertEqual ( value, " OK " )
86
+ expect. fulfill ( )
87
+
88
+ }
89
+
90
+ self . wait {
91
+ XCTAssertNil ( self . task)
92
+ XCTAssertNil ( self . player)
93
+ }
94
+ }
95
+
96
+ func _testPlayer_completionAsArgument_configured( )
97
+ {
98
+ var expect = self . expectationWithDescription ( __FUNCTION__)
99
+
100
+ //
101
+ // retain cycle:
102
+ // ("x->" = will be released shortly)
103
+ //
104
+ // 1. dispatch_queue x-> task
105
+ // dispatch_queue (via player impl) x-> completion -> fulfill -> task
106
+ //
107
+ // 2. dispatch_queue x-> player
108
+ // dispatch_queue (via player impl) x-> player (via completion capturing)
109
+ //
110
+ // 3. task -> player
111
+ // task -> task.machine -> configure (via pause/resume addEventHandler) -> configure.cancel -> player
112
+ //
113
+ self . task = Task { ( progress, fulfill, reject, configure) in
114
+
115
+ let player = Player ( )
116
+ self . player = player
117
+
118
+ configure. cancel = { player. cancel ( ) }
119
+
120
+ player. doSomething {
121
+ fulfill ( " OK " )
122
+ }
123
+
124
+ }
125
+
126
+ XCTAssertNotNil ( self . task, " self.task (weak) should NOT be nil because of retain cycle: task <- dispatch_queue. " )
127
+ XCTAssertNotNil ( self . player, " self.player (weak) should NOT be nil because of retain cycle: player <- configure <- task. " )
128
+
129
+ println ( " then " )
130
+
131
+ self . task!. then { ( value: String ) -> Void in
132
+
133
+ XCTAssertEqual ( value, " OK " )
134
+ expect. fulfill ( )
135
+
136
+ }
137
+
138
+ self . wait {
139
+ XCTAssertNil ( self . task)
140
+ XCTAssertNil ( self . player)
141
+ }
142
+ }
143
+
144
+ func _testPlayer_completionAsStoredProperty_notConfigured( )
145
+ {
146
+ var expect = self . expectationWithDescription ( __FUNCTION__)
147
+
148
+ //
149
+ // retain cycle:
150
+ // ("x->" = will be released shortly)
151
+ //
152
+ // 1. dispatch_queue x-> player -> task
153
+ // dispatch_queue (via player impl) x-> player -> player.completionHandler -> fulfill -> task
154
+ //
155
+ self . task = Task { ( progress, fulfill, reject, configure) in
156
+
157
+ let player = Player ( )
158
+ self . player = player
159
+
160
+ // comment-out: no configuration test
161
+ // configure.cancel = { player.cancel() }
162
+
163
+ player. completionHandler = {
164
+ fulfill ( " OK " )
165
+ }
166
+ player. doSomething ( )
167
+
168
+ }
169
+
170
+ XCTAssertNotNil ( self . task, " self.task (weak) should not be nil because of retain cycle: task <- player <- dispatch_queue. " )
171
+ XCTAssertNotNil ( self . player, " self.player (weak) should not be nil because of retain cycle: player <- configure <- task. " )
172
+
173
+ println ( " then " )
174
+
175
+ self . task!. then { ( value: String ) -> Void in
176
+
177
+ XCTAssertEqual ( value, " OK " )
178
+ expect. fulfill ( )
179
+
180
+ }
181
+
182
+ self . wait {
183
+ XCTAssertNil ( self . task)
184
+ XCTAssertNil ( self . player)
185
+ }
186
+ }
187
+
188
+ func testPlayer_completionAsStoredProperty_configured( )
189
+ {
190
+ var expect = self . expectationWithDescription ( __FUNCTION__)
191
+
192
+ //
193
+ // retain cycle:
194
+ // ("x->" = will be released shortly)
195
+ //
196
+ // 1. dispatch_queue x-> player -> task
197
+ // dispatch_queue (via player impl) -> player -> player.completionHandler -> fulfill -> task
198
+ //
199
+ // 2. task -> player
200
+ // task -> task.machine -> configure (via pause/resume addEventHandler) -> configure.pause/resume/cancel -> player
201
+ //
202
+ self . task = Task { ( progress, fulfill, reject, configure) in
203
+
204
+ let player = Player ( )
205
+ self . player = player
206
+
207
+ configure. cancel = { player. cancel ( ) }
208
+
209
+ player. completionHandler = {
210
+ fulfill ( " OK " )
211
+ }
212
+ player. doSomething ( )
213
+
214
+ }
215
+
216
+ XCTAssertNotNil ( self . task, " self.task (weak) should not be nil because of retain cycle: task <- player <- dispatch_queue. " )
217
+ XCTAssertNotNil ( self . player, " self.player (weak) should not be nil because of retain cycle: player <- configure <- task. " )
218
+
219
+ println ( " then " )
220
+
221
+ self . task!. then { ( value: String ) -> Void in
222
+
223
+ XCTAssertEqual ( value, " OK " )
224
+ expect. fulfill ( )
225
+
226
+ }
227
+
228
+ // TODO: this test will fail
229
+ self . wait {
230
+ XCTAssertNil ( self . task)
231
+ XCTAssertNil ( self . player)
232
+ }
233
+ }
234
+ }
0 commit comments