libuv 0.10.0 → 0.10.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/.gitignore +17 -17
  2. data/.gitmodules +3 -3
  3. data/.rspec +1 -1
  4. data/.travis.yml +16 -16
  5. data/Gemfile +2 -2
  6. data/LICENSE +23 -23
  7. data/README.md +82 -73
  8. data/Rakefile +31 -31
  9. data/lib/libuv.rb +53 -34
  10. data/lib/libuv/async.rb +47 -33
  11. data/lib/libuv/check.rb +55 -48
  12. data/lib/libuv/error.rb +70 -70
  13. data/lib/libuv/ext/ext.rb +264 -256
  14. data/lib/libuv/ext/platform/darwin_x64.rb +12 -12
  15. data/lib/libuv/ext/platform/linux.rb +7 -7
  16. data/lib/libuv/ext/platform/unix.rb +13 -13
  17. data/lib/libuv/ext/platform/windows.rb +26 -26
  18. data/lib/libuv/ext/tasks.rb +27 -27
  19. data/lib/libuv/ext/tasks/mac.rb +23 -23
  20. data/lib/libuv/ext/tasks/unix.rb +23 -23
  21. data/lib/libuv/ext/tasks/win.rb +11 -11
  22. data/lib/libuv/ext/types.rb +234 -229
  23. data/lib/libuv/file.rb +192 -0
  24. data/lib/libuv/filesystem.rb +233 -0
  25. data/lib/libuv/fs_event.rb +31 -31
  26. data/lib/libuv/handle.rb +85 -81
  27. data/lib/libuv/idle.rb +56 -49
  28. data/lib/libuv/loop.rb +338 -310
  29. data/lib/libuv/{assertions.rb → mixins/assertions.rb} +23 -23
  30. data/lib/libuv/mixins/fs_checks.rb +55 -0
  31. data/lib/libuv/{listener.rb → mixins/listener.rb} +34 -34
  32. data/lib/libuv/{net.rb → mixins/net.rb} +37 -37
  33. data/lib/libuv/{resource.rb → mixins/resource.rb} +27 -27
  34. data/lib/libuv/{stream.rb → mixins/stream.rb} +143 -123
  35. data/lib/libuv/pipe.rb +197 -97
  36. data/lib/libuv/prepare.rb +56 -49
  37. data/lib/libuv/q.rb +1 -1
  38. data/lib/libuv/signal.rb +51 -0
  39. data/lib/libuv/tcp.rb +204 -193
  40. data/lib/libuv/timer.rb +88 -75
  41. data/lib/libuv/tty.rb +37 -34
  42. data/lib/libuv/udp.rb +273 -255
  43. data/lib/libuv/version.rb +3 -3
  44. data/lib/libuv/work.rb +63 -61
  45. data/libuv.gemspec +54 -54
  46. data/spec/async_spec.rb +60 -60
  47. data/spec/cpu_spec.rb +10 -0
  48. data/spec/defer_spec.rb +980 -980
  49. data/spec/filesystem_spec.rb +119 -0
  50. data/spec/idle_spec.rb +56 -56
  51. data/spec/pipe_spec.rb +153 -148
  52. data/spec/tcp_spec.rb +203 -188
  53. metadata +73 -49
  54. checksums.yaml +0 -15
  55. data/lib/libuv/simple_async.rb +0 -28
data/lib/libuv/version.rb CHANGED
@@ -1,3 +1,3 @@
1
- module Libuv
2
- VERSION = '0.10.0'
3
- end
1
+ module Libuv
2
+ VERSION = '0.10.2'
3
+ end
data/lib/libuv/work.rb CHANGED
@@ -1,62 +1,64 @@
1
- module Libuv
2
- class Work < Q::DeferredPromise
3
- include Resource, Listener
4
-
5
-
6
- def initialize(loop, work)
7
- super(loop, loop.defer)
8
-
9
- @work = work
10
- @complete = false
11
- @pointer = ::Libuv::Ext.create_request(:uv_work)
12
- @error = nil # error in callback
13
-
14
- error = check_result ::Libuv::Ext.queue_work(@loop, @pointer, callback(:on_work), callback(:on_complete))
15
- if error
16
- ::Libuv::Ext.free(@pointer)
17
- @complete = true
18
- @defer.reject(error)
19
- end
20
- end
21
-
22
- # Attempt to cancel the pending work. Returns true if the work has completed or was canceled.
23
- #
24
- # @return [true, false]
25
- def cancel
26
- if not @complete
27
- @complete = ::Libuv::Ext.cancel(@pointer) >= 0
28
- end
29
- @complete
30
- end
31
-
32
- # Indicates is the work has completed yet or not.
33
- #
34
- # @return [true, false]
35
- def completed?
36
- return @complete
37
- end
38
-
39
-
40
- private
41
-
42
-
43
- def on_complete(req, status)
44
- @complete = true
45
- ::Libuv::Ext.free(req)
46
-
47
- if @error
48
- @defer.reject(@error)
49
- else
50
- resolve @defer, status
51
- end
52
- end
53
-
54
- def on_work(req)
55
- begin
56
- @work.call
57
- rescue Exception => e
58
- @error = e # Catch errors for promise resolution
59
- end
60
- end
61
- end
1
+ module Libuv
2
+ class Work < Q::DeferredPromise
3
+ include Resource, Listener
4
+
5
+
6
+ # @param loop [::Libuv::Loop] loop this work request will be associated
7
+ # @param work [Proc] callback to be called in the thread pool
8
+ def initialize(loop, work)
9
+ super(loop, loop.defer)
10
+
11
+ @work = work
12
+ @complete = false
13
+ @pointer = ::Libuv::Ext.create_request(:uv_work)
14
+ @error = nil # error in callback
15
+
16
+ error = check_result ::Libuv::Ext.queue_work(@loop, @pointer, callback(:on_work), callback(:on_complete))
17
+ if error
18
+ ::Libuv::Ext.free(@pointer)
19
+ @complete = true
20
+ @defer.reject(error)
21
+ end
22
+ end
23
+
24
+ # Attempt to cancel the pending work. Returns true if the work has completed or was canceled.
25
+ #
26
+ # @return [true, false]
27
+ def cancel
28
+ if not @complete
29
+ @complete = ::Libuv::Ext.cancel(@pointer) >= 0
30
+ end
31
+ @complete
32
+ end
33
+
34
+ # Indicates is the work has completed yet or not.
35
+ #
36
+ # @return [true, false]
37
+ def completed?
38
+ return @complete
39
+ end
40
+
41
+
42
+ private
43
+
44
+
45
+ def on_complete(req, status)
46
+ @complete = true
47
+ ::Libuv::Ext.free(req)
48
+
49
+ if @error
50
+ @defer.reject(@error)
51
+ else
52
+ resolve @defer, status
53
+ end
54
+ end
55
+
56
+ def on_work(req)
57
+ begin
58
+ @work.call
59
+ rescue Exception => e
60
+ @error = e # Catch errors for promise resolution
61
+ end
62
+ end
63
+ end
62
64
  end
