Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions core/exception/system_call_error_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,23 @@ def initialize
end

it "sets an 'unknown error' message when an unknown error number" do
SystemCallError.new(-1).message.should =~ /Unknown error(:)? -1/
platform_is_not :windows do
SystemCallError.new(-1).message.should =~ /Unknown error(:)? -1/
end

platform_is :windows do
SystemCallError.new(-1).message.should == "The operation completed successfully."
end
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Observed strange behaviour on Windows. CRuby calls strerror on Windows to get a string description for errno and it returns unexpected strings for unsupported errno codes (e.g. negative or not listed in https://learn.microsoft.com/en-us/cpp/c-runtime-library/errno-constants)

end

it "adds a custom error message to an 'unknown error' message when an unknown error number and a custom message specified" do
SystemCallError.new("custom message", -1).message.should =~ /Unknown error(:)? -1 - custom message/
platform_is_not :windows do
SystemCallError.new("custom message", -1).message.should =~ /Unknown error(:)? -1 - custom message/
end

platform_is :windows do
SystemCallError.new("custom message", -1).message.should == "The operation completed successfully. - custom message"
end
end

it "converts to Integer if errno is a Complex convertible to Integer" do
Expand Down
188 changes: 100 additions & 88 deletions optional/capi/io_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -531,123 +531,135 @@
end
end

describe "rb_io_open_descriptor" do
it "creates a new IO instance" do
io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
io.should.is_a?(IO)
end
quarantine! do # "Errno::EBADF: Bad file descriptor" at closing @r_io, @rw_io etc in the after :each hook
describe "rb_io_open_descriptor" do
it "creates a new IO instance" do
io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
io.should.is_a?(IO)
end

it "return an instance of the specified class" do
io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
io.class.should == File
it "return an instance of the specified class" do
io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
io.class.should == File

io = @o.rb_io_open_descriptor(IO, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
io.class.should == IO
end
io = @o.rb_io_open_descriptor(IO, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
io.class.should == IO
end

it "sets the specified file descriptor" do
io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
io.fileno.should == @r_io.fileno
end
it "sets the specified file descriptor" do
io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
io.fileno.should == @r_io.fileno
end

it "sets the specified path" do
io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
io.path.should == "a.txt"
end
it "sets the specified path" do
io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
io.path.should == "a.txt"
end

it "sets the specified mode" do
io = @o.rb_io_open_descriptor(File, @r_io.fileno, CApiIOSpecs::FMODE_BINMODE, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
io.should.binmode?
it "sets the specified mode" do
io = @o.rb_io_open_descriptor(File, @r_io.fileno, CApiIOSpecs::FMODE_BINMODE, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
io.should.binmode?

io = @o.rb_io_open_descriptor(File, @r_io.fileno, CApiIOSpecs::FMODE_TEXTMODE, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
io.should_not.binmode?
end
io = @o.rb_io_open_descriptor(File, @r_io.fileno, CApiIOSpecs::FMODE_TEXTMODE, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
io.should_not.binmode?
end

it "sets the specified timeout" do
io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
io.timeout.should == 60
end
it "sets the specified timeout" do
io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
io.timeout.should == 60
end

it "sets the specified internal encoding" do
io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
io.internal_encoding.should == Encoding::US_ASCII
end
it "sets the specified internal encoding" do
io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
io.internal_encoding.should == Encoding::US_ASCII
end

it "sets the specified external encoding" do
io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
io.external_encoding.should == Encoding::UTF_8
end
it "sets the specified external encoding" do
io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
io.external_encoding.should == Encoding::UTF_8
end

it "does not apply the specified encoding flags" do
File.write("a.txt", "123\r\n456\n89")
file = File.open("a.txt", "r")
it "does not apply the specified encoding flags" do
name = tmp("rb_io_open_descriptor_specs")
File.write(name, "123\r\n456\n89")
file = File.open(name, "r")

io = @o.rb_io_open_descriptor(File, file.fileno, CApiIOSpecs::FMODE_READABLE, "a.txt", 60, "US-ASCII", "UTF-8", CApiIOSpecs::ECONV_UNIVERSAL_NEWLINE_DECORATOR, {})
io.read_nonblock(20).should == "123\r\n456\n89"
end
io = @o.rb_io_open_descriptor(File, file.fileno, CApiIOSpecs::FMODE_READABLE, "a.txt", 60, "US-ASCII", "UTF-8", CApiIOSpecs::ECONV_UNIVERSAL_NEWLINE_DECORATOR, {})
io.read_nonblock(20).should == "123\r\n456\n89"
ensure
file.close
rm_r name
end

it "ignores the IO open options" do
io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {external_encoding: "windows-1251"})
io.external_encoding.should == Encoding::UTF_8
it "ignores the IO open options" do
io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {external_encoding: "windows-1251"})
io.external_encoding.should == Encoding::UTF_8

io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {internal_encoding: "windows-1251"})
io.internal_encoding.should == Encoding::US_ASCII
io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {internal_encoding: "windows-1251"})
io.internal_encoding.should == Encoding::US_ASCII

io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {encoding: "windows-1251:binary"})
io.external_encoding.should == Encoding::UTF_8
io.internal_encoding.should == Encoding::US_ASCII
io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {encoding: "windows-1251:binary"})
io.external_encoding.should == Encoding::UTF_8
io.internal_encoding.should == Encoding::US_ASCII

io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {textmode: false})
io.should_not.binmode?
io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {textmode: false})
io.should_not.binmode?

