diff --git a/library/io-wait/wait_readable_spec.rb b/library/io-wait/wait_readable_spec.rb index d7473f029..ae433cdb0 100644 --- a/library/io-wait/wait_readable_spec.rb +++ b/library/io-wait/wait_readable_spec.rb @@ -1,4 +1,5 @@ require_relative '../../spec_helper' +require_relative '../../core/fiber/fixtures/scheduler' describe "IO#wait_readable" do before :each do @@ -39,4 +40,45 @@ rd.close wr.close end + + context "with a Fiber scheduler" do + before :each do + @read, @write = IO.pipe + Fiber.set_scheduler(FiberSpecs::LoggingScheduler.new) + end + + # An IO cannot be closed while a fiber is waiting on it, and the scheduler + # leaves the fiber parked inside #io_wait, so run it out before closing. + after :each do + @fiber.resume while @fiber&.alive? + Fiber.set_scheduler(nil) + @read.close unless @read.closed? + @write.close unless @write.closed? + end + + it "calls the scheduler's #io_wait with IO::READABLE and no timeout" do + @fiber = Fiber.new(blocking: false) { @read.wait_readable } + @fiber.resume + + Fiber.scheduler.events.should == [ + { event: :io_wait, fiber: @fiber, args: [@read, IO::READABLE, nil] } + ] + end + + it "passes the given timeout to the scheduler" do + @fiber = Fiber.new(blocking: false) { @read.wait_readable(1) } + @fiber.resume + + Fiber.scheduler.events.should == [ + { event: :io_wait, fiber: @fiber, args: [@read, IO::READABLE, 1] } + ] + end + + it "does not call the scheduler if the fiber is blocking" do + @fiber = Fiber.new(blocking: true) { @read.wait_readable(0) } + @fiber.resume + + Fiber.scheduler.events.should == [] + end + end end diff --git a/library/io-wait/wait_spec.rb b/library/io-wait/wait_spec.rb index 1d784e7aa..e0f295c77 100644 --- a/library/io-wait/wait_spec.rb +++ b/library/io-wait/wait_spec.rb @@ -1,5 +1,6 @@ require_relative '../../spec_helper' require_relative '../../fixtures/io' +require_relative '../../core/fiber/fixtures/scheduler' describe "IO#wait" do before :each do @@ -159,4 +160,52 @@ -> { @io.wait(0, :r) }.should.raise(IOError, "closed stream") end end + + context "with a Fiber scheduler" do + before :each do + Fiber.set_scheduler(FiberSpecs::LoggingScheduler.new) + end + + # An IO cannot be closed while a fiber is waiting on it, and the scheduler + # leaves the fiber parked inside #io_wait, so run it out before the outer + # after hook closes the pipe. + after :each do + @fiber.resume while @fiber&.alive? + Fiber.set_scheduler(nil) + end + + it "calls the scheduler's #io_wait with the events and timeout given" do + @fiber = Fiber.new(blocking: false) { @r.wait(IO::READABLE, 2) } + @fiber.resume + + Fiber.scheduler.events.should == [ + { event: :io_wait, fiber: @fiber, args: [@r, IO::READABLE, 2] } + ] + end + + it "calls the scheduler's #io_wait with IO::READABLE when no events are given" do + @fiber = Fiber.new(blocking: false) { @r.wait } + @fiber.resume + + Fiber.scheduler.events.should == [ + { event: :io_wait, fiber: @fiber, args: [@r, IO::READABLE, nil] } + ] + end + + it "translates a mode Symbol into the matching events mask" do + @fiber = Fiber.new(blocking: false) { @r.wait(0.5, :readable_writable) } + @fiber.resume + + Fiber.scheduler.events.should == [ + { event: :io_wait, fiber: @fiber, args: [@r, IO::READABLE | IO::WRITABLE, 0.5] } + ] + end + + it "does not call the scheduler if the fiber is blocking" do + @fiber = Fiber.new(blocking: true) { @r.wait(IO::READABLE, 0) } + @fiber.resume + + Fiber.scheduler.events.should == [] + end + end end diff --git a/library/io-wait/wait_writable_spec.rb b/library/io-wait/wait_writable_spec.rb index 2017817ca..6959b48e4 100644 --- a/library/io-wait/wait_writable_spec.rb +++ b/library/io-wait/wait_writable_spec.rb @@ -1,5 +1,6 @@ require_relative '../../spec_helper' require_relative '../../fixtures/io' +require_relative '../../core/fiber/fixtures/scheduler' describe "IO#wait_writable" do it "waits for the IO to become writable with no timeout" do @@ -34,4 +35,45 @@ rd.close unless rd.closed? wr.close unless wr.closed? end + + context "with a Fiber scheduler" do + before :each do + @read, @write = IO.pipe + Fiber.set_scheduler(FiberSpecs::LoggingScheduler.new) + end + + # An IO cannot be closed while a fiber is waiting on it, and the scheduler + # leaves the fiber parked inside #io_wait, so run it out before closing. + after :each do + @fiber.resume while @fiber&.alive? + Fiber.set_scheduler(nil) + @read.close unless @read.closed? + @write.close unless @write.closed? + end + + it "calls the scheduler's #io_wait with IO::WRITABLE and no timeout" do + @fiber = Fiber.new(blocking: false) { @write.wait_writable } + @fiber.resume + + Fiber.scheduler.events.should == [ + { event: :io_wait, fiber: @fiber, args: [@write, IO::WRITABLE, nil] } + ] + end + + it "passes the given timeout to the scheduler" do + @fiber = Fiber.new(blocking: false) { @write.wait_writable(1) } + @fiber.resume + + Fiber.scheduler.events.should == [ + { event: :io_wait, fiber: @fiber, args: [@write, IO::WRITABLE, 1] } + ] + end + + it "does not call the scheduler if the fiber is blocking" do + @fiber = Fiber.new(blocking: true) { @write.wait_writable(0) } + @fiber.resume + + Fiber.scheduler.events.should == [] + end + end end