2018-03-04 10:09:32 -05:00
|
|
|
require_relative 'spec_helper'
|
2017-05-07 08:04:49 -04:00
|
|
|
|
|
|
|
load_extension('io')
|
|
|
|
|
|
|
|
describe "C-API IO function" do
|
|
|
|
before :each do
|
|
|
|
@o = CApiIOSpecs.new
|
|
|
|
|
|
|
|
@name = tmp("c_api_rb_io_specs")
|
|
|
|
touch @name
|
|
|
|
|
2019-04-28 17:20:11 -04:00
|
|
|
@io = new_io @name, "w:utf-8"
|
2017-05-07 08:04:49 -04:00
|
|
|
@io.sync = true
|
|
|
|
end
|
|
|
|
|
|
|
|
after :each do
|
|
|
|
@io.close unless @io.closed?
|
|
|
|
rm_r @name
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "rb_io_addstr" do
|
|
|
|
it "calls #to_s to convert the object to a String" do
|
|
|
|
obj = mock("rb_io_addstr string")
|
|
|
|
obj.should_receive(:to_s).and_return("rb_io_addstr data")
|
|
|
|
|
|
|
|
@o.rb_io_addstr(@io, obj)
|
|
|
|
File.read(@name).should == "rb_io_addstr data"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "writes the String to the IO" do
|
|
|
|
@o.rb_io_addstr(@io, "rb_io_addstr data")
|
|
|
|
File.read(@name).should == "rb_io_addstr data"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns the io" do
|
|
|
|
@o.rb_io_addstr(@io, "rb_io_addstr data").should eql(@io)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "rb_io_printf" do
|
|
|
|
it "calls #to_str to convert the format object to a String" do
|
|
|
|
obj = mock("rb_io_printf format")
|
|
|
|
obj.should_receive(:to_str).and_return("%s")
|
|
|
|
|
|
|
|
@o.rb_io_printf(@io, [obj, "rb_io_printf"])
|
|
|
|
File.read(@name).should == "rb_io_printf"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "calls #to_s to convert the object to a String" do
|
|
|
|
obj = mock("rb_io_printf string")
|
|
|
|
obj.should_receive(:to_s).and_return("rb_io_printf")
|
|
|
|
|
|
|
|
@o.rb_io_printf(@io, ["%s", obj])
|
|
|
|
File.read(@name).should == "rb_io_printf"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "writes the Strings to the IO" do
|
|
|
|
@o.rb_io_printf(@io, ["%s_%s_%s", "rb", "io", "printf"])
|
|
|
|
File.read(@name).should == "rb_io_printf"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "rb_io_print" do
|
|
|
|
it "calls #to_s to convert the object to a String" do
|
|
|
|
obj = mock("rb_io_print string")
|
|
|
|
obj.should_receive(:to_s).and_return("rb_io_print")
|
|
|
|
|
|
|
|
@o.rb_io_print(@io, [obj])
|
|
|
|
File.read(@name).should == "rb_io_print"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "writes the Strings to the IO with no separator" do
|
|
|
|
@o.rb_io_print(@io, ["rb_", "io_", "print"])
|
|
|
|
File.read(@name).should == "rb_io_print"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "rb_io_puts" do
|
|
|
|
it "calls #to_s to convert the object to a String" do
|
|
|
|
obj = mock("rb_io_puts string")
|
|
|
|
obj.should_receive(:to_s).and_return("rb_io_puts")
|
|
|
|
|
|
|
|
@o.rb_io_puts(@io, [obj])
|
|
|
|
File.read(@name).should == "rb_io_puts\n"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "writes the Strings to the IO separated by newlines" do
|
|
|
|
@o.rb_io_puts(@io, ["rb", "io", "write"])
|
|
|
|
File.read(@name).should == "rb\nio\nwrite\n"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "rb_io_write" do
|
|
|
|
it "calls #to_s to convert the object to a String" do
|
|
|
|
obj = mock("rb_io_write string")
|
|
|
|
obj.should_receive(:to_s).and_return("rb_io_write")
|
|
|
|
|
|
|
|
@o.rb_io_write(@io, obj)
|
|
|
|
File.read(@name).should == "rb_io_write"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "writes the String to the IO" do
|
|
|
|
@o.rb_io_write(@io, "rb_io_write")
|
|
|
|
File.read(@name).should == "rb_io_write"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "C-API IO function" do
|
|
|
|
before :each do
|
|
|
|
@o = CApiIOSpecs.new
|
|
|
|
|
|
|
|
@name = tmp("c_api_io_specs")
|
|
|
|
touch @name
|
|
|
|
|
2019-04-28 17:20:11 -04:00
|
|
|
@io = new_io @name, "r:utf-8"
|
2017-05-07 08:04:49 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
after :each do
|
|
|
|
@io.close unless @io.closed?
|
|
|
|
rm_r @name
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "rb_io_close" do
|
|
|
|
it "closes an IO object" do
|
|
|
|
@io.closed?.should be_false
|
|
|
|
@o.rb_io_close(@io)
|
|
|
|
@io.closed?.should be_true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "rb_io_check_io" do
|
|
|
|
it "returns the IO object if it is valid" do
|
|
|
|
@o.rb_io_check_io(@io).should == @io
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns nil for non IO objects" do
|
|
|
|
@o.rb_io_check_io({}).should be_nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "rb_io_check_closed" do
|
|
|
|
it "does not raise an exception if the IO is not closed" do
|
|
|
|
# The MRI function is void, so we use should_not raise_error
|
2019-07-27 06:40:09 -04:00
|
|
|
-> { @o.rb_io_check_closed(@io) }.should_not raise_error
|
2017-05-07 08:04:49 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it "raises an error if the IO is closed" do
|
|
|
|
@io.close
|
2019-07-27 06:40:09 -04:00
|
|
|
-> { @o.rb_io_check_closed(@io) }.should raise_error(IOError)
|
2017-05-07 08:04:49 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# NOTE: unlike the name might suggest in MRI this function checks if an
|
|
|
|
# object is frozen, *not* if it's tainted.
|
|
|
|
describe "rb_io_taint_check" do
|
|
|
|
it "does not raise an exception if the IO is not frozen" do
|
2019-07-27 06:40:09 -04:00
|
|
|
-> { @o.rb_io_taint_check(@io) }.should_not raise_error
|
2017-05-07 08:04:49 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it "raises an exception if the IO is frozen" do
|
|
|
|
@io.freeze
|
|
|
|
|
2019-07-27 06:40:09 -04:00
|
|
|
-> { @o.rb_io_taint_check(@io) }.should raise_error(RuntimeError)
|
2017-05-07 08:04:49 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "GetOpenFile" do
|
|
|
|
it "allows access to the system fileno" do
|
|
|
|
@o.GetOpenFile_fd($stdin).should == 0
|
|
|
|
@o.GetOpenFile_fd($stdout).should == 1
|
|
|
|
@o.GetOpenFile_fd($stderr).should == 2
|
|
|
|
@o.GetOpenFile_fd(@io).should == @io.fileno
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "rb_io_binmode" do
|
|
|
|
it "returns self" do
|
|
|
|
@o.rb_io_binmode(@io).should == @io
|
|
|
|
end
|
|
|
|
|
|
|
|
it "sets binmode" do
|
|
|
|
@o.rb_io_binmode(@io)
|
|
|
|
@io.binmode?.should be_true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "C-API IO function" do
|
|
|
|
before :each do
|
|
|
|
@o = CApiIOSpecs.new
|
|
|
|
@r_io, @w_io = IO.pipe
|
|
|
|
|
|
|
|
@name = tmp("c_api_io_specs")
|
|
|
|
touch @name
|
2019-04-28 17:20:11 -04:00
|
|
|
@rw_io = new_io @name, "w+"
|
2017-05-07 08:04:49 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
after :each do
|
|
|
|
@r_io.close unless @r_io.closed?
|
|
|
|
@w_io.close unless @w_io.closed?
|
|
|
|
@rw_io.close unless @rw_io.closed?
|
|
|
|
rm_r @name
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "rb_io_check_readable" do
|
|
|
|
it "does not raise an exception if the IO is opened for reading" do
|
|
|
|
# The MRI function is void, so we use should_not raise_error
|
2019-07-27 06:40:09 -04:00
|
|
|
-> { @o.rb_io_check_readable(@r_io) }.should_not raise_error
|
2017-05-07 08:04:49 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it "does not raise an exception if the IO is opened for read and write" do
|
2019-07-27 06:40:09 -04:00
|
|
|
-> { @o.rb_io_check_readable(@rw_io) }.should_not raise_error
|
2017-05-07 08:04:49 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it "raises an IOError if the IO is not opened for reading" do
|
2019-07-27 06:40:09 -04:00
|
|
|
-> { @o.rb_io_check_readable(@w_io) }.should raise_error(IOError)
|
2017-05-07 08:04:49 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "rb_io_check_writable" do
|
|
|
|
it "does not raise an exeption if the IO is opened for writing" do
|
|
|
|
# The MRI function is void, so we use should_not raise_error
|
2019-07-27 06:40:09 -04:00
|
|
|
-> { @o.rb_io_check_writable(@w_io) }.should_not raise_error
|
2017-05-07 08:04:49 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it "does not raise an exception if the IO is opened for read and write" do
|
2019-07-27 06:40:09 -04:00
|
|
|
-> { @o.rb_io_check_writable(@rw_io) }.should_not raise_error
|
2017-05-07 08:04:49 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it "raises an IOError if the IO is not opened for reading" do
|
2019-07-27 06:40:09 -04:00
|
|
|
-> { @o.rb_io_check_writable(@r_io) }.should raise_error(IOError)
|
2017-05-07 08:04:49 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-10-16 15:42:33 -04:00
|
|
|
describe "rb_io_wait_writable" do
|
2017-05-07 08:04:49 -04:00
|
|
|
it "returns false if there is no error condition" do
|
2018-08-14 13:07:36 -04:00
|
|
|
@o.errno = 0
|
2017-05-07 08:04:49 -04:00
|
|
|
@o.rb_io_wait_writable(@w_io).should be_false
|
|
|
|
end
|
|
|
|
|
|
|
|
it "raises an IOError if the IO is closed" do
|
|
|
|
@w_io.close
|
2019-07-27 06:40:09 -04:00
|
|
|
-> { @o.rb_io_wait_writable(@w_io) }.should raise_error(IOError)
|
2017-05-07 08:04:49 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "rb_thread_fd_writable" do
|
|
|
|
it "waits til an fd is ready for writing" do
|
|
|
|
@o.rb_thread_fd_writable(@w_io).should be_nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-05-12 06:10:15 -04:00
|
|
|
platform_is_not :windows do
|
|
|
|
describe "rb_io_wait_readable" do
|
|
|
|
it "returns false if there is no error condition" do
|
2018-08-14 13:07:36 -04:00
|
|
|
@o.errno = 0
|
2017-05-12 06:10:15 -04:00
|
|
|
@o.rb_io_wait_readable(@r_io, false).should be_false
|
|
|
|
end
|
2017-05-07 08:04:49 -04:00
|
|
|
|
2017-05-12 06:10:15 -04:00
|
|
|
it "raises and IOError if passed a closed stream" do
|
|
|
|
@r_io.close
|
2019-07-27 06:40:09 -04:00
|
|
|
-> {
|
2017-05-27 17:55:02 -04:00
|
|
|
@o.rb_io_wait_readable(@r_io, false)
|
|
|
|
}.should raise_error(IOError)
|
2017-05-07 08:04:49 -04:00
|
|
|
end
|
|
|
|
|
2017-05-12 06:10:15 -04:00
|
|
|
it "blocks until the io is readable and returns true" do
|
|
|
|
@o.instance_variable_set :@write_data, false
|
|
|
|
thr = Thread.new do
|
2017-05-27 17:55:02 -04:00
|
|
|
Thread.pass until @o.instance_variable_get(:@write_data)
|
2017-05-12 06:10:15 -04:00
|
|
|
@w_io.write "rb_io_wait_readable"
|
|
|
|
end
|
2017-05-07 08:04:49 -04:00
|
|
|
|
2018-08-14 13:07:36 -04:00
|
|
|
@o.errno = Errno::EAGAIN.new.errno
|
2017-05-12 06:10:15 -04:00
|
|
|
@o.rb_io_wait_readable(@r_io, true).should be_true
|
|
|
|
@o.instance_variable_get(:@read_data).should == "rb_io_wait_re"
|
|
|
|
|
|
|
|
thr.join
|
|
|
|
end
|
2017-05-07 08:04:49 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "rb_thread_wait_fd" do
|
|
|
|
it "waits til an fd is ready for reading" do
|
|
|
|
start = false
|
|
|
|
thr = Thread.new do
|
|
|
|
start = true
|
2017-05-27 17:55:02 -04:00
|
|
|
sleep 0.05
|
2017-05-07 08:04:49 -04:00
|
|
|
@w_io.write "rb_io_wait_readable"
|
|
|
|
end
|
|
|
|
|
|
|
|
Thread.pass until start
|
|
|
|
|
|
|
|
@o.rb_thread_wait_fd(@r_io).should be_nil
|
|
|
|
|
|
|
|
thr.join
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-04-27 12:53:23 -04:00
|
|
|
describe "rb_wait_for_single_fd" do
|
|
|
|
it "waits til an fd is ready for reading" do
|
|
|
|
start = false
|
|
|
|
thr = Thread.new do
|
|
|
|
start = true
|
|
|
|
sleep 0.05
|
|
|
|
@w_io.write "rb_io_wait_readable"
|
|
|
|
end
|
|
|
|
|
|
|
|
Thread.pass until start
|
|
|
|
|
|
|
|
@o.rb_wait_for_single_fd(@r_io, 1, nil, nil).should == 1
|
|
|
|
|
|
|
|
thr.join
|
|
|
|
end
|
|
|
|
|
|
|
|
it "polls whether an fd is ready for reading if timeout is 0" do
|
|
|
|
@o.rb_wait_for_single_fd(@r_io, 1, 0, 0).should == 0
|
|
|
|
end
|
|
|
|
end
|
2017-05-07 08:04:49 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
describe "rb_fd_fix_cloexec" do
|
|
|
|
|
|
|
|
before :each do
|
|
|
|
@o = CApiIOSpecs.new
|
|
|
|
|
|
|
|
@name = tmp("c_api_rb_io_specs")
|
|
|
|
touch @name
|
|
|
|
|
2019-04-28 17:20:11 -04:00
|
|
|
@io = new_io @name, "w:utf-8"
|
2017-05-07 08:04:49 -04:00
|
|
|
@io.close_on_exec = false
|
|
|
|
@io.sync = true
|
|
|
|
end
|
|
|
|
|
|
|
|
after :each do
|
|
|
|
@io.close unless @io.closed?
|
|
|
|
rm_r @name
|
|
|
|
end
|
|
|
|
|
|
|
|
it "sets close_on_exec on the IO" do
|
|
|
|
@o.rb_fd_fix_cloexec(@io)
|
|
|
|
@io.close_on_exec?.should be_true
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "rb_cloexec_open" do
|
|
|
|
before :each do
|
|
|
|
@o = CApiIOSpecs.new
|
|
|
|
@name = tmp("c_api_rb_io_specs")
|
|
|
|
touch @name
|
|
|
|
|
|
|
|
@io = nil
|
|
|
|
end
|
|
|
|
|
|
|
|
after :each do
|
|
|
|
@io.close unless @io.nil? || @io.closed?
|
|
|
|
rm_r @name
|
|
|
|
end
|
|
|
|
|
|
|
|
it "sets close_on_exec on the newly-opened IO" do
|
|
|
|
@io = @o.rb_cloexec_open(@name, 0, 0)
|
|
|
|
@io.close_on_exec?.should be_true
|
|
|
|
end
|
|
|
|
end
|