io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {binmode: true})
io.should_not.binmode?
io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {binmode: true})
io.should_not.binmode?

io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {autoclose: false})
io.should.autoclose?
io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {autoclose: false})
io.should.autoclose?

io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {path: "a.txt"})
io.path.should == "a.txt"
end
io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {path: "a.txt"})
io.path.should == "a.txt"
end

it "ignores the IO encoding options" do
io = @o.rb_io_open_descriptor(File, @w_io.fileno, CApiIOSpecs::FMODE_WRITABLE, "a.txt", 60, "US-ASCII", "UTF-8", 0, {crlf_newline: true})
it "ignores the IO encoding options" do
io = @o.rb_io_open_descriptor(File, @w_io.fileno, CApiIOSpecs::FMODE_WRITABLE, "a.txt", 60, "US-ASCII", "UTF-8", 0, {crlf_newline: true})

io.write("123\r\n456\n89")
io.flush
io.write("123\r\n456\n89")
io.flush

@r_io.read_nonblock(20).should == "123\r\n456\n89"
end
@r_io.read_nonblock(20).should == "123\r\n456\n89"
end

it "allows wrong mode" do
io = @o.rb_io_open_descriptor(File, @w_io.fileno, CApiIOSpecs::FMODE_READABLE, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
io.should.is_a?(File)
it "allows wrong mode" do
io = @o.rb_io_open_descriptor(File, @w_io.fileno, CApiIOSpecs::FMODE_READABLE, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
io.should.is_a?(File)

-> { io.read_nonblock(1) }.should raise_error(Errno::EBADF)
end
platform_is_not :windows do
-> { io.read_nonblock(1) }.should raise_error(Errno::EBADF)
end

it "tolerates NULL as rb_io_encoding *encoding parameter" do
io = @o.rb_io_open_descriptor_without_encoding(File, @r_io.fileno, 0, "a.txt", 60)
io.should.is_a?(File)
end
platform_is :windows do
-> { io.read_nonblock(1) }.should raise_error(IO::EWOULDBLOCKWaitReadable)
end
end

it "tolerates NULL as rb_io_encoding *encoding parameter" do
io = @o.rb_io_open_descriptor_without_encoding(File, @r_io.fileno, 0, "a.txt", 60)
io.should.is_a?(File)
end

it "deduplicates path String" do
path = "a.txt".dup
io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, path, 60, "US-ASCII", "UTF-8", 0, {})
io.path.should_not equal(path)
it "deduplicates path String" do
path = "a.txt".dup
io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, path, 60, "US-ASCII", "UTF-8", 0, {})
io.path.should_not equal(path)

