spec/defer.rb in em-promise-1.0.1 vs spec/defer.rb in em-promise-1.0.2

- old
+ new

@@ -3,28 +3,484 @@ require 'em-promise' describe EventMachine::Defer do + + before :each do + @deferred = EM::Defer.new + @promise = @deferred.promise + @log = [] + @default_fail = proc { |reason| + fail(reason) + EM.stop + } + end + + + describe 'resolve' do + + + it "should call the callback in the next turn" do + deferred2 = EM::Defer.new + EventMachine.run { + @promise.then(proc {|result| + @log << result + }, @default_fail) + + @deferred.resolve(:foo) + + EM.next_tick do + @log.should == [:foo] + EM.stop + end + } + end + + + it "should fulfill success callbacks in the registration order" do + EventMachine.run { + @promise.then(proc {|result| + @log << :first + }, @default_fail) + + @promise.then(proc {|result| + @log << :second + }, @default_fail) + + @deferred.resolve(:foo) + + EM.next_tick do + @log.should == [:first, :second] + EM.stop + end + } + end + + + it "should do nothing if a promise was previously resolved" do + EventMachine.run { + @promise.then(proc {|result| + @log << result + @log.should == [:foo] + @deferred.resolve(:bar) + }, @default_fail) + + @deferred.resolve(:foo) + @deferred.reject(:baz) + + # + # 4 ticks should detect any errors + # + EM.next_tick do + EM.next_tick do + EM.next_tick do + EM.next_tick do + @log.should == [:foo] + EM.stop + end + end + end + end + } + end + + + it "should allow deferred resolution with a new promise" do + deferred2 = EM::Defer.new + EventMachine.run { + @promise.then(proc {|result| + result.should == :foo + EM.stop + }, @default_fail) + + @deferred.resolve(deferred2.promise) + deferred2.resolve(:foo) + } + end + + + it "should not break if a callbacks registers another callback" do + EventMachine.run { + @promise.then(proc {|result| + @log << :outer + @promise.then(proc {|result| + @log << :inner + }, @default_fail) + }, @default_fail) + + @deferred.resolve(:foo) + + EM.next_tick do + EM.next_tick do + @log.should == [:outer, :inner] + EM.stop + end + end + } + end + + + + it "can modify the result of a promise before returning" do + EventMachine.run { + proc { |name| + EM.defer { @deferred.resolve("Hello #{name}") } + @promise.then(proc {|result| + result.should == 'Hello Robin Hood' + result += "?" + result + }) + }.call('Robin Hood').then(proc { |greeting| + greeting.should == 'Hello Robin Hood?' + EM.stop + }, @default_fail) + } + end + + end + + + describe 'reject' do + + it "should reject the promise and execute all error callbacks" do + EventMachine.run { + @promise.then(@default_fail, proc {|result| + @log << :first + }) + @promise.then(@default_fail, proc {|result| + @log << :second + }) + + @deferred.reject(:foo) + + EM.next_tick do + @log.should == [:first, :second] + EM.stop + end + } + end + + + it "should do nothing if a promise was previously rejected" do + EventMachine.run { + @promise.then(@default_fail, proc {|result| + @log << result + @log.should == [:baz] + @deferred.resolve(:bar) + }) + + @deferred.reject(:baz) + @deferred.resolve(:foo) + + # + # 4 ticks should detect any errors + # + EM.next_tick do + EM.next_tick do + EM.next_tick do + EM.next_tick do + @log.should == [:baz] + EM.stop + end + end + end + end + } + end + + + it "should not defer rejection with a new promise" do + deferred2 = EM::Defer.new + EventMachine.run { + @promise.then(@default_fail, @default_fail) + begin + @deferred.reject(deferred2.promise) + rescue => e + e.is_a?(ArgumentError).should == true + EM.stop + end + } + end + + end + + + describe EventMachine::Defer::Promise do + + describe 'then' do + + it "should allow registration of a success callback without an errback and resolve" do + EventMachine.run { + @promise.then(proc {|result| + @log << result + }) + @deferred.resolve(:foo) + + EM.next_tick do + @log.should == [:foo] + EM.stop + end + } + end + + + it "should allow registration of a success callback without an errback and reject" do + EventMachine.run { + @promise.then(proc {|result| + @log << result + }) + + @deferred.reject(:foo) + + EM.next_tick do + @log.should == [] + EM.stop + end + } + end + + + it "should allow registration of an errback without a success callback and reject" do + EventMachine.run { + @promise.then(nil, proc {|reason| + @log << reason + }) + + @deferred.reject(:foo) + + EM.next_tick do + @log.should == [:foo] + EM.stop + end + } + end + + + it "should allow registration of an errback without a success callback and resolve" do + EventMachine.run { + @promise.then(nil, proc {|reason| + @log << reason + }) + + @deferred.resolve(:foo) + + EM.next_tick do + @log.should == [] + EM.stop + end + } + end + + + it "should resolve all callbacks with the original value" do + EventMachine.run { + @promise.then(proc {|result| + @log << result + :alt1 + }, @default_fail) + @promise.then(proc {|result| + @log << result + 'ERROR' + }, @default_fail) + @promise.then(proc {|result| + @log << result + EM::Defer.reject('some reason') + }, @default_fail) + @promise.then(proc {|result| + @log << result + :alt2 + }, @default_fail) + + @deferred.resolve(:foo) + + EM.next_tick do + @log.should == [:foo, :foo, :foo, :foo] + EM.stop + end + } + end + + + it "should reject all callbacks with the original reason" do + EventMachine.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 + EM::Defer.reject('some reason') + }) + @promise.then(@default_fail, proc {|result| + @log << result + :alt2 + }) + + @deferred.reject(:foo) + + EM.next_tick do + @log.should == [:foo, :foo, :foo, :foo] + EM.stop + end + } + end + + + it "should propagate resolution and rejection between dependent promises" do + EventMachine.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) + + EM.next_tick do + EM.next_tick do + EM.next_tick do + EM.next_tick do + EM.next_tick do + @log.should == [:foo, :bar, 'baz', 'bob', :done] + EM.stop + end + end + end + end + end + } + end + + + it "should call error callback in the next turn even if promise is already rejected" do + EventMachine.run { + @deferred.reject(:foo) + + @promise.then(nil, proc {|reason| + @log << reason + }) + + EM.next_tick do + @log.should == [:foo] + EM.stop + end + } + end + + + end + + end - it "should fulfill the promise and execute all success callbacks in the registration order" do - EventMachine.run { - proc { |name| - deferred = EM::Defer.new - EM.defer { deferred.resolve("Hello #{name}") } - deferred.promise.then(proc {|result| - result += "?" - result + + + describe 'reject' do + + it "should package a string into a rejected promise" do + EventMachine.run { + rejectedPromise = EM::Defer.reject('not gonna happen') + + @promise.then(nil, proc {|reason| + @log << reason }) - }.call('Robin Hood').then(proc { |greeting| - greeting.should == 'Hello Robin Hood?' - EventMachine.stop - }, proc { |reason| - fail(reason) - EventMachine.stop - }) - } + + @deferred.resolve(rejectedPromise) + + EM.next_tick do + @log.should == ['not gonna happen'] + EM.stop + end + } + end + + + it "should return a promise that forwards callbacks if the callbacks are missing" do + EventMachine.run { + rejectedPromise = EM::Defer.reject('not gonna happen') + + @promise.then(nil, proc {|reason| + @log << reason + }) + + @deferred.resolve(rejectedPromise.then()) + + EM.next_tick do + EM.next_tick do + @log.should == ['not gonna happen'] + EM.stop + end + end + } + end + end + + + + describe 'all' do + + it "should resolve all of nothing" do + EventMachine.run { + EM::Defer.all().then(proc {|result| + @log << result + }, @default_fail) + + EM.next_tick do + @log.should == [[]] + EM.stop + end + } + end + + it "should take an array of promises and return a promise for an array of results" do + EventMachine.run { + deferred1 = EM::Defer.new + deferred2 = EM::Defer.new + + EM::Defer.all(@promise, deferred1.promise, deferred2.promise).then(proc {|result| + result.should == [:foo, :bar, :baz] + EM.stop + }, @default_fail) + + EM.defer { @deferred.resolve(:foo) } + EM.defer { deferred2.resolve(:baz) } + EM.defer { deferred1.resolve(:bar) } + } + end + + + it "should reject the derived promise if at least one of the promises in the array is rejected" do + EventMachine.run { + deferred1 = EM::Defer.new + deferred2 = EM::Defer.new + + EM::Defer.all(@promise, deferred1.promise, deferred2.promise).then(@default_fail, proc {|reason| + reason.should == :baz + EM.stop + }) + + EM.defer { @deferred.resolve(:foo) } + EM.defer { deferred2.reject(:baz) } + } + end + + end + end