spec/defer_spec.rb in libuv-0.11.4 vs spec/defer_spec.rb in libuv-0.11.5
- old
+ new
@@ -1,980 +1,980 @@
-require 'libuv'
-
-
-describe Libuv::Q do
-
- before :each do
- @loop = Libuv::Loop.new
- @deferred = @loop.defer
- @promise = @deferred.promise
- @log = []
- @default_fail = proc { |reason|
- @loop.stop
- }
- end
-
-
- describe 'resolve' do
-
-
- it "should call the callback in the next turn" do
- @loop.run {
- @promise.then nil, @default_fail do |result|
- @log << result
- end
-
- @deferred.resolve(:foo)
-
- @loop.next_tick do
- @loop.stop
- end
- }
-
- @log.should == [:foo]
- end
-
-
-
- it "should be able to resolve the callback after it has already been resolved" do
- @loop.run {
- @promise.then nil, @default_fail do |result|
- @log << result
- @promise.then nil, @default_fail do |result|
- @log << result
- end
- end
-
- @deferred.resolve(:foo)
-
- @loop.next_tick do
- @loop.next_tick do
- @loop.stop
- end
- end
- }
- @log.should == [:foo, :foo]
- end
-
-
-
- it "should fulfill success callbacks in the registration order" do
- @loop.run {
- @promise.then nil, @default_fail do |result|
- @log << :first
- end
-
- @promise.then nil, @default_fail do |result|
- @log << :second
- end
-
- @deferred.resolve(:foo)
-
- @loop.next_tick do
- @loop.stop
- end
- }
- @log.should == [:first, :second]
- end
-
-
- it "should do nothing if a promise was previously resolved" do
- @loop.run {
- @promise.then nil, @default_fail do |result|
- @log << result
- @log.should == [:foo]
- @deferred.resolve(:bar)
- end
-
- @deferred.resolve(:foo)
- @deferred.reject(:baz)
-
- #
- # 4 ticks should detect any errors
- #
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @loop.stop
- end
- end
- end
- end
- }
- @log.should == [:foo]
- end
-
-
- it "should allow deferred resolution with a new promise" do
- deferred2 = @loop.defer
- @loop.run {
- @promise.then nil, @default_fail do |result|
- @log << result
- @loop.stop
- end
-
- @deferred.resolve(deferred2.promise)
- deferred2.resolve(:foo)
- }
- @log.should == [:foo]
- end
-
-
- it "should not break if a callbacks registers another callback" do
- @loop.run {
- @promise.then nil, @default_fail do |result|
- @log << :outer
- @promise.then nil, @default_fail do |result|
- @log << :inner
- end
- end
-
- @deferred.resolve(:foo)
-
- @loop.next_tick do
- @loop.next_tick do
- @loop.stop
- end
- end
- }
-
- @log.should == [:outer, :inner]
- end
-
-
-
- it "can modify the result of a promise before returning" do
- @loop.run {
- proc { |name|
- @loop.work { @deferred.resolve("Hello #{name}") }
- @promise.then nil, @default_fail do |result|
- @log << result
- result += "?"
- result
- end
- }.call('Robin Hood').then nil, @default_fail do |greeting|
- @log << greeting
- @loop.stop
- end
- }
-
- @log.should == ['Hello Robin Hood', 'Hello Robin Hood?']
- end
-
- end
-
-
- describe 'reject' do
-
- it "should reject the promise and execute all error callbacks" do
- @loop.run {
- @promise.then(@default_fail, proc {|result|
- @log << :first
- })
- @promise.then(@default_fail, proc {|result|
- @log << :second
- })
-
- @deferred.reject(:foo)
-
- @loop.next_tick do
- @loop.stop
- end
- }
- @log.should == [:first, :second]
- end
-
-
- it "should do nothing if a promise was previously rejected" do
- @loop.run {
- @promise.then(@default_fail, proc {|result|
- @log << result
- @deferred.resolve(:bar)
- })
-
- @deferred.reject(:baz)
- @deferred.resolve(:foo)
-
- #
- # 4 ticks should detect any errors
- #
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @loop.stop
- end
- end
- end
- end
- }
- @log.should == [:baz]
- end
-
-
- it "should not defer rejection with a new promise" do
- deferred2 = @loop.defer
- @loop.run {
- @promise.then(@default_fail, @default_fail)
- begin
- @deferred.reject(deferred2.promise)
- rescue => e
- @log << e.is_a?(ArgumentError)
- @loop.stop
- end
- }
-
- @log.should == [true]
- end
-
- end
-
-
- describe 'notify' do
- it "should execute all progress callbacks in the registration order" do
- @loop.run {
- @promise.progress do |update|
- @log << :first
- end
-
- @promise.progress do |update|
- @log << :second
- end
-
- @deferred.notify(:foo)
-
- @loop.next_tick do
- @loop.stop
- end
- }
-
- @log.should == [:first, :second]
- end
-
- it "should do nothing if a promise was previously resolved" do
- @loop.run {
-
- @promise.progress do |update|
- @log << update
- end
-
- @deferred.resolve(:foo)
- @deferred.notify(:baz)
-
-
- #
- # 4 ticks should detect any errors
- #
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @loop.stop
- end
- end
- end
- end
- }
-
- @log.should == []
- end
-
- it "should do nothing if a promise was previously rejected" do
- @loop.run {
-
- @promise.progress do |update|
- @log << update
- end
- @deferred.reject(:foo)
- @deferred.notify(:baz)
-
-
-
- #
- # 4 ticks should detect any errors
- #
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @loop.stop
- end
- end
- end
- end
- }
-
- @log.should == []
- end
-
-
- it "should not apply any special treatment to promises passed to notify" do
- @loop.run {
- deferred2 = @loop.defer
-
- @promise.progress do |update|
- @log << update.is_a?(::Libuv::Q::Promise)
- end
- @deferred.notify(deferred2.promise)
-
- @loop.next_tick do
- @loop.stop
- end
- }
-
- @log.should == [true]
- end
-
-
- it "should call the progress callbacks in the next turn" do
- @loop.run {
- @promise.progress do |update|
- @log << :first
- end
-
- @promise.progress do |update|
- @log << :second
- end
-
- @deferred.notify(:foo)
-
- @log << @log.length # Has notify run in this tick
- @loop.stop # Stop will run through the next tick before stopping
- }
-
- @log.should == [0, :first, :second]
- end
-
- it "should ignore notifications sent out in the same turn before listener registration" do
- @loop.run {
- @deferred.notify(:foo)
-
- @promise.progress do |update|
- @log << :first
- end
-
- @promise.progress do |update|
- @log << :second
- end
-
- @loop.next_tick do
- @loop.stop
- end
- }
-
- @log.should == []
- end
- end
-
-
- describe Libuv::Q::Promise do
-
- describe 'then' do
-
- it "should allow registration of a success callback without an errback and resolve" do
- @loop.run {
- @promise.then do |result|
- @log << result
- end
-
- @deferred.resolve(:foo)
-
- @loop.next_tick do
- @loop.stop
- end
- }
-
- @log.should == [:foo]
- end
-
-
- it "should allow registration of a success callback without an errback and reject" do
- @loop.run {
- @promise.then do |result|
- @log << result
- end
-
- @deferred.reject(:foo)
-
- @loop.next_tick do
- @loop.stop
- end
- }
-
- @log.should == []
- end
-
-
- it "should allow registration of an errback without a success callback and reject" do
- @loop.run {
- @promise.catch(proc {|reason|
- @log << reason
- })
-
- @deferred.reject(:foo)
-
- @loop.next_tick do
- @loop.stop
- end
- }
-
- @log.should == [:foo]
- end
-
-
- it "should allow registration of an errback without a success callback and resolve" do
- @loop.run {
- @promise.catch(proc {|reason|
- @log << reason
- })
-
- @deferred.resolve(:foo)
-
- @loop.next_tick do
- @loop.stop
- end
- }
-
- @log.should == []
- end
-
-
- it "should resolve all callbacks with the original value" do
- @loop.run {
- @promise.then nil, @default_fail do |result|
- @log << result
- :alt1
- end
- @promise.then nil, @default_fail do |result|
- @log << result
- 'ERROR'
- end
- @promise.then nil, @default_fail do |result|
- @log << result
- Libuv::Q.reject(@loop, 'some reason')
- end
- @promise.then nil, @default_fail do |result|
- @log << result
- :alt2
- end
-
- @deferred.resolve(:foo)
-
- @loop.next_tick do
- @loop.stop
- end
- }
-
- @log.should == [:foo, :foo, :foo, :foo]
- end
-
-
- it "should notify all callbacks with the original value" do
- @loop.run { |loop_promise|
- @promise.progress do |result|
- @log << result
- :alt1
- end
- @promise.progress do |result|
- @log << result
- 'ERROR'
- end
- @promise.progress do |result|
- @log << result
- Libuv::Q.reject(@loop, 'some reason')
- end
- @promise.progress do |result|
- @log << result
- :alt2
- end
-
-
- @deferred.notify(:foo)
-
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @loop.stop
- end
- end
- end
- end
- end
- }
-
- @log.should == [:foo, :foo, :foo, :foo]
- end
-
-
- it "should reject all callbacks with the original reason" do
- @loop.run {
- @promise.then(@default_fail, proc {|result|
- @log << result
- :alt1
- })
- @promise.then(@default_fail, proc {|result|
- @log << result
- 'ERROR'
- })
- @promise.then(@default_fail, proc {|result|
- @log << result
- Libuv::Q.reject(@loop, 'some reason')
- })
- @promise.then(@default_fail, proc {|result|
- @log << result
- :alt2
- })
-
- @deferred.reject(:foo)
-
- @loop.next_tick do
- @loop.stop
- end
- }
-
- @log.should == [:foo, :foo, :foo, :foo]
- end
-
-
- it "should propagate resolution and rejection between dependent promises" do
- @loop.run {
- @promise.then(proc { |result|
- @log << result
- :bar
- }, @default_fail).then(proc { |result|
- @log << result
- raise 'baz'
- }, @default_fail).then(@default_fail, proc {|result|
- @log << result.message
- raise 'bob'
- }).then(@default_fail, proc {|result|
- @log << result.message
- :done
- }).then(proc { |result|
- @log << result
- }, @default_fail)
-
- @deferred.resolve(:foo)
-
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do # extra tick?
- @loop.stop
- end
- end
- end
- end
- end
- end
- }
-
- @log.should == [:foo, :bar, 'baz', 'bob', :done]
- end
-
-
- it "should propagate notification between dependent promises" do
- @loop.run { |loop_promise|
- loop_promise.progress do |type, id, error|
- @log << id
- end
-
-
- @promise.progress(proc { |result|
- @log << result
- :bar
- }).progress(proc { |result|
- @log << result
- result
- }).progress(proc {|result|
- @log << result
- result
- }).progress(proc {|result|
- @log << result
- :done
- }).progress(proc { |result|
- @log << result
- result
- })
-
-
- @deferred.notify(:foo)
-
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do # extra tick?
- @loop.stop
- end
- end
- end
- end
- end
- end
- }
-
- @log.should == [:foo, :bar, :bar, :bar, :done]
- end
-
-
- it "should stop notification propagation in case of error" do
- @loop.run { |loop_logger|
- loop_logger.progress do |type, id, error|
- @log << id
- end
-
-
- @promise.progress(proc { |result|
- @log << result
- :bar
- }).progress(proc { |result|
- @log << result
- raise 'err'
- result
- }).progress(proc {|result|
- @log << result
- result
- }).progress(proc {|result|
- @log << result
- :done
- }).progress(proc { |result|
- @log << result
- result
- })
-
-
- @deferred.notify(:foo)
-
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @loop.stop
- end
- end
- end
- end
- end
- }
-
- @log.should == [:foo, :bar, :q_progress_cb]
- end
-
-
- it "should call error callback in the next turn even if promise is already rejected" do
- @loop.run {
- @deferred.reject(:foo)
-
- @promise.catch(proc {|reason|
- @log << reason
- })
-
- @loop.next_tick do
- @loop.stop
- end
- }
-
- @log.should == [:foo]
- end
-
-
- end
-
-
- describe 'finally' do
-
- describe 'when the promise is fulfilled' do
-
- it "should call the callback" do
- @loop.run {
- @promise.finally do
- @log << :finally
- end
-
- @deferred.resolve(:foo)
-
- @loop.next_tick do
- @loop.stop
- end
- }
-
- @log.should == [:finally]
- end
-
- it "should fulfill with the original value" do
- @loop.run {
- @promise.finally(proc {
- @log << :finally
- :finally
- }).then do |result|
- @log << result
- end
-
-
- @deferred.resolve(:foo)
-
- @loop.next_tick do
- @loop.next_tick do
- @loop.stop
- end
- end
- }
-
- @log.should == [:finally, :foo]
- end
-
- it "should fulfill with the original value (larger test)" do
- @loop.run {
- @promise.then(proc { |result|
- @log << result
- result
- }).finally(proc {
- @log << :finally
- :finally
- }).then(proc { |result|
- @log << result
- :change
- }).then(proc { |result|
- @log << result
- result
- }).finally(proc {
- @log << :finally
- :finally
- }).then(proc { |result|
- @log << result
- result
- })
-
-
- @deferred.resolve(:foo)
-
-
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @loop.stop
- end
- end
- end
- end
- end
- end
- end
- end
- }
-
- @log.should == [:foo, :finally, :foo, :change, :finally, :change]
- end
-
- describe "when the callback throws an exception" do
- it "should reject with this new exception" do
- @loop.run {
- @promise.finally(proc {
- @log << :finally
- raise 'error'
- }).catch do |reason|
- @log.push reason.is_a?(Exception)
- end
-
- @deferred.resolve(:foo)
-
- @loop.next_tick do
- @loop.next_tick do
- @loop.stop
- end
- end
- }
-
- @log.should == [:finally, true]
- end
- end
-
- describe "when the callback returns a promise" do
- it "should fulfill with the original reason after that promise resolves" do
- @loop.run {
- deferred2 = @loop.defer
-
- @promise.finally(proc {
- @log << :finally
- deferred2.promise
- }).then do |result|
- @log << result
- end
-
- @deferred.resolve(:foo)
-
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @log << :resolving
- deferred2.resolve('working')
- @loop.next_tick do
- @loop.next_tick do
- @loop.stop
- end
- end
- end
- end
- end
- end
- }
-
- @log.should == [:finally, :resolving, :foo]
- end
-
-
- it "should reject with the new reason when it is rejected" do
- @loop.run {
- deferred2 = @loop.defer
-
- @promise.finally(proc {
- @log << :finally
- deferred2.promise
- }).catch do |result|
- @log << result
- end
-
- @deferred.resolve(:foo)
-
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @loop.next_tick do
- @log << :rejecting
- deferred2.reject(:rejected)
- @loop.next_tick do
- @loop.next_tick do
- @loop.stop
- end
- end
- end
- end
- end
- end
- }
-
- @log.should == [:finally, :rejecting, :rejected]
- end
- end
-
- end
-
- end
-
- end
-
-
-
- describe 'reject' do
-
- it "should package a string into a rejected promise" do
- @loop.run {
- rejectedPromise = Libuv::Q.reject(@loop, 'not gonna happen')
-
- @promise.then(@default_fail, proc {|reason|
- @log << reason
- })
-
- @deferred.resolve(rejectedPromise)
-
- @loop.next_tick do
- @loop.stop
- end
- }
-
- @log.should == ['not gonna happen']
- end
-
-
- it "should return a promise that forwards callbacks if the callbacks are missing" do
- @loop.run {
- rejectedPromise = Libuv::Q.reject(@loop, 'not gonna happen')
-
- @promise.then(@default_fail, proc {|reason|
- @log << reason
- })
-
- @deferred.resolve(rejectedPromise.then())
-
- @loop.next_tick do
- @loop.next_tick do
- @loop.stop
- end
- end
- }
-
- @log.should == ['not gonna happen']
- end
-
- end
-
-
-
- describe 'all' do
-
- it "should resolve all of nothing" do
- @loop.run {
- Libuv::Q.all(@loop).then nil, @default_fail do |result|
- @log << result
- end
-
- @loop.next_tick do
- @loop.stop
- end
- }
-
- @log.should == [[]]
- end
-
- it "should take an array of promises and return a promise for an array of results" do
- @loop.run {
- deferred1 = @loop.defer
- deferred2 = @loop.defer
-
- Libuv::Q.all(@loop, @promise, deferred1.promise, deferred2.promise).then nil, @default_fail do |result|
- @log = result
- @loop.stop
- end
-
- @loop.work { @deferred.resolve(:foo) }
- @loop.work { deferred2.resolve(:baz) }
- @loop.work { deferred1.resolve(:bar) }
- }
-
- @log.should == [:foo, :bar, :baz]
- end
-
-
- it "should reject the derived promise if at least one of the promises in the array is rejected" do
- @loop.run {
- deferred1 = @loop.defer
- deferred2 = @loop.defer
-
- Libuv::Q.all(@loop, @promise, deferred1.promise, deferred2.promise).then(@default_fail, proc {|reason|
- @log << reason
- @loop.stop
- })
-
- @loop.work { @deferred.resolve(:foo) }
- @loop.work { deferred2.reject(:baz) }
- }
-
- @log.should == [:baz]
- end
-
- end
-
-end
+require 'libuv'
+
+
+describe Libuv::Q do
+
+ before :each do
+ @loop = Libuv::Loop.new
+ @deferred = @loop.defer
+ @promise = @deferred.promise
+ @log = []
+ @default_fail = proc { |reason|
+ @loop.stop
+ }
+ end
+
+
+ describe 'resolve' do
+
+
+ it "should call the callback in the next turn" do
+ @loop.run {
+ @promise.then nil, @default_fail do |result|
+ @log << result
+ end
+
+ @deferred.resolve(:foo)
+
+ @loop.next_tick do
+ @loop.stop
+ end
+ }
+
+ @log.should == [:foo]
+ end
+
+
+
+ it "should be able to resolve the callback after it has already been resolved" do
+ @loop.run {
+ @promise.then nil, @default_fail do |result|
+ @log << result
+ @promise.then nil, @default_fail do |result|
+ @log << result
+ end
+ end
+
+ @deferred.resolve(:foo)
+
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.stop
+ end
+ end
+ }
+ @log.should == [:foo, :foo]
+ end
+
+
+
+ it "should fulfill success callbacks in the registration order" do
+ @loop.run {
+ @promise.then nil, @default_fail do |result|
+ @log << :first
+ end
+
+ @promise.then nil, @default_fail do |result|
+ @log << :second
+ end
+
+ @deferred.resolve(:foo)
+
+ @loop.next_tick do
+ @loop.stop
+ end
+ }
+ @log.should == [:first, :second]
+ end
+
+
+ it "should do nothing if a promise was previously resolved" do
+ @loop.run {
+ @promise.then nil, @default_fail do |result|
+ @log << result
+ @log.should == [:foo]
+ @deferred.resolve(:bar)
+ end
+
+ @deferred.resolve(:foo)
+ @deferred.reject(:baz)
+
+ #
+ # 4 ticks should detect any errors
+ #
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.stop
+ end
+ end
+ end
+ end
+ }
+ @log.should == [:foo]
+ end
+
+
+ it "should allow deferred resolution with a new promise" do
+ deferred2 = @loop.defer
+ @loop.run {
+ @promise.then nil, @default_fail do |result|
+ @log << result
+ @loop.stop
+ end
+
+ @deferred.resolve(deferred2.promise)
+ deferred2.resolve(:foo)
+ }
+ @log.should == [:foo]
+ end
+
+
+ it "should not break if a callbacks registers another callback" do
+ @loop.run {
+ @promise.then nil, @default_fail do |result|
+ @log << :outer
+ @promise.then nil, @default_fail do |result|
+ @log << :inner
+ end
+ end
+
+ @deferred.resolve(:foo)
+
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.stop
+ end
+ end
+ }
+
+ @log.should == [:outer, :inner]
+ end
+
+
+
+ it "can modify the result of a promise before returning" do
+ @loop.run {
+ proc { |name|
+ @loop.work { @deferred.resolve("Hello #{name}") }
+ @promise.then nil, @default_fail do |result|
+ @log << result
+ result += "?"
+ result
+ end
+ }.call('Robin Hood').then nil, @default_fail do |greeting|
+ @log << greeting
+ @loop.stop
+ end
+ }
+
+ @log.should == ['Hello Robin Hood', 'Hello Robin Hood?']
+ end
+
+ end
+
+
+ describe 'reject' do
+
+ it "should reject the promise and execute all error callbacks" do
+ @loop.run {
+ @promise.then(@default_fail, proc {|result|
+ @log << :first
+ })
+ @promise.then(@default_fail, proc {|result|
+ @log << :second
+ })
+
+ @deferred.reject(:foo)
+
+ @loop.next_tick do
+ @loop.stop
+ end
+ }
+ @log.should == [:first, :second]
+ end
+
+
+ it "should do nothing if a promise was previously rejected" do
+ @loop.run {
+ @promise.then(@default_fail, proc {|result|
+ @log << result
+ @deferred.resolve(:bar)
+ })
+
+ @deferred.reject(:baz)
+ @deferred.resolve(:foo)
+
+ #
+ # 4 ticks should detect any errors
+ #
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.stop
+ end
+ end
+ end
+ end
+ }
+ @log.should == [:baz]
+ end
+
+
+ it "should not defer rejection with a new promise" do
+ deferred2 = @loop.defer
+ @loop.run {
+ @promise.then(@default_fail, @default_fail)
+ begin
+ @deferred.reject(deferred2.promise)
+ rescue => e
+ @log << e.is_a?(ArgumentError)
+ @loop.stop
+ end
+ }
+
+ @log.should == [true]
+ end
+
+ end
+
+
+ describe 'notify' do
+ it "should execute all progress callbacks in the registration order" do
+ @loop.run {
+ @promise.progress do |update|
+ @log << :first
+ end
+
+ @promise.progress do |update|
+ @log << :second
+ end
+
+ @deferred.notify(:foo)
+
+ @loop.next_tick do
+ @loop.stop
+ end
+ }
+
+ @log.should == [:first, :second]
+ end
+
+ it "should do nothing if a promise was previously resolved" do
+ @loop.run {
+
+ @promise.progress do |update|
+ @log << update
+ end
+
+ @deferred.resolve(:foo)
+ @deferred.notify(:baz)
+
+
+ #
+ # 4 ticks should detect any errors
+ #
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.stop
+ end
+ end
+ end
+ end
+ }
+
+ @log.should == []
+ end
+
+ it "should do nothing if a promise was previously rejected" do
+ @loop.run {
+
+ @promise.progress do |update|
+ @log << update
+ end
+ @deferred.reject(:foo)
+ @deferred.notify(:baz)
+
+
+
+ #
+ # 4 ticks should detect any errors
+ #
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.stop
+ end
+ end
+ end
+ end
+ }
+
+ @log.should == []
+ end
+
+
+ it "should not apply any special treatment to promises passed to notify" do
+ @loop.run {
+ deferred2 = @loop.defer
+
+ @promise.progress do |update|
+ @log << update.is_a?(::Libuv::Q::Promise)
+ end
+ @deferred.notify(deferred2.promise)
+
+ @loop.next_tick do
+ @loop.stop
+ end
+ }
+
+ @log.should == [true]
+ end
+
+
+ it "should call the progress callbacks in the next turn" do
+ @loop.run {
+ @promise.progress do |update|
+ @log << :first
+ end
+
+ @promise.progress do |update|
+ @log << :second
+ end
+
+ @deferred.notify(:foo)
+
+ @log << @log.length # Has notify run in this tick
+ @loop.stop # Stop will run through the next tick before stopping
+ }
+
+ @log.should == [0, :first, :second]
+ end
+
+ it "should ignore notifications sent out in the same turn before listener registration" do
+ @loop.run {
+ @deferred.notify(:foo)
+
+ @promise.progress do |update|
+ @log << :first
+ end
+
+ @promise.progress do |update|
+ @log << :second
+ end
+
+ @loop.next_tick do
+ @loop.stop
+ end
+ }
+
+ @log.should == []
+ end
+ end
+
+
+ describe Libuv::Q::Promise do
+
+ describe 'then' do
+
+ it "should allow registration of a success callback without an errback and resolve" do
+ @loop.run {
+ @promise.then do |result|
+ @log << result
+ end
+
+ @deferred.resolve(:foo)
+
+ @loop.next_tick do
+ @loop.stop
+ end
+ }
+
+ @log.should == [:foo]
+ end
+
+
+ it "should allow registration of a success callback without an errback and reject" do
+ @loop.run {
+ @promise.then do |result|
+ @log << result
+ end
+
+ @deferred.reject(:foo)
+
+ @loop.next_tick do
+ @loop.stop
+ end
+ }
+
+ @log.should == []
+ end
+
+
+ it "should allow registration of an errback without a success callback and reject" do
+ @loop.run {
+ @promise.catch(proc {|reason|
+ @log << reason
+ })
+
+ @deferred.reject(:foo)
+
+ @loop.next_tick do
+ @loop.stop
+ end
+ }
+
+ @log.should == [:foo]
+ end
+
+
+ it "should allow registration of an errback without a success callback and resolve" do
+ @loop.run {
+ @promise.catch(proc {|reason|
+ @log << reason
+ })
+
+ @deferred.resolve(:foo)
+
+ @loop.next_tick do
+ @loop.stop
+ end
+ }
+
+ @log.should == []
+ end
+
+
+ it "should resolve all callbacks with the original value" do
+ @loop.run {
+ @promise.then nil, @default_fail do |result|
+ @log << result
+ :alt1
+ end
+ @promise.then nil, @default_fail do |result|
+ @log << result
+ 'ERROR'
+ end
+ @promise.then nil, @default_fail do |result|
+ @log << result
+ Libuv::Q.reject(@loop, 'some reason')
+ end
+ @promise.then nil, @default_fail do |result|
+ @log << result
+ :alt2
+ end
+
+ @deferred.resolve(:foo)
+
+ @loop.next_tick do
+ @loop.stop
+ end
+ }
+
+ @log.should == [:foo, :foo, :foo, :foo]
+ end
+
+
+ it "should notify all callbacks with the original value" do
+ @loop.run { |loop_promise|
+ @promise.progress do |result|
+ @log << result
+ :alt1
+ end
+ @promise.progress do |result|
+ @log << result
+ 'ERROR'
+ end
+ @promise.progress do |result|
+ @log << result
+ Libuv::Q.reject(@loop, 'some reason')
+ end
+ @promise.progress do |result|
+ @log << result
+ :alt2
+ end
+
+
+ @deferred.notify(:foo)
+
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.stop
+ end
+ end
+ end
+ end
+ end
+ }
+
+ @log.should == [:foo, :foo, :foo, :foo]
+ end
+
+
+ it "should reject all callbacks with the original reason" do
+ @loop.run {
+ @promise.then(@default_fail, proc {|result|
+ @log << result
+ :alt1
+ })
+ @promise.then(@default_fail, proc {|result|
+ @log << result
+ 'ERROR'
+ })
+ @promise.then(@default_fail, proc {|result|
+ @log << result
+ Libuv::Q.reject(@loop, 'some reason')
+ })
+ @promise.then(@default_fail, proc {|result|
+ @log << result
+ :alt2
+ })
+
+ @deferred.reject(:foo)
+
+ @loop.next_tick do
+ @loop.stop
+ end
+ }
+
+ @log.should == [:foo, :foo, :foo, :foo]
+ end
+
+
+ it "should propagate resolution and rejection between dependent promises" do
+ @loop.run {
+ @promise.then(proc { |result|
+ @log << result
+ :bar
+ }, @default_fail).then(proc { |result|
+ @log << result
+ raise 'baz'
+ }, @default_fail).then(@default_fail, proc {|result|
+ @log << result.message
+ raise 'bob'
+ }).then(@default_fail, proc {|result|
+ @log << result.message
+ :done
+ }).then(proc { |result|
+ @log << result
+ }, @default_fail)
+
+ @deferred.resolve(:foo)
+
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do # extra tick?
+ @loop.stop
+ end
+ end
+ end
+ end
+ end
+ end
+ }
+
+ @log.should == [:foo, :bar, 'baz', 'bob', :done]
+ end
+
+
+ it "should propagate notification between dependent promises" do
+ @loop.run { |loop_promise|
+ loop_promise.progress do |type, id, error|
+ @log << id
+ end
+
+
+ @promise.progress(proc { |result|
+ @log << result
+ :bar
+ }).progress(proc { |result|
+ @log << result
+ result
+ }).progress(proc {|result|
+ @log << result
+ result
+ }).progress(proc {|result|
+ @log << result
+ :done
+ }).progress(proc { |result|
+ @log << result
+ result
+ })
+
+
+ @deferred.notify(:foo)
+
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do # extra tick?
+ @loop.stop
+ end
+ end
+ end
+ end
+ end
+ end
+ }
+
+ @log.should == [:foo, :bar, :bar, :bar, :done]
+ end
+
+
+ it "should stop notification propagation in case of error" do
+ @loop.run { |loop_logger|
+ loop_logger.progress do |type, id, error|
+ @log << id
+ end
+
+
+ @promise.progress(proc { |result|
+ @log << result
+ :bar
+ }).progress(proc { |result|
+ @log << result
+ raise 'err'
+ result
+ }).progress(proc {|result|
+ @log << result
+ result
+ }).progress(proc {|result|
+ @log << result
+ :done
+ }).progress(proc { |result|
+ @log << result
+ result
+ })
+
+
+ @deferred.notify(:foo)
+
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.stop
+ end
+ end
+ end
+ end
+ end
+ }
+
+ @log.should == [:foo, :bar, :q_progress_cb]
+ end
+
+
+ it "should call error callback in the next turn even if promise is already rejected" do
+ @loop.run {
+ @deferred.reject(:foo)
+
+ @promise.catch(proc {|reason|
+ @log << reason
+ })
+
+ @loop.next_tick do
+ @loop.stop
+ end
+ }
+
+ @log.should == [:foo]
+ end
+
+
+ end
+
+
+ describe 'finally' do
+
+ describe 'when the promise is fulfilled' do
+
+ it "should call the callback" do
+ @loop.run {
+ @promise.finally do
+ @log << :finally
+ end
+
+ @deferred.resolve(:foo)
+
+ @loop.next_tick do
+ @loop.stop
+ end
+ }
+
+ @log.should == [:finally]
+ end
+
+ it "should fulfill with the original value" do
+ @loop.run {
+ @promise.finally(proc {
+ @log << :finally
+ :finally
+ }).then do |result|
+ @log << result
+ end
+
+
+ @deferred.resolve(:foo)
+
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.stop
+ end
+ end
+ }
+
+ @log.should == [:finally, :foo]
+ end
+
+ it "should fulfill with the original value (larger test)" do
+ @loop.run {
+ @promise.then(proc { |result|
+ @log << result
+ result
+ }).finally(proc {
+ @log << :finally
+ :finally
+ }).then(proc { |result|
+ @log << result
+ :change
+ }).then(proc { |result|
+ @log << result
+ result
+ }).finally(proc {
+ @log << :finally
+ :finally
+ }).then(proc { |result|
+ @log << result
+ result
+ })
+
+
+ @deferred.resolve(:foo)
+
+
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.stop
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ }
+
+ @log.should == [:foo, :finally, :foo, :change, :finally, :change]
+ end
+
+ describe "when the callback throws an exception" do
+ it "should reject with this new exception" do
+ @loop.run {
+ @promise.finally(proc {
+ @log << :finally
+ raise 'error'
+ }).catch do |reason|
+ @log.push reason.is_a?(Exception)
+ end
+
+ @deferred.resolve(:foo)
+
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.stop
+ end
+ end
+ }
+
+ @log.should == [:finally, true]
+ end
+ end
+
+ describe "when the callback returns a promise" do
+ it "should fulfill with the original reason after that promise resolves" do
+ @loop.run {
+ deferred2 = @loop.defer
+
+ @promise.finally(proc {
+ @log << :finally
+ deferred2.promise
+ }).then do |result|
+ @log << result
+ end
+
+ @deferred.resolve(:foo)
+
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @log << :resolving
+ deferred2.resolve('working')
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.stop
+ end
+ end
+ end
+ end
+ end
+ end
+ }
+
+ @log.should == [:finally, :resolving, :foo]
+ end
+
+
+ it "should reject with the new reason when it is rejected" do
+ @loop.run {
+ deferred2 = @loop.defer
+
+ @promise.finally(proc {
+ @log << :finally
+ deferred2.promise
+ }).catch do |result|
+ @log << result
+ end
+
+ @deferred.resolve(:foo)
+
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.next_tick do
+ @log << :rejecting
+ deferred2.reject(:rejected)
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.stop
+ end
+ end
+ end
+ end
+ end
+ end
+ }
+
+ @log.should == [:finally, :rejecting, :rejected]
+ end
+ end
+
+ end
+
+ end
+
+ end
+
+
+
+ describe 'reject' do
+
+ it "should package a string into a rejected promise" do
+ @loop.run {
+ rejectedPromise = Libuv::Q.reject(@loop, 'not gonna happen')
+
+ @promise.then(@default_fail, proc {|reason|
+ @log << reason
+ })
+
+ @deferred.resolve(rejectedPromise)
+
+ @loop.next_tick do
+ @loop.stop
+ end
+ }
+
+ @log.should == ['not gonna happen']
+ end
+
+
+ it "should return a promise that forwards callbacks if the callbacks are missing" do
+ @loop.run {
+ rejectedPromise = Libuv::Q.reject(@loop, 'not gonna happen')
+
+ @promise.then(@default_fail, proc {|reason|
+ @log << reason
+ })
+
+ @deferred.resolve(rejectedPromise.then())
+
+ @loop.next_tick do
+ @loop.next_tick do
+ @loop.stop
+ end
+ end
+ }
+
+ @log.should == ['not gonna happen']
+ end
+
+ end
+
+
+
+ describe 'all' do
+
+ it "should resolve all of nothing" do
+ @loop.run {
+ Libuv::Q.all(@loop).then nil, @default_fail do |result|
+ @log << result
+ end
+
+ @loop.next_tick do
+ @loop.stop
+ end
+ }
+
+ @log.should == [[]]
+ end
+
+ it "should take an array of promises and return a promise for an array of results" do
+ @loop.run {
+ deferred1 = @loop.defer
+ deferred2 = @loop.defer
+
+ Libuv::Q.all(@loop, @promise, deferred1.promise, deferred2.promise).then nil, @default_fail do |result|
+ @log = result
+ @loop.stop
+ end
+
+ @loop.work { @deferred.resolve(:foo) }
+ @loop.work { deferred2.resolve(:baz) }
+ @loop.work { deferred1.resolve(:bar) }
+ }
+
+ @log.should == [:foo, :bar, :baz]
+ end
+
+
+ it "should reject the derived promise if at least one of the promises in the array is rejected" do
+ @loop.run {
+ deferred1 = @loop.defer
+ deferred2 = @loop.defer
+
+ Libuv::Q.all(@loop, @promise, deferred1.promise, deferred2.promise).then(@default_fail, proc {|reason|
+ @log << reason
+ @loop.stop
+ })
+
+ @loop.work { @deferred.resolve(:foo) }
+ @loop.work { deferred2.reject(:baz) }
+ }
+
+ @log.should == [:baz]
+ end
+
+ end
+
+end