diff options
Diffstat (limited to 'spec/ruby/core/io/read_spec.rb')
| -rw-r--r-- | spec/ruby/core/io/read_spec.rb | 265 |
1 files changed, 188 insertions, 77 deletions
diff --git a/spec/ruby/core/io/read_spec.rb b/spec/ruby/core/io/read_spec.rb index 28cab13340..dd787c9b60 100644 --- a/spec/ruby/core/io/read_spec.rb +++ b/spec/ruby/core/io/read_spec.rb @@ -23,6 +23,15 @@ describe "IO.read" do IO.read(p) end + # https://bugs.ruby-lang.org/issues/19354 + it "accepts options as keyword arguments" do + IO.read(@fname, 3, 0, mode: "r+").should == @contents[0, 3] + + -> { + IO.read(@fname, 3, 0, {mode: "r+"}) + }.should.raise(ArgumentError, /wrong number of arguments/) + end + it "accepts an empty options Hash" do IO.read(@fname, **{}).should == @contents end @@ -36,11 +45,11 @@ describe "IO.read" do end it "raises an IOError if the options Hash specifies write mode" do - -> { IO.read(@fname, 3, 0, mode: "w") }.should raise_error(IOError) + -> { IO.read(@fname, 3, 0, mode: "w") }.should.raise(IOError) end it "raises an IOError if the options Hash specifies append only mode" do - -> { IO.read(@fname, mode: "a") }.should raise_error(IOError) + -> { IO.read(@fname, mode: "a") }.should.raise(IOError) end it "reads the file if the options Hash includes read mode" do @@ -55,6 +64,24 @@ describe "IO.read" do IO.read(@fname, mode: "a+").should == @contents end + platform_is_not :windows do + end + + it "disregards other options if :open_args is given" do + string = IO.read(@fname,mode: "w", encoding: Encoding::UTF_32LE, open_args: ["r", encoding: Encoding::UTF_8]) + string.encoding.should == Encoding::UTF_8 + end + + it "doesn't require mode to be specified in :open_args" do + string = IO.read(@fname, nil, 0, open_args: [{encoding: Encoding::US_ASCII}]) + string.encoding.should == Encoding::US_ASCII + end + + it "doesn't require mode to be specified in :open_args even if flags option passed" do + string = IO.read(@fname, nil, 0, open_args: [{encoding: Encoding::US_ASCII, flags: File::CREAT}]) + string.encoding.should == Encoding::US_ASCII + end + it "treats second nil argument as no length limit" do IO.read(@fname, nil).should == @contents IO.read(@fname, nil, 5).should == IO.read(@fname, @contents.length, 5) @@ -77,22 +104,31 @@ describe "IO.read" do IO.read(@fname, 1, 10).should == nil end + it "returns an empty string when reading zero bytes" do + IO.read(@fname, 0).should == '' + end + + it "returns a String in BINARY when passed a size" do + IO.read(@fname, 1).encoding.should == Encoding::BINARY + IO.read(@fname, 0).encoding.should == Encoding::BINARY + end + it "raises an Errno::ENOENT when the requested file does not exist" do rm_r @fname - -> { IO.read @fname }.should raise_error(Errno::ENOENT) + -> { IO.read @fname }.should.raise(Errno::ENOENT) end it "raises a TypeError when not passed a String type" do - -> { IO.read nil }.should raise_error(TypeError) + -> { IO.read nil }.should.raise(TypeError) end it "raises an ArgumentError when not passed a valid length" do - -> { IO.read @fname, -1 }.should raise_error(ArgumentError) + -> { IO.read @fname, -1 }.should.raise(ArgumentError) end - it "raises an Errno::EINVAL when not passed a valid offset" do - -> { IO.read @fname, 0, -1 }.should raise_error(Errno::EINVAL) - -> { IO.read @fname, -1, -1 }.should raise_error(Errno::EINVAL) + it "raises an ArgumentError when not passed a valid offset" do + -> { IO.read @fname, 0, -1 }.should.raise(ArgumentError) + -> { IO.read @fname, -1, -1 }.should.raise(ArgumentError) end it "uses the external encoding specified via the :external_encoding option" do @@ -114,55 +150,79 @@ describe "IO.read" do end end -describe "IO.read from a pipe" do - it "runs the rest as a subprocess and returns the standard output" do - cmd = "|sh -c 'echo hello'" - platform_is :windows do - cmd = "|cmd.exe /C echo hello" +ruby_version_is ""..."4.0" do + describe "IO.read from a pipe" do + it "runs the rest as a subprocess and returns the standard output" do + cmd = "|sh -c 'echo hello'" + platform_is :windows do + cmd = "|cmd.exe /C echo hello" + end + + suppress_warning do # https://bugs.ruby-lang.org/issues/19630 + IO.read(cmd).should == "hello\n" + end end - IO.read(cmd).should == "hello\n" - end - platform_is_not :windows do - it "opens a pipe to a fork if the rest is -" do - str = IO.read("|-") - if str # parent - str.should == "hello from child\n" - else #child - puts "hello from child" - exit! + platform_is_not :windows do + it "opens a pipe to a fork if the rest is -" do + str = nil + suppress_warning do # https://bugs.ruby-lang.org/issues/19630 + str = IO.read("|-") + end + + if str # parent + str.should == "hello from child\n" + else #child + puts "hello from child" + exit! + end end end - end - it "reads only the specified number of bytes requested" do - cmd = "|sh -c 'echo hello'" - platform_is :windows do - cmd = "|cmd.exe /C echo hello" + it "reads only the specified number of bytes requested" do + cmd = "|sh -c 'echo hello'" + platform_is :windows do + cmd = "|cmd.exe /C echo hello" + end + + suppress_warning do # https://bugs.ruby-lang.org/issues/19630 + IO.read(cmd, 1).should == "h" + end end - IO.read(cmd, 1).should == "h" - end - platform_is_not :windows do - it "raises Errno::ESPIPE if passed an offset" do - -> { - IO.read("|sh -c 'echo hello'", 1, 1) - }.should raise_error(Errno::ESPIPE) + platform_is_not :windows do + it "raises Errno::ESPIPE if passed an offset" do + -> { + suppress_warning do # https://bugs.ruby-lang.org/issues/19630 + IO.read("|sh -c 'echo hello'", 1, 1) + end + }.should.raise(Errno::ESPIPE) + end end - end -quarantine! do # The process tried to write to a nonexistent pipe. - platform_is :windows do - # TODO: It should raise Errno::ESPIPE on Windows as well - # once https://bugs.ruby-lang.org/issues/12230 is fixed. - it "raises Errno::EINVAL if passed an offset" do + quarantine! do # The process tried to write to a nonexistent pipe. + platform_is :windows do + # TODO: It should raise Errno::ESPIPE on Windows as well + # once https://bugs.ruby-lang.org/issues/12230 is fixed. + it "raises Errno::EINVAL if passed an offset" do + -> { + suppress_warning do # https://bugs.ruby-lang.org/issues/19630 + IO.read("|cmd.exe /C echo hello", 1, 1) + end + }.should.raise(Errno::EINVAL) + end + end + end + + # https://bugs.ruby-lang.org/issues/19630 + it "warns about deprecation" do + cmd = "|echo ok" -> { - IO.read("|cmd.exe /C echo hello", 1, 1) - }.should raise_error(Errno::EINVAL) + IO.read(cmd) + }.should complain(/IO process creation with a leading '\|'/) end end end -end describe "IO.read on an empty file" do before :each do @@ -205,21 +265,53 @@ describe "IO#read" do @io.read(4).should == '7890' end + it "treats first nil argument as no length limit" do + @io.read(nil).should == @contents + end + + it "raises an ArgumentError when not passed a valid length" do + -> { @io.read(-1) }.should.raise(ArgumentError) + end + it "clears the output buffer if there is nothing to read" do @io.pos = 10 - buf = 'non-empty string' + buf = +'non-empty string' @io.read(10, buf).should == nil buf.should == '' + + buf = +'non-empty string' + + @io.read(nil, buf).should == "" + + buf.should == '' + + buf = +'non-empty string' + + @io.read(0, buf).should == "" + + buf.should == '' + end + + it "raise FrozenError if the output buffer is frozen" do + @io.read + -> { @io.read(0, 'frozen-string'.freeze) }.should.raise(FrozenError) + -> { @io.read(1, 'frozen-string'.freeze) }.should.raise(FrozenError) + -> { @io.read(nil, 'frozen-string'.freeze) }.should.raise(FrozenError) + end + + it "raise FrozenError if the output buffer is frozen (2)" do + @io.read + -> { @io.read(1, ''.freeze) }.should.raise(FrozenError) end it "consumes zero bytes when reading zero bytes" do @io.read(0).should == '' @io.pos.should == 0 - @io.getc.chr.should == '1' + @io.getc.should == '1' end it "is at end-of-file when everything has been read" do @@ -232,50 +324,72 @@ describe "IO#read" do end it "places the specified number of bytes in the buffer" do - buf = "" + buf = +"" @io.read 5, buf buf.should == "12345" end it "expands the buffer when too small" do - buf = "ABCDE" + buf = +"ABCDE" @io.read nil, buf buf.should == @contents end it "overwrites the buffer" do - buf = "ABCDEFGHIJ" + buf = +"ABCDEFGHIJ" @io.read nil, buf buf.should == @contents end it "truncates the buffer when too big" do - buf = "ABCDEFGHIJKLMNO" + buf = +"ABCDEFGHIJKLMNO" @io.read nil, buf buf.should == @contents @io.rewind - buf = "ABCDEFGHIJKLMNO" + buf = +"ABCDEFGHIJKLMNO" @io.read 5, buf buf.should == @contents[0..4] end + it "preserves the encoding of the given buffer" do + buffer = ''.encode(Encoding::ISO_8859_1) + @io.read(10, buffer) + + buffer.encoding.should == Encoding::ISO_8859_1 + end + + # https://bugs.ruby-lang.org/issues/20416 + it "does not preserve the encoding of the given buffer when max length is not provided" do + buffer = ''.encode(Encoding::ISO_8859_1) + @io.read(nil, buffer) + + buffer.encoding.should_not == Encoding::ISO_8859_1 + end + it "returns the given buffer" do - buf = "" + buf = +"" + + @io.read(nil, buf).should.equal? buf + end + + it "returns the given buffer when there is nothing to read" do + buf = +"" - @io.read(nil, buf).should equal buf + @io.read + @io.read(nil, buf).should.equal? buf end it "coerces the second argument to string and uses it as a buffer" do - buf = "ABCDE" + buf = +"ABCDE" obj = mock("buff") obj.should_receive(:to_str).any_number_of_times.and_return(buf) - @io.read(15, obj).should_not equal obj + @io.read(15, obj).should_not.equal? obj buf.should == @contents end @@ -309,9 +423,12 @@ describe "IO#read" do end it "raises IOError on closed stream" do - -> { IOSpecs.closed_io.read }.should raise_error(IOError) + -> { IOSpecs.closed_io.read }.should.raise(IOError) end + it "raises ArgumentError when length is less than 0" do + -> { @io.read(-1) }.should.raise(ArgumentError) + end platform_is_not :windows do it "raises IOError when stream is closed by another thread" do @@ -327,7 +444,7 @@ describe "IO#read" do Thread.pass until t.stop? r.close t.join - t.value.should be_kind_of(IOError) + t.value.should.is_a?(IOError) w.close end end @@ -392,13 +509,6 @@ describe "IO#read in binary mode" do xE2 = [226].pack('C*') result.should == ("abc" + xE2 + "def").force_encoding(Encoding::BINARY) end - - it "does not transcode file contents when an internal encoding is specified" do - result = File.open(@name, "r:binary:utf-8") { |f| f.read }.chomp - result.encoding.should == Encoding::BINARY - xE2 = [226].pack('C*') - result.should == ("abc" + xE2 + "def").force_encoding(Encoding::BINARY) - end end describe "IO#read in text mode" do @@ -468,20 +578,20 @@ describe :io_read_internal_encoding, shared: true do end it "sets the String encoding to the internal encoding" do - @io.read.encoding.should equal(Encoding::UTF_8) + @io.read.encoding.should.equal?(Encoding::UTF_8) end describe "when passed nil for limit" do it "sets the buffer to a transcoded String" do - result = @io.read(nil, buf = "") - buf.should equal(result) + result = @io.read(nil, buf = +"") + buf.should.equal?(result) buf.should == "ありがとう\n" end it "sets the buffer's encoding to the internal encoding" do - buf = "".force_encoding Encoding::ISO_8859_1 + buf = "".dup.force_encoding Encoding::ISO_8859_1 @io.read(nil, buf) - buf.encoding.should equal(Encoding::UTF_8) + buf.encoding.should.equal?(Encoding::UTF_8) end end end @@ -492,23 +602,24 @@ describe :io_read_size_internal_encoding, shared: true do end it "returns a String in BINARY when passed a size" do - @io.read(4).encoding.should equal(Encoding::BINARY) + @io.read(4).encoding.should.equal?(Encoding::BINARY) + @io.read(0).encoding.should.equal?(Encoding::BINARY) end it "does not change the buffer's encoding when passed a limit" do - buf = "".force_encoding Encoding::ISO_8859_1 + buf = "".dup.force_encoding Encoding::ISO_8859_1 @io.read(4, buf) buf.should == [164, 162, 164, 234].pack('C*').force_encoding(Encoding::ISO_8859_1) - buf.encoding.should equal(Encoding::ISO_8859_1) + buf.encoding.should.equal?(Encoding::ISO_8859_1) end it "truncates the buffer but does not change the buffer's encoding when no data remains" do - buf = "abc".force_encoding Encoding::ISO_8859_1 + buf = "abc".dup.force_encoding Encoding::ISO_8859_1 @io.read - @io.read(1, buf).should be_nil + @io.read(1, buf).should == nil buf.size.should == 0 - buf.encoding.should equal(Encoding::ISO_8859_1) + buf.encoding.should.equal?(Encoding::ISO_8859_1) end end @@ -526,7 +637,7 @@ describe "IO#read" do end it "sets the String encoding to Encoding.default_external" do - @io.read.encoding.should equal(Encoding.default_external) + @io.read.encoding.should.equal?(Encoding.default_external) end end @@ -545,7 +656,7 @@ describe "IO#read" do end it "sets the String encoding to the external encoding" do - @io.read.encoding.should equal(Encoding::EUC_JP) + @io.read.encoding.should.equal?(Encoding::EUC_JP) end it_behaves_like :io_read_size_internal_encoding, nil |