data/libuv.gemspec CHANGED
@@ -1,54 +1,54 @@
1
- require File.expand_path("../lib/libuv/version", __FILE__)
2
-
3
- Gem::Specification.new do |gem|
4
- gem.name = "libuv"
5
- gem.version = Libuv::VERSION
6
- gem.license = 'MIT'
7
- gem.authors = ["Bulat Shakirzyanov", "Stephen von Takach"]
8
- gem.email = ["mallluhuct@gmail.com", "steve@cotag.me"]
9
- gem.homepage = "https://github.com/cotag/libuv"
10
- gem.summary = "libuv bindings for Ruby"
11
- gem.description = "An opinionated wrapper around libuv for Ruby"
12
-
13
- gem.extensions << "ext/Rakefile"
14
-
15
- gem.required_ruby_version = '>= 1.9.2'
16
- gem.require_paths = ["lib"]
17
-
18
- gem.add_runtime_dependency 'ffi', '>= 1.9'
19
- gem.add_runtime_dependency 'thread_safe'
20
- gem.add_development_dependency 'rspec', '>= 2.14'
21
- gem.add_development_dependency 'rake', '>= 10.1'
22
- gem.add_development_dependency 'yard'
23
-
24
- gem.files = `git ls-files`.split("\n")
25
- gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
26
-
27
- # Add the submodule to the gem
28
- relative_path = File.expand_path("../", __FILE__) + '/'
29
- `git submodule --quiet foreach pwd`.split($\).each do |submodule_path|
30
-
31
- if (ENV['OS'] == 'Windows_NT') && submodule_path[0] == '/'
32
- # Detect if cygwin path is being used by git
33
- submodule_path = submodule_path[1..-1]
34
- submodule_path.insert(1, ':')
35
- end
36
-
37
- # for each submodule, change working directory to that submodule
38
- Dir.chdir(submodule_path) do
39
- # Make the submodule path relative
40
- submodule_path = submodule_path.gsub(/#{relative_path}/i, '')
41
-
42
- # issue git ls-files in submodule's directory
43
- submodule_files = `git ls-files`.split($\)
44
-
45
- # prepend the submodule path to create relative file paths
46
- submodule_files_paths = submodule_files.map do |filename|
47
- File.join(submodule_path, filename)
48
- end
49
-
50
- # add relative paths to gem.files
51
- gem.files += submodule_files_paths
52
- end
53
- end
54
- end
1
+ require File.expand_path("../lib/libuv/version", __FILE__)
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.name = "libuv"
5
+ gem.version = Libuv::VERSION
6
+ gem.license = 'MIT'
7
+ gem.authors = ["Bulat Shakirzyanov", "Stephen von Takach"]
8
+ gem.email = ["mallluhuct@gmail.com", "steve@cotag.me"]
9
+ gem.homepage = "https://github.com/cotag/libuv"
10
+ gem.summary = "libuv bindings for Ruby"
11
+ gem.description = "An opinionated wrapper around libuv for Ruby"
12
+
13
+ gem.extensions << "ext/Rakefile"
14
+
15
+ gem.required_ruby_version = '>= 1.9.2'
16
+ gem.require_paths = ["lib"]
17
+
18
+ gem.add_runtime_dependency 'ffi', '>= 1.9'
19
+ gem.add_runtime_dependency 'thread_safe'
20
+ gem.add_development_dependency 'rspec', '>= 2.14'
21
+ gem.add_development_dependency 'rake', '>= 10.1'
22
+ gem.add_development_dependency 'yard'
23
+
24
+ gem.files = `git ls-files`.split("\n")
25
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
26
+
27
+ # Add the submodule to the gem
28
+ relative_path = File.expand_path("../", __FILE__) + '/'
29
+ `git submodule --quiet foreach pwd`.split($\).each do |submodule_path|
30
+
31
+ if (ENV['OS'] == 'Windows_NT') && submodule_path[0] == '/'
32
+ # Detect if cygwin path is being used by git
33
+ submodule_path = submodule_path[1..-1]
34
+ submodule_path.insert(1, ':')
35
+ end
36
+
37
+ # for each submodule, change working directory to that submodule
38
+ Dir.chdir(submodule_path) do
39
+ # Make the submodule path relative
40
+ submodule_path = submodule_path.gsub(/#{relative_path}/i, '')
41
+
42
+ # issue git ls-files in submodule's directory
43
+ submodule_files = `git ls-files`.split($\)
44
+
45
+ # prepend the submodule path to create relative file paths
46
+ submodule_files_paths = submodule_files.map do |filename|
47
+ File.join(submodule_path, filename)
48
+ end
49
+
50
+ # add relative paths to gem.files
51
+ gem.files += submodule_files_paths
52
+ end
53
+ end
54
+ end
data/spec/async_spec.rb CHANGED
@@ -1,60 +1,60 @@
1
- require 'libuv'
2
-
3
-
4
- describe Libuv::Async do
5
- before :each do
6
- @log = []
7
- @general_failure = []
8
-
9
- @loop = Libuv::Loop.new
10
- @call = @loop.pipe
11
- @timeout = @loop.timer do
12
- @loop.stop
13
- @general_failure << "test timed out"
14
- end
15
- @timeout.start(5000)
16
-
17
- @loop.all(@server, @client, @timeout).catch do |reason|
18
- @general_failure << reason.inspect
19
- p "Failed with: #{reason.message}\n#{reason.backtrace.join("\n")}\n"
20
- end
21
- end
22
-
23
- it "Should call the async function from the thread pool stopping the counter" do
24
- @loop.run { |logger|
25
- logger.progress do |level, errorid, error|
26
- begin
27
- p "Log called: #{level}: #{errorid}\n#{error.message}\n#{error.backtrace.join("\n")}\n"
28
- rescue Exception
29
- p 'error in logger'
30
- end
31
- end
32
-
33
- @count = 0
34
-
35
- timer = @loop.timer do
36
- @count += 1
37
- end
38
- timer.start(0, 200)
39
-
40
- callback = @loop.async do
41
- stopper = @loop.timer do
42
- timer.close
43
- callback.close
44
- stopper.close
45
- @loop.stop
46
- end
47
- stopper.start(1000)
48
- end
49
-
50
- @loop.work(proc {
51
- callback.call
52
- }).catch do |err|
53
- @general_failure << err
54
- end
55
- }
56
-
57
- @general_failure.should == []
58
- (@count < 7 && @count > 3).should == true
59
- end
60
- end
1
+ require 'libuv'
2
+
3
+
4
+ describe Libuv::Async do
5
+ before :each do
6
+ @log = []
7
+ @general_failure = []
8
+
9
+ @loop = Libuv::Loop.new
10
+ @call = @loop.pipe
11
+ @timeout = @loop.timer do
12
+ @loop.stop
13
+ @general_failure << "test timed out"
14
+ end
15
+ @timeout.start(5000)
16
+
17
+ @loop.all(@server, @client, @timeout).catch do |reason|
18
+ @general_failure << reason.inspect
19
+ p "Failed with: #{reason.message}\n#{reason.backtrace.join("\n")}\n"
20
+ end
21
+ end
22
+
23
+ it "Should call the async function from the thread pool stopping the counter" do
24
+ @loop.run { |logger|
25
+ logger.progress do |level, errorid, error|
26
+ begin
27
+ p "Log called: #{level}: #{errorid}\n#{error.message}\n#{error.backtrace.join("\n")}\n"
28
+ rescue Exception
29
+ p 'error in logger'
30
+ end
31
+ end
32
+
33
+ @count = 0
34
+
35
+ timer = @loop.timer do
36
+ @count += 1
37
+ end
38
+ timer.start(0, 200)
39
+
40
+ callback = @loop.async do
41
+ stopper = @loop.timer do
42
+ timer.close
43
+ callback.close
44
+ stopper.close
45
+ @loop.stop
46
+ end
47
+ stopper.start(1000)
48
+ end
49
+
50
+ @loop.work(proc {
51
+ callback.call
52
+ }).catch do |err|
53
+ @general_failure << err
54
+ end
55
+ }
56
+
57
+ @general_failure.should == []
58
+ (@count < 7 && @count > 3).should == true
59
+ end
60
+ end
data/spec/cpu_spec.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'libuv'
2
+
3
+
4
+ describe ::Libuv do
5
+ it "Should return the number of CPU cores on the platform" do
6
+ count = Libuv.cpu_count
7
+
8
+ (count != nil && count > 0).should == true
9
+ end
10
+ end
data/spec/defer_spec.rb CHANGED
@@ -1,980 +1,980 @@
1
- require 'libuv'
2
-
3
-
4
- describe Libuv::Q do
5
-
6
- before :each do
7
- @loop = Libuv::Loop.new
8
- @deferred = @loop.defer
9
- @promise = @deferred.promise
10
- @log = []
11
- @default_fail = proc { |reason|
12
- @loop.stop
13
- }
14
- end
15
-
16
-
17
- describe 'resolve' do
18
-
19
-
20
- it "should call the callback in the next turn" do
21
- @loop.run {
22
- @promise.then nil, @default_fail do |result|
23
- @log << result
24
- end
25
-
26
- @deferred.resolve(:foo)
27
-
28
- @loop.next_tick do
29
- @loop.stop
30
- end
31
- }
32
-
33
- @log.should == [:foo]
34
- end
35
-
36
-
37
-
38
- it "should be able to resolve the callback after it has already been resolved" do
39
- @loop.run {
40
- @promise.then nil, @default_fail do |result|
41
- @log << result
42
- @promise.then nil, @default_fail do |result|
43
- @log << result
44
- end
45
- end
46
-
47
- @deferred.resolve(:foo)
48
-
49
- @loop.next_tick do
50
- @loop.next_tick do
51
- @loop.stop
52
- end
53
- end
54
- }
55
- @log.should == [:foo, :foo]
56
- end
57
-
58
-
59
-
60
- it "should fulfill success callbacks in the registration order" do
61
- @loop.run {
62
- @promise.then nil, @default_fail do |result|
63
- @log << :first
64
- end
65
-
66
- @promise.then nil, @default_fail do |result|
67
- @log << :second
68
- end
69
-
70
- @deferred.resolve(:foo)
71
-
72
- @loop.next_tick do
73
- @loop.stop
74
- end
75
- }
76
- @log.should == [:first, :second]
77
- end
78
-
79
-
80
- it "should do nothing if a promise was previously resolved" do
81
- @loop.run {
82
- @promise.then nil, @default_fail do |result|
83
- @log << result
84
- @log.should == [:foo]
85
- @deferred.resolve(:bar)
86
- end
87
-
88
- @deferred.resolve(:foo)
89
- @deferred.reject(:baz)
90
-
91
- #
92
- # 4 ticks should detect any errors
93
- #
94
- @loop.next_tick do
95
- @loop.next_tick do
96
- @loop.next_tick do
97
- @loop.next_tick do
98
- @loop.stop
99
- end
100
- end
101
- end
102
- end
103
- }
104
- @log.should == [:foo]
105
- end
106
-
107
-
108
- it "should allow deferred resolution with a new promise" do
109
- deferred2 = @loop.defer
110
- @loop.run {
111
- @promise.then nil, @default_fail do |result|
112
- @log << result
113
- @loop.stop
114
- end
115
-
116
- @deferred.resolve(deferred2.promise)
117
- deferred2.resolve(:foo)
118
- }
119
- @log.should == [:foo]
120
- end
121
-
122
-
123
- it "should not break if a callbacks registers another callback" do
124
- @loop.run {
125
- @promise.then nil, @default_fail do |result|
126
- @log << :outer
127
- @promise.then nil, @default_fail do |result|
128
- @log << :inner
129
- end
130
- end
131
-
132
- @deferred.resolve(:foo)
133
-
134
- @loop.next_tick do
135
- @loop.next_tick do
136
- @loop.stop
137
- end
138
- end
139
- }
140
-
141
- @log.should == [:outer, :inner]
142
- end
143
-
144
-
145
-
146
- it "can modify the result of a promise before returning" do
147
- @loop.run {
148
- proc { |name|
149
- @loop.work { @deferred.resolve("Hello #{name}") }
150
- @promise.then nil, @default_fail do |result|
151
- @log << result
152
- result += "?"
153
- result
154
- end
155
- }.call('Robin Hood').then nil, @default_fail do |greeting|
156
- @log << greeting
157
- @loop.stop
158
- end
159
- }
160
-
161
- @log.should == ['Hello Robin Hood', 'Hello Robin Hood?']
162
- end
163
-
164
- end
165
-
166
-
167
- describe 'reject' do
168
-
169
- it "should reject the promise and execute all error callbacks" do
170
- @loop.run {
171
- @promise.then(@default_fail, proc {|result|
172
- @log << :first
173
- })
174
- @promise.then(@default_fail, proc {|result|
175
- @log << :second
176
- })
177
-
178
- @deferred.reject(:foo)
179
-
180
- @loop.next_tick do
181
- @loop.stop
182
- end
183
- }
184
- @log.should == [:first, :second]
185
- end
186
-
187
-
188
- it "should do nothing if a promise was previously rejected" do
189
- @loop.run {
190
- @promise.then(@default_fail, proc {|result|
191
- @log << result
192
- @deferred.resolve(:bar)
193
- })
194
-
195
- @deferred.reject(:baz)
196
- @deferred.resolve(:foo)
197
-
198
- #
199
- # 4 ticks should detect any errors
200
- #
201
- @loop.next_tick do
202
- @loop.next_tick do
203
- @loop.next_tick do
204
- @loop.next_tick do
205
- @loop.stop
206
- end
207
- end
208
- end
209
- end
210
- }
211
- @log.should == [:baz]
212
- end
213
-
214
-
215
- it "should not defer rejection with a new promise" do
216
- deferred2 = @loop.defer
217
- @loop.run {
218
- @promise.then(@default_fail, @default_fail)
219
- begin
220
- @deferred.reject(deferred2.promise)
221
- rescue => e
222
- @log << e.is_a?(ArgumentError)
223
- @loop.stop
224
- end
225
- }
226
-
227
- @log.should == [true]
228
- end
229
-
230
- end
231
-
232
-
233
- describe 'notify' do
234
- it "should execute all progress callbacks in the registration order" do
235
- @loop.run {
236
- @promise.progress do |update|
237
- @log << :first
238
- end
239
-
240
- @promise.progress do |update|
241
- @log << :second
242
- end
243
-
244
- @deferred.notify(:foo)
245
-
246
- @loop.next_tick do
247
- @loop.stop
248
- end
249
- }
250
-
251
- @log.should == [:first, :second]
252
- end
253
-
254
- it "should do nothing if a promise was previously resolved" do
255
- @loop.run {
256
-
257
- @promise.progress do |update|
258
- @log << update
259
- end
260
-
261
- @deferred.resolve(:foo)
262
- @deferred.notify(:baz)
263
-
264
-
265
- #
266
- # 4 ticks should detect any errors
267
- #
268
- @loop.next_tick do
269
- @loop.next_tick do
270
- @loop.next_tick do
271
- @loop.next_tick do
272
- @loop.stop
273
- end
274
- end
275
- end
276
- end
277
- }
278
-
279
- @log.should == []
280
- end
281
-
282
- it "should do nothing if a promise was previously rejected" do
283
- @loop.run {
284
-
285
- @promise.progress do |update|
286
- @log << update
287
- end
288
- @deferred.reject(:foo)
289
- @deferred.notify(:baz)
290
-
291
-
292
-
293
- #
294
- # 4 ticks should detect any errors
295
- #
296
- @loop.next_tick do
297
- @loop.next_tick do
298
- @loop.next_tick do
299
- @loop.next_tick do
300
- @loop.stop
301
- end
302
- end
303
- end
304
- end
305
- }
306
-
307
- @log.should == []
308
- end
309
-
310
-
311
- it "should not apply any special treatment to promises passed to notify" do
312
- @loop.run {
313
- deferred2 = @loop.defer
314
-
315
- @promise.progress do |update|
316
- @log << update.is_a?(::Libuv::Q::Promise)
317
- end
318
- @deferred.notify(deferred2.promise)
319
-
320
- @loop.next_tick do
321
- @loop.stop
322
- end
323
- }
324
-
325
- @log.should == [true]
326
- end
327
-
328
-
329
- it "should call the progress callbacks in the next turn" do
330
- @loop.run {
331
- @promise.progress do |update|
332
- @log << :first
333
- end
334
-
335
- @promise.progress do |update|
336
- @log << :second
337
- end
338
-
339
- @deferred.notify(:foo)
340
-
341
- @log << @log.length # Has notify run in this tick
342
- @loop.stop # Stop will run through the next tick before stopping
343
- }
344
-
345
- @log.should == [0, :first, :second]
346
- end
347
-
348
- it "should ignore notifications sent out in the same turn before listener registration" do
349
- @loop.run {
350
- @deferred.notify(:foo)
351
-
352
- @promise.progress do |update|
353
- @log << :first
354
- end
355
-
356
- @promise.progress do |update|
357
- @log << :second
358
- end
359
-
360
- @loop.next_tick do
361
- @loop.stop
362
- end
363
- }
364
-
365
- @log.should == []
366
- end
367
- end
368
-
369
-
370
- describe Libuv::Q::Promise do
371
-
372
- describe 'then' do
373
-
374
- it "should allow registration of a success callback without an errback and resolve" do
375
- @loop.run {
376
- @promise.then do |result|
377
- @log << result
378
- end
379
-
380
- @deferred.resolve(:foo)
381
-
382
- @loop.next_tick do
383
- @loop.stop
384
- end
385
- }
386
-
387
- @log.should == [:foo]
388
- end
389
-
390
-
391
- it "should allow registration of a success callback without an errback and reject" do
392
- @loop.run {
393
- @promise.then do |result|
394
- @log << result
395
- end
396
-
397
- @deferred.reject(:foo)
398
-
399
- @loop.next_tick do
400
- @loop.stop
401
- end
402
- }
403
-
404
- @log.should == []
405
- end
406
-
407
-
408
- it "should allow registration of an errback without a success callback and reject" do
409
- @loop.run {
410
- @promise.catch(proc {|reason|
411
- @log << reason
412
- })
413
-
414
- @deferred.reject(:foo)
415
-
416
- @loop.next_tick do
417
- @loop.stop
418
- end
419
- }
420
-
421
- @log.should == [:foo]
422
- end
423
-
424
-
425
- it "should allow registration of an errback without a success callback and resolve" do
426
- @loop.run {
427
- @promise.catch(proc {|reason|
428
- @log << reason
429
- })
430
-
431
- @deferred.resolve(:foo)
432
-
433
- @loop.next_tick do
434
- @loop.stop
435
- end
436
- }
437
-
438
- @log.should == []
439
- end
440
-
441
-
442
- it "should resolve all callbacks with the original value" do
443
- @loop.run {
444
- @promise.then nil, @default_fail do |result|
445
- @log << result
446
- :alt1
447
- end
448
- @promise.then nil, @default_fail do |result|
449
- @log << result
450
- 'ERROR'
451
- end
452
- @promise.then nil, @default_fail do |result|
453
- @log << result
454
- Libuv::Q.reject(@loop, 'some reason')
455
- end
456
- @promise.then nil, @default_fail do |result|
457
- @log << result
458
- :alt2
459
- end
460
-
461
- @deferred.resolve(:foo)
462
-
463
- @loop.next_tick do
464
- @loop.stop
465
- end
466
- }
467
-
468
- @log.should == [:foo, :foo, :foo, :foo]
469
- end
470
-
471
-
472
- it "should notify all callbacks with the original value" do
473
- @loop.run { |loop_promise|
474
- @promise.progress do |result|
475
- @log << result
476
- :alt1
477
- end
478
- @promise.progress do |result|
479
- @log << result
480
- 'ERROR'
481
- end
482
- @promise.progress do |result|
483
- @log << result
484
- Libuv::Q.reject(@loop, 'some reason')
485
- end
486
- @promise.progress do |result|
487
- @log << result
488
- :alt2
489
- end
490
-
491
-
492
- @deferred.notify(:foo)
493
-
494
- @loop.next_tick do
495
- @loop.next_tick do
496
- @loop.next_tick do
497
- @loop.next_tick do
498
- @loop.next_tick do
499
- @loop.stop
500
- end
501
- end
502
- end
503
- end
504
- end
505
- }
506
-
507
- @log.should == [:foo, :foo, :foo, :foo]
508
- end
509
-
510
-
511
- it "should reject all callbacks with the original reason" do
512
- @loop.run {
513
- @promise.then(@default_fail, proc {|result|
514
- @log << result
515
- :alt1
516
- })
517
- @promise.then(@default_fail, proc {|result|
518
- @log << result
519
- 'ERROR'
520
- })
521
- @promise.then(@default_fail, proc {|result|
522
- @log << result
523
- Libuv::Q.reject(@loop, 'some reason')
524
- })
525
- @promise.then(@default_fail, proc {|result|
526
- @log << result
527
- :alt2
528
- })
529
-
530
- @deferred.reject(:foo)
531
-
532
- @loop.next_tick do
533
- @loop.stop
534
- end
535
- }
536
-
537
- @log.should == [:foo, :foo, :foo, :foo]
538
- end
539
-
540
-
541
- it "should propagate resolution and rejection between dependent promises" do
542
- @loop.run {
543
- @promise.then(proc { |result|
544
- @log << result
545
- :bar
546
- }, @default_fail).then(proc { |result|
547
- @log << result
548
- raise 'baz'
549
- }, @default_fail).then(@default_fail, proc {|result|
550
- @log << result.message
551
- raise 'bob'
552
- }).then(@default_fail, proc {|result|
553
- @log << result.message
554
- :done
555
- }).then(proc { |result|
556
- @log << result
557
- }, @default_fail)
558
-
559
- @deferred.resolve(:foo)
560
-
561
- @loop.next_tick do
562
- @loop.next_tick do
563
- @loop.next_tick do
564
- @loop.next_tick do
565
- @loop.next_tick do
566
- @loop.next_tick do # extra tick?
567
- @loop.stop
568
- end
569
- end
570
- end
571
- end
572
- end
573
- end
574
- }
575
-
576
- @log.should == [:foo, :bar, 'baz', 'bob', :done]
577
- end
578
-
579
-
580
- it "should propagate notification between dependent promises" do
581
- @loop.run { |loop_promise|
582
- loop_promise.progress do |type, id, error|
583
- @log << id
584
- end
585
-
586
-
587
- @promise.progress(proc { |result|
588
- @log << result
589
- :bar
590
- }).progress(proc { |result|
591
- @log << result
592
- result
593
- }).progress(proc {|result|
594
- @log << result
595
- result
596
- }).progress(proc {|result|
597
- @log << result
598
- :done
599
- }).progress(proc { |result|
600
- @log << result
601
- result
602
- })
603
-
604
-
605
- @deferred.notify(:foo)
606
-
607
- @loop.next_tick do
608
- @loop.next_tick do
609
- @loop.next_tick do
610
- @loop.next_tick do
611
- @loop.next_tick do
612
- @loop.next_tick do # extra tick?
613
- @loop.stop
614
- end
615
- end
616
- end
617
- end
618
- end
619
- end
620
- }
621
-
622
- @log.should == [:foo, :bar, :bar, :bar, :done]
623
- end
624
-
625
-
626
- it "should stop notification propagation in case of error" do
627
- @loop.run { |loop_logger|
628
- loop_logger.progress do |type, id, error|
629
- @log << id
630
- end
631
-
632
-
633
- @promise.progress(proc { |result|
634
- @log << result
635
- :bar
636
- }).progress(proc { |result|
637
- @log << result
638
- raise 'err'
639
- result
640
- }).progress(proc {|result|
641
- @log << result
642
- result
643
- }).progress(proc {|result|
644
- @log << result
645
- :done
646
- }).progress(proc { |result|
647
- @log << result
648
- result
649
- })
650
-
651
-
652
- @deferred.notify(:foo)
653
-
654
- @loop.next_tick do
655
- @loop.next_tick do
656
- @loop.next_tick do
657
- @loop.next_tick do
658
- @loop.next_tick do
659
- @loop.stop
660
- end
661
- end
662
- end
663
- end
664
- end
665
- }
666
-
667
- @log.should == [:foo, :bar, :q_progress_cb]
668
- end
669
-
670
-
671
- it "should call error callback in the next turn even if promise is already rejected" do
672
- @loop.run {
673
- @deferred.reject(:foo)
674
-
675
- @promise.catch(proc {|reason|
676
- @log << reason
677
- })
678
-
679
- @loop.next_tick do
680
- @loop.stop
681
- end
682
- }
683
-
684
- @log.should == [:foo]
685
- end
686
-
687
-
688
- end
689
-
690
-
691
- describe 'finally' do
692
-
693
- describe 'when the promise is fulfilled' do
694
-
695
- it "should call the callback" do
696
- @loop.run {
697
- @promise.finally do
698
- @log << :finally
699
- end
700
-
701
- @deferred.resolve(:foo)
702
-
703
- @loop.next_tick do
704
- @loop.stop
705
- end
706
- }
707
-
708
- @log.should == [:finally]
709
- end
710
-
711
- it "should fulfill with the original value" do
712
- @loop.run {
713
- @promise.finally(proc {
714
- @log << :finally
715
- :finally
716
- }).then do |result|
717
- @log << result
718
- end
719
-
720
-
721
- @deferred.resolve(:foo)
722
-
723
- @loop.next_tick do
724
- @loop.next_tick do
725
- @loop.stop
726
- end
727
- end
728
- }
729
-
730
- @log.should == [:finally, :foo]
731
- end
732
-
733
- it "should fulfill with the original value (larger test)" do
734
- @loop.run {
735
- @promise.then(proc { |result|
736
- @log << result
737
- result
738
- }).finally(proc {
739
- @log << :finally
740
- :finally
741
- }).then(proc { |result|
742
- @log << result
743
- :change
744
- }).then(proc { |result|
745
- @log << result
746
- result
747
- }).finally(proc {
748
- @log << :finally
749
- :finally
750
- }).then(proc { |result|
751
- @log << result
752
- result
753
- })
754
-
755
-
756
- @deferred.resolve(:foo)
757
-
758
-
759
- @loop.next_tick do
760
- @loop.next_tick do
761
- @loop.next_tick do
762
- @loop.next_tick do
763
- @loop.next_tick do
764
- @loop.next_tick do
765
- @loop.next_tick do
766
- @loop.next_tick do
767
- @loop.stop
768
- end
769
- end
770
- end
771
- end
772
- end
773
- end
774
- end
775
- end
776
- }
777
-
778
- @log.should == [:foo, :finally, :foo, :change, :finally, :change]
779
- end
780
-
781
- describe "when the callback throws an exception" do
782
- it "should reject with this new exception" do
783
- @loop.run {
784
- @promise.finally(proc {
785
- @log << :finally
786
- raise 'error'
787
- }).catch do |reason|
788
- @log.push reason.is_a?(Exception)
789
- end
790
-
791
- @deferred.resolve(:foo)
792
-
793
- @loop.next_tick do
794
- @loop.next_tick do
795
- @loop.stop
796
- end
797
- end
798
- }
799
-
800
- @log.should == [:finally, true]
801
- end
802
- end
803
-
804
- describe "when the callback returns a promise" do
805
- it "should fulfill with the original reason after that promise resolves" do
806
- @loop.run {
807
- deferred2 = @loop.defer
808
-
809
- @promise.finally(proc {
810
- @log << :finally
811
- deferred2.promise
812
- }).then do |result|
813
- @log << result
814
- end
815
-
816
- @deferred.resolve(:foo)
817
-
818
- @loop.next_tick do
819
- @loop.next_tick do
820
- @loop.next_tick do
821
- @loop.next_tick do
822
- @log << :resolving
823
- deferred2.resolve('working')
824
- @loop.next_tick do
825
- @loop.next_tick do
826
- @loop.stop
827
- end
828
- end
829
- end
830
- end
831
- end
832
- end
833
- }
834
-
835
- @log.should == [:finally, :resolving, :foo]
836
- end
837
-
838
-
839
- it "should reject with the new reason when it is rejected" do
840
- @loop.run {
841
- deferred2 = @loop.defer
842
-
843
- @promise.finally(proc {
844
- @log << :finally
845
- deferred2.promise
846
- }).catch do |result|
847
- @log << result
848
- end
849
-
850
- @deferred.resolve(:foo)
851
-
852
- @loop.next_tick do
853
- @loop.next_tick do
854
- @loop.next_tick do
855
- @loop.next_tick do
856
- @log << :rejecting
857
- deferred2.reject(:rejected)
858
- @loop.next_tick do
859
- @loop.next_tick do
860
- @loop.stop
861
- end
862
- end
863
- end
864
- end
865
- end
866
- end
867
- }
868
-
869
- @log.should == [:finally, :rejecting, :rejected]
870
- end
871
- end
872
-
873
- end
874
-
875
- end
876
-
877
- end
878
-
879
-
880
-
881
- describe 'reject' do
882
-
883
- it "should package a string into a rejected promise" do
884
- @loop.run {
885
- rejectedPromise = Libuv::Q.reject(@loop, 'not gonna happen')
886
-
887
- @promise.then(@default_fail, proc {|reason|
888
- @log << reason
889
- })
890
-
891
- @deferred.resolve(rejectedPromise)
892
-
893
- @loop.next_tick do
894
- @loop.stop
895
- end
896
- }
897
-
898
- @log.should == ['not gonna happen']
899
- end
900
-
901
-
902
- it "should return a promise that forwards callbacks if the callbacks are missing" do
903
- @loop.run {
904
- rejectedPromise = Libuv::Q.reject(@loop, 'not gonna happen')
905
-
906
- @promise.then(@default_fail, proc {|reason|
907
- @log << reason
908
- })
909
-
910
- @deferred.resolve(rejectedPromise.then())
911
-
912
- @loop.next_tick do
913
- @loop.next_tick do
914
- @loop.stop
915
- end
916
- end
917
- }
918
-
919
- @log.should == ['not gonna happen']
920
- end
921
-
922
- end
923
-
924
-
925
-
926
- describe 'all' do
927
-
928
- it "should resolve all of nothing" do
929
- @loop.run {
930
- Libuv::Q.all(@loop).then nil, @default_fail do |result|
931
- @log << result
932
- end
933
-
934
- @loop.next_tick do
935
- @loop.stop
936
- end
937
- }
938
-
939
- @log.should == [[]]
940
- end
941
-
942
- it "should take an array of promises and return a promise for an array of results" do
943
- @loop.run {
944
- deferred1 = @loop.defer
945
- deferred2 = @loop.defer
946
-
947
- Libuv::Q.all(@loop, @promise, deferred1.promise, deferred2.promise).then nil, @default_fail do |result|
948
- @log = result
949
- @loop.stop
950
- end
951
-
952
- @loop.work { @deferred.resolve(:foo) }
953
- @loop.work { deferred2.resolve(:baz) }
954
- @loop.work { deferred1.resolve(:bar) }
955
- }
956
-
957
- @log.should == [:foo, :bar, :baz]
958
- end
959
-
960
-
961
- it "should reject the derived promise if at least one of the promises in the array is rejected" do
962
- @loop.run {
963
- deferred1 = @loop.defer
964
- deferred2 = @loop.defer
965
-
966
- Libuv::Q.all(@loop, @promise, deferred1.promise, deferred2.promise).then(@default_fail, proc {|reason|
967
- @log << reason
968
- @loop.stop
969
- })
970
-
971
- @loop.work { @deferred.resolve(:foo) }
972
- @loop.work { deferred2.reject(:baz) }
973
- }
974
-
975
- @log.should == [:baz]
976
- end
977
-
978
- end
979
-
980
- end
1
+ require 'libuv'
2
+
3
+
4
+ describe Libuv::Q do
5
+
6
+ before :each do
7
+ @loop = Libuv::Loop.new
8
+ @deferred = @loop.defer
9
+ @promise = @deferred.promise
10
+ @log = []
11
+ @default_fail = proc { |reason|
12
+ @loop.stop
13
+ }
14
+ end
15
+
16
+
17
+ describe 'resolve' do
18
+
19
+
20
+ it "should call the callback in the next turn" do
21
+ @loop.run {
22
+ @promise.then nil, @default_fail do |result|
23
+ @log << result
24
+ end
25
+
26
+ @deferred.resolve(:foo)
27
+
28
+ @loop.next_tick do
29
+ @loop.stop
30
+ end
31
+ }
32
+
33
+ @log.should == [:foo]
34
+ end
35
+
36
+
37
+
38
+ it "should be able to resolve the callback after it has already been resolved" do
39
+ @loop.run {
40
+ @promise.then nil, @default_fail do |result|
41
+ @log << result
42
+ @promise.then nil, @default_fail do |result|
43
+ @log << result
44
+ end
45
+ end
46
+
47
+ @deferred.resolve(:foo)
48
+
49
+ @loop.next_tick do
50
+ @loop.next_tick do
51
+ @loop.stop
52
+ end
53
+ end
54
+ }
55
+ @log.should == [:foo, :foo]
56
+ end
57
+
58
+
59
+
60
+ it "should fulfill success callbacks in the registration order" do
61
+ @loop.run {
62
+ @promise.then nil, @default_fail do |result|
63
+ @log << :first
64
+ end
65
+
66
+ @promise.then nil, @default_fail do |result|
67
+ @log << :second
68
+ end
69
+
70
+ @deferred.resolve(:foo)
71
+
72
+ @loop.next_tick do
73
+ @loop.stop
74
+ end
75
+ }
76
+ @log.should == [:first, :second]
77
+ end
78
+
79
+
80
+ it "should do nothing if a promise was previously resolved" do
81
+ @loop.run {
82
+ @promise.then nil, @default_fail do |result|
83
+ @log << result
84
+ @log.should == [:foo]
85
+ @deferred.resolve(:bar)
86
+ end
87
+
88
+ @deferred.resolve(:foo)
89
+ @deferred.reject(:baz)
90
+
91
+ #
92
+ # 4 ticks should detect any errors
93
+ #
94
+ @loop.next_tick do
95
+ @loop.next_tick do
96
+ @loop.next_tick do
97
+ @loop.next_tick do
98
+ @loop.stop
99
+ end
100
+ end
101
+ end
102
+ end
103
+ }
104
+ @log.should == [:foo]
105
+ end
106
+
107
+
108
+ it "should allow deferred resolution with a new promise" do
109
+ deferred2 = @loop.defer
110
+ @loop.run {
111
+ @promise.then nil, @default_fail do |result|
112
+ @log << result
113
+ @loop.stop
114
+ end
115
+
116
+ @deferred.resolve(deferred2.promise)
117
+ deferred2.resolve(:foo)
118
+ }
119
+ @log.should == [:foo]
120
+ end
121
+
122
+
123
+ it "should not break if a callbacks registers another callback" do
124
+ @loop.run {
125
+ @promise.then nil, @default_fail do |result|
126
+ @log << :outer
127
+ @promise.then nil, @default_fail do |result|
128
+ @log << :inner
129
+ end
130
+ end
131
+
132
+ @deferred.resolve(:foo)
133
+
134
+ @loop.next_tick do
135
+ @loop.next_tick do
136
+ @loop.stop
137
+ end
138
+ end
139
+ }
140
+
141
+ @log.should == [:outer, :inner]
142
+ end
143
+
144
+
145
+
146
+ it "can modify the result of a promise before returning" do
147
+ @loop.run {
148
+ proc { |name|
149
+ @loop.work { @deferred.resolve("Hello #{name}") }
150
+ @promise.then nil, @default_fail do |result|
151
+ @log << result
152
+ result += "?"
153
+ result
154
+ end
155
+ }.call('Robin Hood').then nil, @default_fail do |greeting|
156
+ @log << greeting
157
+ @loop.stop
158
+ end
159
+ }
160
+
161
+ @log.should == ['Hello Robin Hood', 'Hello Robin Hood?']
162
+ end
163
+
164
+ end
165
+
166
+
167
+ describe 'reject' do
168
+
169
+ it "should reject the promise and execute all error callbacks" do
170
+ @loop.run {
171
+ @promise.then(@default_fail, proc {|result|
172
+ @log << :first
173
+ })
174
+ @promise.then(@default_fail, proc {|result|
175
+ @log << :second
176
+ })
177
+
178
+ @deferred.reject(:foo)
179
+
180
+ @loop.next_tick do
181
+ @loop.stop
182
+ end
183
+ }
184
+ @log.should == [:first, :second]
185
+ end
186
+
187
+
188
+ it "should do nothing if a promise was previously rejected" do
189
+ @loop.run {
190
+ @promise.then(@default_fail, proc {|result|
191
+ @log << result
192
+ @deferred.resolve(:bar)
193
+ })
194
+
195
+ @deferred.reject(:baz)
196
+ @deferred.resolve(:foo)
197
+
198
+ #
199
+ # 4 ticks should detect any errors
200
+ #
201
+ @loop.next_tick do
202
+ @loop.next_tick do
203
+ @loop.next_tick do
204
+ @loop.next_tick do
205
+ @loop.stop
206
+ end
207
+ end
208
+ end
209
+ end
210
+ }
211
+ @log.should == [:baz]
212
+ end
213
+
214
+
215
+ it "should not defer rejection with a new promise" do
216
+ deferred2 = @loop.defer
217
+ @loop.run {
218
+ @promise.then(@default_fail, @default_fail)
219
+ begin
220
+ @deferred.reject(deferred2.promise)
221
+ rescue => e
222
+ @log << e.is_a?(ArgumentError)
223
+ @loop.stop
224
+ end
225
+ }
226
+
227
+ @log.should == [true]
228
+ end
229
+
230
+ end
231
+
232
+
233
+ describe 'notify' do
234
+ it "should execute all progress callbacks in the registration order" do
235
+ @loop.run {
236
+ @promise.progress do |update|
237
+ @log << :first
238
+ end
239
+
240
+ @promise.progress do |update|
241
+ @log << :second
242
+ end
243
+
244
+ @deferred.notify(:foo)
245
+
246
+ @loop.next_tick do
247
+ @loop.stop
248
+ end
249
+ }
250
+
251
+ @log.should == [:first, :second]
252
+ end
253
+
254
+ it "should do nothing if a promise was previously resolved" do
255
+ @loop.run {
256
+
257
+ @promise.progress do |update|
258
+ @log << update
259
+ end
260
+
261
+ @deferred.resolve(:foo)
262
+ @deferred.notify(:baz)
263
+
264
+
265
+ #
266
+ # 4 ticks should detect any errors
267
+ #
268
+ @loop.next_tick do
269
+ @loop.next_tick do
270
+ @loop.next_tick do
271
+ @loop.next_tick do
272
+ @loop.stop
273
+ end
274
+ end
275
+ end
276
+ end
277
+ }
278
+
279
+ @log.should == []
280
+ end
281
+
282
+ it "should do nothing if a promise was previously rejected" do
283
+ @loop.run {
284
+
285
+ @promise.progress do |update|
286
+ @log << update
287
+ end
288
+ @deferred.reject(:foo)
289
+ @deferred.notify(:baz)
290
+
291
+
292
+
293
+ #
294
+ # 4 ticks should detect any errors
295
+ #
296
+ @loop.next_tick do
297
+ @loop.next_tick do
298
+ @loop.next_tick do
299
+ @loop.next_tick do
300
+ @loop.stop
301
+ end
302
+ end
303
+ end
304
+ end
305
+ }
306
+
307
+ @log.should == []
308
+ end
309
+
310
+
311
+ it "should not apply any special treatment to promises passed to notify" do
312
+ @loop.run {
313
+ deferred2 = @loop.defer
314
+
315
+ @promise.progress do |update|
316
+ @log << update.is_a?(::Libuv::Q::Promise)
317
+ end
318
+ @deferred.notify(deferred2.promise)
319
+
320
+ @loop.next_tick do
321
+ @loop.stop
322
+ end
323
+ }
324
+
325
+ @log.should == [true]
326
+ end
327
+
328
+
329
+ it "should call the progress callbacks in the next turn" do
330
+ @loop.run {
331
+ @promise.progress do |update|
332
+ @log << :first
333
+ end
334
+
335
+ @promise.progress do |update|
336
+ @log << :second
337
+ end
338
+
339
+ @deferred.notify(:foo)
340
+
341
+ @log << @log.length # Has notify run in this tick
342
+ @loop.stop # Stop will run through the next tick before stopping
343
+ }
344
+
345
+ @log.should == [0, :first, :second]
346
+ end
347
+
348
+ it "should ignore notifications sent out in the same turn before listener registration" do
349
+ @loop.run {
350
+ @deferred.notify(:foo)
351
+
352
+ @promise.progress do |update|
353
+ @log << :first
354
+ end
355
+
356
+ @promise.progress do |update|
357
+ @log << :second
358
+ end
359
+
360
+ @loop.next_tick do
361
+ @loop.stop
362
+ end
363
+ }
364
+
365
+ @log.should == []
366
+ end
367
+ end
368
+
369
+
370
+ describe Libuv::Q::Promise do
371
+
372
+ describe 'then' do
373
+
374
+ it "should allow registration of a success callback without an errback and resolve" do
375
+ @loop.run {
376
+ @promise.then do |result|
377
+ @log << result
378
+ end
379
+
380
+ @deferred.resolve(:foo)
381
+
382
+ @loop.next_tick do
383
+ @loop.stop
384
+ end
385
+ }
386
+
387
+ @log.should == [:foo]
388
+ end
389
+
390
+
391
+ it "should allow registration of a success callback without an errback and reject" do
392
+ @loop.run {
393
+ @promise.then do |result|
394
+ @log << result
395
+ end
396
+
397
+ @deferred.reject(:foo)
398
+
399
+ @loop.next_tick do
400
+ @loop.stop
401
+ end
402
+ }
403
+
404
+ @log.should == []
405
+ end
406
+
407
+
408
+ it "should allow registration of an errback without a success callback and reject" do
409
+ @loop.run {
410
+ @promise.catch(proc {|reason|
411
+ @log << reason
412
+ })
413
+
414
+ @deferred.reject(:foo)
415
+
416
+ @loop.next_tick do
417
+ @loop.stop
418
+ end
419
+ }
420
+
421
+ @log.should == [:foo]
422
+ end
423
+
424
+
425
+ it "should allow registration of an errback without a success callback and resolve" do
426
+ @loop.run {
427
+ @promise.catch(proc {|reason|
428
+ @log << reason
429
+ })
430
+
431
+ @deferred.resolve(:foo)
432
+
433
+ @loop.next_tick do
434
+ @loop.stop
435
+ end
436
+ }
437
+
438
+ @log.should == []
439
+ end
440
+
441
+
442
+ it "should resolve all callbacks with the original value" do
443
+ @loop.run {
444
+ @promise.then nil, @default_fail do |result|
445
+ @log << result
446
+ :alt1
447
+ end
448
+ @promise.then nil, @default_fail do |result|
449
+ @log << result
450
+ 'ERROR'
451
+ end
452
+ @promise.then nil, @default_fail do |result|
453
+ @log << result
454
+ Libuv::Q.reject(@loop, 'some reason')
455
+ end
456
+ @promise.then nil, @default_fail do |result|
457
+ @log << result
458
+ :alt2
459
+ end
460
+
461
+ @deferred.resolve(:foo)
462
+
463
+ @loop.next_tick do
464
+ @loop.stop
465
+ end
466
+ }
467
+
468
+ @log.should == [:foo, :foo, :foo, :foo]
469
+ end
470
+
471
+
472
+ it "should notify all callbacks with the original value" do
473
+ @loop.run { |loop_promise|
474
+ @promise.progress do |result|
475
+ @log << result
476
+ :alt1
477
+ end
478
+ @promise.progress do |result|
479
+ @log << result
480
+ 'ERROR'
481
+ end
482
+ @promise.progress do |result|
483
+ @log << result
484
+ Libuv::Q.reject(@loop, 'some reason')
485
+ end
486
+ @promise.progress do |result|
487
+ @log << result
488
+ :alt2
489
+ end
490
+
491
+
492
+ @deferred.notify(:foo)
493
+
494
+ @loop.next_tick do
495
+ @loop.next_tick do
496
+ @loop.next_tick do
497
+ @loop.next_tick do
498
+ @loop.next_tick do
499
+ @loop.stop
500
+ end
501
+ end
502
+ end
503
+ end
504
+ end
505
+ }
506
+
507
+ @log.should == [:foo, :foo, :foo, :foo]
508
+ end
509
+
510
+
511
+ it "should reject all callbacks with the original reason" do
512
+ @loop.run {
513
+ @promise.then(@default_fail, proc {|result|
514
+ @log << result
515
+ :alt1
516
+ })
517
+ @promise.then(@default_fail, proc {|result|
518
+ @log << result
519
+ 'ERROR'
520
+ })
521
+ @promise.then(@default_fail, proc {|result|
522
+ @log << result
523
+ Libuv::Q.reject(@loop, 'some reason')
524
+ })
525
+ @promise.then(@default_fail, proc {|result|
526
+ @log << result
527
+ :alt2
528
+ })
529
+
530
+ @deferred.reject(:foo)
531
+
532
+ @loop.next_tick do
533
+ @loop.stop
534
+ end
535
+ }
536
+
537
+ @log.should == [:foo, :foo, :foo, :foo]
538
+ end
539
+
540
+
541
+ it "should propagate resolution and rejection between dependent promises" do
542
+ @loop.run {
543
+ @promise.then(proc { |result|
544
+ @log << result
545
+ :bar
546
+ }, @default_fail).then(proc { |result|
547
+ @log << result
548
+ raise 'baz'
549
+ }, @default_fail).then(@default_fail, proc {|result|
550
+ @log << result.message
551
+ raise 'bob'
552
+ }).then(@default_fail, proc {|result|
553
+ @log << result.message
554
+ :done
555
+ }).then(proc { |result|
556
+ @log << result
557
+ }, @default_fail)
558
+
559
+ @deferred.resolve(:foo)
560
+
561
+ @loop.next_tick do
562
+ @loop.next_tick do
563
+ @loop.next_tick do
564
+ @loop.next_tick do
565
+ @loop.next_tick do
566
+ @loop.next_tick do # extra tick?
567
+ @loop.stop
568
+ end
569
+ end
570
+ end
571
+ end
572
+ end
573
+ end
574
+ }
575
+
576
+ @log.should == [:foo, :bar, 'baz', 'bob', :done]
577
+ end
578
+
579
+
580
+ it "should propagate notification between dependent promises" do
581
+ @loop.run { |loop_promise|
582
+ loop_promise.progress do |type, id, error|
583
+ @log << id
584
+ end
585
+
586
+
587
+ @promise.progress(proc { |result|
588
+ @log << result
589
+ :bar
590
+ }).progress(proc { |result|
591
+ @log << result
592
+ result
593
+ }).progress(proc {|result|
594
+ @log << result
595
+ result
596
+ }).progress(proc {|result|
597
+ @log << result
598
+ :done
599
+ }).progress(proc { |result|
600
+ @log << result
601
+ result
602
+ })
603
+
604
+
605
+ @deferred.notify(:foo)
606
+
607
+ @loop.next_tick do
608
+ @loop.next_tick do
609
+ @loop.next_tick do
610
+ @loop.next_tick do
611
+ @loop.next_tick do
612
+ @loop.next_tick do # extra tick?
613
+ @loop.stop
614
+ end
615
+ end
616
+ end
617
+ end
618
+ end
619
+ end
620
+ }
621
+
622
+ @log.should == [:foo, :bar, :bar, :bar, :done]
623
+ end
624
+
625
+
626
+ it "should stop notification propagation in case of error" do
627
+ @loop.run { |loop_logger|
628
+ loop_logger.progress do |type, id, error|
629
+ @log << id
630
+ end
631
+
632
+
633
+ @promise.progress(proc { |result|
634
+ @log << result
635
+ :bar
636
+ }).progress(proc { |result|
637
+ @log << result
638
+ raise 'err'
639
+ result
640
+ }).progress(proc {|result|
641
+ @log << result
642
+ result
643
+ }).progress(proc {|result|
644
+ @log << result
645
+ :done
646
+ }).progress(proc { |result|
647
+ @log << result
648
+ result
649
+ })
650
+
651
+
652
+ @deferred.notify(:foo)
653
+
654
+ @loop.next_tick do
655
+ @loop.next_tick do
656
+ @loop.next_tick do
657
+ @loop.next_tick do
658
+ @loop.next_tick do
659
+ @loop.stop
660
+ end
661
+ end
662
+ end
663
+ end
664
+ end
665
+ }
666
+
667
+ @log.should == [:foo, :bar, :q_progress_cb]
668
+ end
669
+
670
+
671
+ it "should call error callback in the next turn even if promise is already rejected" do
672
+ @loop.run {
673
+ @deferred.reject(:foo)
674
+
675
+ @promise.catch(proc {|reason|
676
+ @log << reason
677
+ })
678
+
679
+ @loop.next_tick do
680
+ @loop.stop
681
+ end
682
+ }
683
+
684
+ @log.should == [:foo]
685
+ end
686
+
687
+
688
+ end
689
+
690
+
691
+ describe 'finally' do
692
+
693
+ describe 'when the promise is fulfilled' do
694
+
695
+ it "should call the callback" do
696
+ @loop.run {
697
+ @promise.finally do
698
+ @log << :finally
699
+ end
700
+
701
+ @deferred.resolve(:foo)
702
+
703
+ @loop.next_tick do
704
+ @loop.stop
705
+ end
706
+ }
707
+
708
+ @log.should == [:finally]
709
+ end
710
+
711
+ it "should fulfill with the original value" do
712
+ @loop.run {
713
+ @promise.finally(proc {
714
+ @log << :finally
715
+ :finally
716
+ }).then do |result|
717
+ @log << result
718
+ end
719
+
720
+
721
+ @deferred.resolve(:foo)
722
+
723
+ @loop.next_tick do
724
+ @loop.next_tick do
725
+ @loop.stop
726
+ end
727
+ end
728
+ }
729
+
730
+ @log.should == [:finally, :foo]
731
+ end
732
+
733
+ it "should fulfill with the original value (larger test)" do
734
+ @loop.run {
735
+ @promise.then(proc { |result|
736
+ @log << result
737
+ result
738
+ }).finally(proc {
739
+ @log << :finally
740
+ :finally
741
+ }).then(proc { |result|
742
+ @log << result
743
+ :change
744
+ }).then(proc { |result|
745
+ @log << result
746
+ result
747
+ }).finally(proc {
748
+ @log << :finally
749
+ :finally
750
+ }).then(proc { |result|
751
+ @log << result
752
+ result
753
+ })
754
+
755
+
756
+ @deferred.resolve(:foo)
757
+
758
+
759
+ @loop.next_tick do
760
+ @loop.next_tick do
761
+ @loop.next_tick do
762
+ @loop.next_tick do
763
+ @loop.next_tick do
764
+ @loop.next_tick do
765
+ @loop.next_tick do
766
+ @loop.next_tick do
767
+ @loop.stop
768
+ end
769
+ end
770
+ end
771
+ end
772
+ end
773
+ end
774
+ end
775
+ end
776
+ }
777
+
778
+ @log.should == [:foo, :finally, :foo, :change, :finally, :change]
779
+ end
780
+
781
+ describe "when the callback throws an exception" do
782
+ it "should reject with this new exception" do
783
+ @loop.run {
784
+ @promise.finally(proc {
785
+ @log << :finally
786
+ raise 'error'
787
+ }).catch do |reason|
788
+ @log.push reason.is_a?(Exception)
789
+ end
790
+
791
+ @deferred.resolve(:foo)
792
+
793
+ @loop.next_tick do
794
+ @loop.next_tick do
795
+ @loop.stop
796
+ end
797
+ end
798
+ }
799
+
800
+ @log.should == [:finally, true]
801
+ end
802
+ end
803
+
804
+ describe "when the callback returns a promise" do
805
+ it "should fulfill with the original reason after that promise resolves" do
806
+ @loop.run {
807
+ deferred2 = @loop.defer
808
+
809
+ @promise.finally(proc {
810
+ @log << :finally
811
+ deferred2.promise
812
+ }).then do |result|
813
+ @log << result
814
+ end
815
+
816
+ @deferred.resolve(:foo)
817
+
818
+ @loop.next_tick do
819
+ @loop.next_tick do
820
+ @loop.next_tick do
821
+ @loop.next_tick do
822
+ @log << :resolving
823
+ deferred2.resolve('working')
824
+ @loop.next_tick do
825
+ @loop.next_tick do
826
+ @loop.stop
827
+ end
828
+ end
829
+ end
830
+ end
831
+ end
832
+ end
833
+ }
834
+
835
+ @log.should == [:finally, :resolving, :foo]
836
+ end
837
+
838
+
839
+ it "should reject with the new reason when it is rejected" do
840
+ @loop.run {
841
+ deferred2 = @loop.defer
842
+
843
+ @promise.finally(proc {
844
+ @log << :finally
845
+ deferred2.promise
846
+ }).catch do |result|
847
+ @log << result
848
+ end
849
+
850
+ @deferred.resolve(:foo)
851
+
852
+ @loop.next_tick do
853
+ @loop.next_tick do
854
+ @loop.next_tick do
855
+ @loop.next_tick do
856
+ @log << :rejecting
857
+ deferred2.reject(:rejected)
858
+ @loop.next_tick do
859
+ @loop.next_tick do
860
+ @loop.stop
861
+ end
862
+ end
863
+ end
864
+ end
865
+ end
866
+ end
867
+ }
868
+
869
+ @log.should == [:finally, :rejecting, :rejected]
870
+ end
871
+ end
872
+
873
+ end
874
+
875
+ end
876
+
877
+ end
878
+
879
+
880
+
881
+ describe 'reject' do
882
+
883
+ it "should package a string into a rejected promise" do
884
+ @loop.run {
885
+ rejectedPromise = Libuv::Q.reject(@loop, 'not gonna happen')
886
+
887
+ @promise.then(@default_fail, proc {|reason|
888
+ @log << reason
889
+ })
890
+
891
+ @deferred.resolve(rejectedPromise)
892
+
893
+ @loop.next_tick do
894
+ @loop.stop
895
+ end
896
+ }
897
+
898
+ @log.should == ['not gonna happen']
899
+ end
900
+
901
+
902
+ it "should return a promise that forwards callbacks if the callbacks are missing" do
903
+ @loop.run {
904
+ rejectedPromise = Libuv::Q.reject(@loop, 'not gonna happen')
905
+
906
+ @promise.then(@default_fail, proc {|reason|
907
+ @log << reason
908
+ })
909
+
910
+ @deferred.resolve(rejectedPromise.then())
911
+
912
+ @loop.next_tick do
913
+ @loop.next_tick do
914
+ @loop.stop
915
+ end
916
+ end
917
+ }
918
+
919
+ @log.should == ['not gonna happen']
920
+ end
921
+
922
+ end
923
+
924
+
925
+
926
+ describe 'all' do
927
+
928
+ it "should resolve all of nothing" do
929
+ @loop.run {
930
+ Libuv::Q.all(@loop).then nil, @default_fail do |result|
931
+ @log << result
932
+ end
933
+
934
+ @loop.next_tick do
935
+ @loop.stop
936
+ end
937
+ }
938
+
939
+ @log.should == [[]]
940
+ end
941
+
942
+ it "should take an array of promises and return a promise for an array of results" do
943
+ @loop.run {
944
+ deferred1 = @loop.defer
945
+ deferred2 = @loop.defer
946
+
947
+ Libuv::Q.all(@loop, @promise, deferred1.promise, deferred2.promise).then nil, @default_fail do |result|
948
+ @log = result
949
+ @loop.stop
950
+ end
951
+
952
+ @loop.work { @deferred.resolve(:foo) }
953
+ @loop.work { deferred2.resolve(:baz) }
954
+ @loop.work { deferred1.resolve(:bar) }
955
+ }
956
+
957
+ @log.should == [:foo, :bar, :baz]
958
+ end
959
+
960
+
961
+ it "should reject the derived promise if at least one of the promises in the array is rejected" do
962
+ @loop.run {
963
+ deferred1 = @loop.defer
964
+ deferred2 = @loop.defer
965
+
966
+ Libuv::Q.all(@loop, @promise, deferred1.promise, deferred2.promise).then(@default_fail, proc {|reason|
967
+ @log << reason
968
+ @loop.stop
969
+ })
970
+
971
+ @loop.work { @deferred.resolve(:foo) }
972
+ @loop.work { deferred2.reject(:baz) }
973
+ }
974
+
975
+ @log.should == [:baz]
976
+ end
977
+
978
+ end
979
+
980
+ end