path = "a.txt".freeze
io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, path, 60, "US-ASCII", "UTF-8", 0, {})
io.path.should_not equal(path)
end
path = "a.txt".freeze
io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, path, 60, "US-ASCII", "UTF-8", 0, {})
io.path.should_not equal(path)
end

it "calls #to_str to convert a path to a String" do
path = Object.new
def path.to_str; "a.txt"; end
it "calls #to_str to convert a path to a String" do
path = Object.new
def path.to_str; "a.txt"; end

io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, path, 60, "US-ASCII", "UTF-8", 0, {})
io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, path, 60, "US-ASCII", "UTF-8", 0, {})

io.path.should == "a.txt"
io.path.should == "a.txt"
end
Copy link
Member Author

@andrykonchin andrykonchin Dec 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By some reason specs fail on macOS/Ubintu on CI with error:

-----------------------------------------------------
The rubyspec temp directory is not empty. Ensure that
all specs are cleaning up temporary files:
  /Users/runner/work/spec/spec/rubyspec_temp/[59](https://github.com/ruby/spec/actions/runs/12241512394/job/34146769462?pr=1226#step:9:60)61
-----------------------------------------------------


1)
An exception occurred during: after :each
C-API IO function rb_io_open_descriptor sets the specified path ERROR
Errno::EBADF: Bad file descriptor @ fptr_finalize_flush - /Users/runner/work/spec/spec/rubyspec_temp/59[61](https://github.com/ruby/spec/actions/runs/12241512394/job/34146769462?pr=1226#step:9:62)/110-c_api_io_specs
/Users/runner/work/spec/spec/optional/capi/io_spec.rb:218:in `close'
/Users/runner/work/spec/spec/optional/capi/io_spec.rb:218:in `block (2 levels) in <top (required)>'
/Users/runner/work/spec/spec/optional/capi/io_spec.rb:205:in `<top (required)>'

Cannot reproduce it locally. So I decided just to quarantine them until the issue is investigated.

https://github.com/ruby/spec/actions/runs/12241512394/job/34146769462
https://github.com/ruby/spec/actions/runs/12241608299/job/34147078428

end
end
end
Expand Down
20 changes: 14 additions & 6 deletions optional/capi/kernel_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,13 @@ class CApiKernelSpecs::Exc < StandardError
end

it "uses an 'unknown error' message when errno is unknown" do
-> do
@s.rb_syserr_fail(-10, nil)
end.should raise_error(SystemCallError, /Unknown error(:)? -1/) # a new class Errno::E-01 is generated on the fly
platform_is_not :windows do
-> { @s.rb_syserr_fail(-10, nil) }.should raise_error(SystemCallError, /Unknown error(:)? -10/)
end

platform_is :windows do
-> { @s.rb_syserr_fail(-1, nil) }.should raise_error(SystemCallError, "The operation completed successfully.")
end
end
end

Expand All @@ -217,9 +221,13 @@ class CApiKernelSpecs::Exc < StandardError
end

it "uses an 'unknown error' message when errno is unknown" do
-> do
@s.rb_syserr_fail_str(-1, nil)
end.should raise_error(SystemCallError, /Unknown error(:)? -1/) # a new class Errno::E-01 is generated on the fly
platform_is_not :windows do
-> { @s.rb_syserr_fail_str(-10, nil) }.should raise_error(SystemCallError, /Unknown error(:)? -10/)
end

platform_is :windows do
-> { @s.rb_syserr_fail_str(-1, nil) }.should raise_error(SystemCallError, "The operation completed successfully.")
end
end
end

Expand Down
Loading