summaryrefslogtreecommitdiff
path: root/spec/ruby/library/stringio
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/library/stringio')
-rw-r--r--spec/ruby/library/stringio/each_line_spec.rb4
-rw-r--r--spec/ruby/library/stringio/each_spec.rb4
-rw-r--r--spec/ruby/library/stringio/fileno_spec.rb3
-rw-r--r--spec/ruby/library/stringio/fixtures/classes.rb4
-rw-r--r--spec/ruby/library/stringio/gets_spec.rb263
-rw-r--r--spec/ruby/library/stringio/readline_spec.rb160
-rw-r--r--spec/ruby/library/stringio/readpartial_spec.rb38
-rw-r--r--spec/ruby/library/stringio/set_encoding_by_bom_spec.rb237
-rw-r--r--spec/ruby/library/stringio/shared/each.rb78
-rw-r--r--spec/ruby/library/stringio/shared/gets.rb249
-rw-r--r--spec/ruby/library/stringio/shared/read.rb4
-rw-r--r--spec/ruby/library/stringio/shared/sysread.rb2
-rw-r--r--spec/ruby/library/stringio/shared/write.rb2
-rw-r--r--spec/ruby/library/stringio/sysread_spec.rb5
14 files changed, 669 insertions, 384 deletions
diff --git a/spec/ruby/library/stringio/each_line_spec.rb b/spec/ruby/library/stringio/each_line_spec.rb
index c68f7dae82..4ac0db7c45 100644
--- a/spec/ruby/library/stringio/each_line_spec.rb
+++ b/spec/ruby/library/stringio/each_line_spec.rb
@@ -21,3 +21,7 @@ end
describe "StringIO#each_line when passed limit" do
it_behaves_like :stringio_each_limit, :each_line
end
+
+describe "StringIO#each when passed separator and limit" do
+ it_behaves_like :stringio_each_separator_and_limit, :each_line
+end
diff --git a/spec/ruby/library/stringio/each_spec.rb b/spec/ruby/library/stringio/each_spec.rb
index 2c30ed5cda..7eb322f3ff 100644
--- a/spec/ruby/library/stringio/each_spec.rb
+++ b/spec/ruby/library/stringio/each_spec.rb
@@ -25,3 +25,7 @@ end
describe "StringIO#each when passed limit" do
it_behaves_like :stringio_each_limit, :each
end
+
+describe "StringIO#each when passed separator and limit" do
+ it_behaves_like :stringio_each_separator_and_limit, :each
+end
diff --git a/spec/ruby/library/stringio/fileno_spec.rb b/spec/ruby/library/stringio/fileno_spec.rb
index eea03a5af3..5a9f440a0f 100644
--- a/spec/ruby/library/stringio/fileno_spec.rb
+++ b/spec/ruby/library/stringio/fileno_spec.rb
@@ -1,6 +1,5 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-require_relative 'shared/each'
+require 'stringio'
describe "StringIO#fileno" do
it "returns nil" do
diff --git a/spec/ruby/library/stringio/fixtures/classes.rb b/spec/ruby/library/stringio/fixtures/classes.rb
index bb8dc354cc..832c5457d7 100644
--- a/spec/ruby/library/stringio/fixtures/classes.rb
+++ b/spec/ruby/library/stringio/fixtures/classes.rb
@@ -4,12 +4,12 @@ class StringSubclass < String; end
module StringIOSpecs
def self.build
- str = <<-EOS
+ str = <<-EOS
each
peach
pear
plum
- EOS
+ EOS
StringIO.new(str)
end
end
diff --git a/spec/ruby/library/stringio/gets_spec.rb b/spec/ruby/library/stringio/gets_spec.rb
index 4af7704a41..ac876f0b4f 100644
--- a/spec/ruby/library/stringio/gets_spec.rb
+++ b/spec/ruby/library/stringio/gets_spec.rb
@@ -1,250 +1,61 @@
require_relative '../../spec_helper'
require "stringio"
+require_relative "shared/gets"
-describe "StringIO#gets when passed [separator]" do
- before :each do
- @io = StringIO.new("this>is>an>example")
- end
-
- it "returns the data read till the next occurrence of the passed separator" do
- @io.gets(">").should == "this>"
- @io.gets(">").should == "is>"
- @io.gets(">").should == "an>"
- @io.gets(">").should == "example"
- end
-
- it "sets $_ to the read content" do
- @io.gets(">")
- $_.should == "this>"
- @io.gets(">")
- $_.should == "is>"
- @io.gets(">")
- $_.should == "an>"
- @io.gets(">")
- $_.should == "example"
- @io.gets(">")
- $_.should be_nil
- end
-
- it "accepts string as separator" do
- @io.gets("is>")
- $_.should == "this>"
- @io.gets("an>")
- $_.should == "is>an>"
- @io.gets("example")
- $_.should == "example"
- @io.gets("ple")
- $_.should be_nil
- end
-
- it "updates self's lineno by one" do
- @io.gets(">")
- @io.lineno.should eql(1)
-
- @io.gets(">")
- @io.lineno.should eql(2)
-
- @io.gets(">")
- @io.lineno.should eql(3)
- end
-
- it "returns the next paragraph when the passed separator is an empty String" do
- io = StringIO.new("this is\n\nan example")
- io.gets("").should == "this is\n\n"
- io.gets("").should == "an example"
- end
-
- it "returns the remaining content starting at the current position when passed nil" do
- io = StringIO.new("this is\n\nan example")
- io.pos = 5
- io.gets(nil).should == "is\n\nan example"
- end
+describe "StringIO#gets" do
+ describe "when passed [separator]" do
+ it_behaves_like :stringio_gets_separator, :gets
- it "tries to convert the passed separator to a String using #to_str" do
- obj = mock('to_str')
- obj.should_receive(:to_str).and_return(">")
- @io.gets(obj).should == "this>"
- end
-end
-
-describe "StringIO#gets when passed no argument" do
- before :each do
- @io = StringIO.new("this is\nan example\nfor StringIO#gets")
- end
+ it "returns nil if self is at the end" do
+ @io = StringIO.new("this>is>an>example")
- it "returns the data read till the next occurrence of $/ or till eof" do
- @io.gets.should == "this is\n"
-
- begin
- old_sep = $/
- suppress_warning {$/ = " "}
- @io.gets.should == "an "
- @io.gets.should == "example\nfor "
- @io.gets.should == "StringIO#gets"
- ensure
- suppress_warning {$/ = old_sep}
+ @io.pos = 36
+ @io.gets(">").should be_nil
+ @io.gets(">").should be_nil
end
end
- it "sets $_ to the read content" do
- @io.gets
- $_.should == "this is\n"
- @io.gets
- $_.should == "an example\n"
- @io.gets
- $_.should == "for StringIO#gets"
- @io.gets
- $_.should be_nil
- end
-
- it "updates self's position" do
- @io.gets
- @io.pos.should eql(8)
-
- @io.gets
- @io.pos.should eql(19)
-
- @io.gets
- @io.pos.should eql(36)
- end
-
- it "updates self's lineno" do
- @io.gets
- @io.lineno.should eql(1)
-
- @io.gets
- @io.lineno.should eql(2)
-
- @io.gets
- @io.lineno.should eql(3)
- end
-
- it "returns nil if self is at the end" do
- @io.pos = 36
- @io.gets.should be_nil
- @io.gets.should be_nil
- end
-end
-
-describe "StringIO#gets when passed [limit]" do
- before :each do
- @io = StringIO.new("this>is>an>example")
- end
-
- it "returns the data read until the limit is met" do
- @io.gets(4).should == "this"
- @io.gets(3).should == ">is"
- @io.gets(5).should == ">an>e"
- @io.gets(6).should == "xample"
- end
-
- it "sets $_ to the read content" do
- @io.gets(4)
- $_.should == "this"
- @io.gets(3)
- $_.should == ">is"
- @io.gets(5)
- $_.should == ">an>e"
- @io.gets(6)
- $_.should == "xample"
- @io.gets(3)
- $_.should be_nil
- end
-
- it "updates self's lineno by one" do
- @io.gets(3)
- @io.lineno.should eql(1)
-
- @io.gets(3)
- @io.lineno.should eql(2)
-
- @io.gets(3)
- @io.lineno.should eql(3)
- end
-
- it "tries to convert the passed limit to an Integer using #to_int" do
- obj = mock('to_int')
- obj.should_receive(:to_int).and_return(4)
- @io.gets(obj).should == "this"
- end
-
- it "returns a blank string when passed a limit of 0" do
- @io.gets(0).should == ""
- end
-
- it "ignores it when passed a negative limit" do
- @io.gets(-4).should == "this>is>an>example"
- end
-end
+ describe "when passed [limit]" do
+ it_behaves_like :stringio_gets_limit, :gets
-describe "StringIO#gets when passed [separator] and [limit]" do
- before :each do
- @io = StringIO.new("this>is>an>example")
- end
-
- it "returns the data read until the limit is consumed or the separator is met" do
- @io.gets('>', 8).should == "this>"
- @io.gets('>', 2).should == "is"
- @io.gets('>', 10).should == ">"
- @io.gets('>', 6).should == "an>"
- @io.gets('>', 5).should == "examp"
- end
+ it "returns nil if self is at the end" do
+ @io = StringIO.new("this>is>an>example")
- it "sets $_ to the read content" do
- @io.gets('>', 8)
- $_.should == "this>"
- @io.gets('>', 2)
- $_.should == "is"
- @io.gets('>', 10)
- $_.should == ">"
- @io.gets('>', 6)
- $_.should == "an>"
- @io.gets('>', 5)
- $_.should == "examp"
+ @io.pos = 36
+ @io.gets(3).should be_nil
+ @io.gets(3).should be_nil
+ end
end
- it "updates self's lineno by one" do
- @io.gets('>', 3)
- @io.lineno.should eql(1)
+ describe "when passed [separator] and [limit]" do
+ it_behaves_like :stringio_gets_separator_and_limit, :gets
- @io.gets('>', 3)
- @io.lineno.should eql(2)
+ it "returns nil if self is at the end" do
+ @io = StringIO.new("this>is>an>example")
- @io.gets('>', 3)
- @io.lineno.should eql(3)
+ @io.pos = 36
+ @io.gets(">", 3).should be_nil
+ @io.gets(">", 3).should be_nil
+ end
end
- it "tries to convert the passed separator to a String using #to_str" do
- obj = mock('to_str')
- obj.should_receive(:to_str).and_return('>')
- @io.gets(obj, 5).should == "this>"
- end
+ describe "when passed no argument" do
+ it_behaves_like :stringio_gets_no_argument, :gets
- it "does not raise TypeError if passed separator is nil" do
- @io.gets(nil, 5).should == "this>"
- end
+ it "returns nil if self is at the end" do
+ @io = StringIO.new("this>is>an>example")
- it "tries to convert the passed limit to an Integer using #to_int" do
- obj = mock('to_int')
- obj.should_receive(:to_int).and_return(5)
- @io.gets('>', obj).should == "this>"
+ @io.pos = 36
+ @io.gets.should be_nil
+ @io.gets.should be_nil
+ end
end
-end
-
-describe "StringIO#gets when in write-only mode" do
- it "raises an IOError" do
- io = StringIO.new(+"xyz", "w")
- -> { io.gets }.should raise_error(IOError)
- io = StringIO.new("xyz")
- io.close_read
- -> { io.gets }.should raise_error(IOError)
+ describe "when passed [chomp]" do
+ it_behaves_like :stringio_gets_chomp, :gets
end
-end
-describe "StringIO#gets when passed [chomp]" do
- it "returns the data read without a trailing newline character" do
- io = StringIO.new("this>is>an>example\n")
- io.gets(chomp: true).should == "this>is>an>example"
+ describe "when in write-only mode" do
+ it_behaves_like :stringio_gets_write_only, :gets
end
end
diff --git a/spec/ruby/library/stringio/readline_spec.rb b/spec/ruby/library/stringio/readline_spec.rb
index b16a16e23f..085360707f 100644
--- a/spec/ruby/library/stringio/readline_spec.rb
+++ b/spec/ruby/library/stringio/readline_spec.rb
@@ -1,150 +1,58 @@
require_relative '../../spec_helper'
+require "stringio"
require_relative 'fixtures/classes'
+require_relative "shared/gets"
+describe "StringIO#readline" do
+ describe "when passed [separator]" do
+ it_behaves_like :stringio_gets_separator, :readline
-describe "StringIO#readline when passed [separator]" do
- before :each do
- @io = StringIO.new("this>is>an>example")
- end
-
- it "returns the data read till the next occurrence of the passed separator" do
- @io.readline(">").should == "this>"
- @io.readline(">").should == "is>"
- @io.readline(">").should == "an>"
- @io.readline(">").should == "example"
- end
-
- it "sets $_ to the read content" do
- @io.readline(">")
- $_.should == "this>"
- @io.readline(">")
- $_.should == "is>"
- @io.readline(">")
- $_.should == "an>"
- @io.readline(">")
- $_.should == "example"
- end
-
- it "updates self's lineno by one" do
- @io.readline(">")
- @io.lineno.should eql(1)
-
- @io.readline(">")
- @io.lineno.should eql(2)
-
- @io.readline(">")
- @io.lineno.should eql(3)
- end
-
- it "returns the next paragraph when the passed separator is an empty String" do
- io = StringIO.new("this is\n\nan example")
- io.readline("").should == "this is\n\n"
- io.readline("").should == "an example"
- end
-
- it "returns the remaining content starting at the current position when passed nil" do
- io = StringIO.new("this is\n\nan example")
- io.pos = 5
- io.readline(nil).should == "is\n\nan example"
- end
-
- it "tries to convert the passed separator to a String using #to_str" do
- obj = mock('to_str')
- obj.should_receive(:to_str).and_return(">")
- @io.readline(obj).should == "this>"
- end
-end
-
-describe "StringIO#readline when passed no argument" do
- before :each do
- @io = StringIO.new("this is\nan example\nfor StringIO#readline")
- end
-
- it "returns the data read till the next occurrence of $/ or till eof" do
- @io.readline.should == "this is\n"
+ it "raises an IOError if self is at the end" do
+ @io = StringIO.new("this>is>an>example")
- begin
- old_sep = $/
- suppress_warning {$/ = " "}
- @io.readline.should == "an "
- @io.readline.should == "example\nfor "
- @io.readline.should == "StringIO#readline"
- ensure
- suppress_warning {$/ = old_sep}
+ @io.pos = 36
+ -> { @io.readline(">") }.should raise_error(IOError)
end
end
- it "sets $_ to the read content" do
- @io.readline
- $_.should == "this is\n"
- @io.readline
- $_.should == "an example\n"
- @io.readline
- $_.should == "for StringIO#readline"
- end
+ describe "when passed [limit]" do
+ it_behaves_like :stringio_gets_limit, :readline
- it "updates self's position" do
- @io.readline
- @io.pos.should eql(8)
+ it "raises an IOError if self is at the end" do
+ @io = StringIO.new("this>is>an>example")
- @io.readline
- @io.pos.should eql(19)
-
- @io.readline
- @io.pos.should eql(40)
+ @io.pos = 36
+ -> { @io.readline(3) }.should raise_error(IOError)
+ end
end
- it "updates self's lineno" do
- @io.readline
- @io.lineno.should eql(1)
+ describe "when passed [separator] and [limit]" do
+ it_behaves_like :stringio_gets_separator_and_limit, :readline
- @io.readline
- @io.lineno.should eql(2)
+ it "raises an IOError if self is at the end" do
+ @io = StringIO.new("this>is>an>example")
- @io.readline
- @io.lineno.should eql(3)
- end
-
- it "raises an IOError if self is at the end" do
- @io.pos = 40
- -> { @io.readline }.should raise_error(IOError)
- end
-end
-
-describe "StringIO#readline when in write-only mode" do
- it "raises an IOError" do
- io = StringIO.new(+"xyz", "w")
- -> { io.readline }.should raise_error(IOError)
-
- io = StringIO.new("xyz")
- io.close_read
- -> { io.readline }.should raise_error(IOError)
+ @io.pos = 36
+ -> { @io.readline(">", 3) }.should raise_error(IOError)
+ end
end
-end
-describe "StringIO#readline when passed [chomp]" do
- it "returns the data read without a trailing newline character" do
- io = StringIO.new("this>is>an>example\n")
- io.readline(chomp: true).should == "this>is>an>example"
- end
-end
+ describe "when passed no argument" do
+ it_behaves_like :stringio_gets_no_argument, :readline
-describe "StringIO#readline when passed [limit]" do
- before :each do
- @io = StringIO.new("this>is>an>example")
- end
+ it "raises an IOError if self is at the end" do
+ @io = StringIO.new("this>is>an>example")
- it "returns the data read until the limit is met" do
- io = StringIO.new("this>is>an>example\n")
- io.readline(3).should == "thi"
+ @io.pos = 36
+ -> { @io.readline }.should raise_error(IOError)
+ end
end
- it "returns a blank string when passed a limit of 0" do
- @io.readline(0).should == ""
+ describe "when passed [chomp]" do
+ it_behaves_like :stringio_gets_chomp, :readline
end
- it "ignores it when the limit is negative" do
- seen = []
- @io.readline(-4).should == "this>is>an>example"
+ describe "when in write-only mode" do
+ it_behaves_like :stringio_gets_write_only, :readline
end
end
diff --git a/spec/ruby/library/stringio/readpartial_spec.rb b/spec/ruby/library/stringio/readpartial_spec.rb
index f25cef4014..dadefb7837 100644
--- a/spec/ruby/library/stringio/readpartial_spec.rb
+++ b/spec/ruby/library/stringio/readpartial_spec.rb
@@ -10,13 +10,7 @@ describe "StringIO#readpartial" do
@string.close unless @string.closed?
end
- it "raises IOError on closed stream" do
- @string.close
- -> { @string.readpartial(10) }.should raise_error(IOError)
- end
-
it "reads at most the specified number of bytes" do
-
# buffered read
@string.read(1).should == 'S'
# return only specified number, not the whole buffer
@@ -30,6 +24,14 @@ describe "StringIO#readpartial" do
@string.readpartial(3).should == ", l"
end
+ it "reads after ungetc with multibyte characters in the buffer" do
+ @string = StringIO.new(+"∂φ/∂x = gaîté")
+ c = @string.getc
+ @string.ungetc(c)
+ @string.readpartial(3).should == "\xE2\x88\x82".b
+ @string.readpartial(3).should == "\xCF\x86/".b
+ end
+
it "reads after ungetc without data in the buffer" do
@string = StringIO.new
@string.write("f").should == 1
@@ -67,14 +69,34 @@ describe "StringIO#readpartial" do
it "raises IOError if the stream is closed" do
@string.close
- -> { @string.readpartial(1) }.should raise_error(IOError)
+ -> { @string.readpartial(1) }.should raise_error(IOError, "not opened for reading")
end
it "raises ArgumentError if the negative argument is provided" do
- -> { @string.readpartial(-1) }.should raise_error(ArgumentError)
+ -> { @string.readpartial(-1) }.should raise_error(ArgumentError, "negative length -1 given")
end
it "immediately returns an empty string if the length argument is 0" do
@string.readpartial(0).should == ""
end
+
+ it "raises IOError if the stream is closed and the length argument is 0" do
+ @string.close
+ -> { @string.readpartial(0) }.should raise_error(IOError, "not opened for reading")
+ end
+
+ it "clears and returns the given buffer if the length argument is 0" do
+ buffer = +"existing content"
+ @string.readpartial(0, buffer).should == buffer
+ buffer.should == ""
+ end
+
+ version_is StringIO::VERSION, "3.1.2" do # ruby_version_is "3.4"
+ it "preserves the encoding of the given buffer" do
+ buffer = ''.encode(Encoding::ISO_8859_1)
+ @string.readpartial(10, buffer)
+
+ buffer.encoding.should == Encoding::ISO_8859_1
+ end
+ end
end
diff --git a/spec/ruby/library/stringio/set_encoding_by_bom_spec.rb b/spec/ruby/library/stringio/set_encoding_by_bom_spec.rb
new file mode 100644
index 0000000000..1030aad042
--- /dev/null
+++ b/spec/ruby/library/stringio/set_encoding_by_bom_spec.rb
@@ -0,0 +1,237 @@
+require 'stringio'
+require_relative '../../spec_helper'
+
+# Should be synced with specs for IO#set_encoding_by_bom
+describe "StringIO#set_encoding_by_bom" do
+ it "returns nil if not readable" do
+ io = StringIO.new("".b, "wb")
+
+ io.set_encoding_by_bom.should be_nil
+ io.external_encoding.should == Encoding::ASCII_8BIT
+ end
+
+ it "returns the result encoding if found BOM UTF-8 sequence" do
+ io = StringIO.new("\u{FEFF}".b, "rb")
+
+ io.set_encoding_by_bom.should == Encoding::UTF_8
+ io.external_encoding.should == Encoding::UTF_8
+ io.read.b.should == "".b
+
+ io = StringIO.new("\u{FEFF}abc".b, "rb")
+
+ io.set_encoding_by_bom.should == Encoding::UTF_8
+ io.external_encoding.should == Encoding::UTF_8
+ io.read.b.should == "abc".b
+ end
+
+ it "returns the result encoding if found BOM UTF_16LE sequence" do
+ io = StringIO.new("\xFF\xFE".b, "rb")
+
+ io.set_encoding_by_bom.should == Encoding::UTF_16LE
+ io.external_encoding.should == Encoding::UTF_16LE
+ io.read.b.should == "".b
+
+ io = StringIO.new("\xFF\xFEabc".b, "rb")
+
+ io.set_encoding_by_bom.should == Encoding::UTF_16LE
+ io.external_encoding.should == Encoding::UTF_16LE
+ io.read.b.should == "abc".b
+ end
+
+ it "returns the result encoding if found BOM UTF_16BE sequence" do
+ io = StringIO.new("\xFE\xFF".b, "rb")
+
+ io.set_encoding_by_bom.should == Encoding::UTF_16BE
+ io.external_encoding.should == Encoding::UTF_16BE
+ io.read.b.should == "".b
+
+ io = StringIO.new("\xFE\xFFabc".b, "rb")
+
+ io.set_encoding_by_bom.should == Encoding::UTF_16BE
+ io.external_encoding.should == Encoding::UTF_16BE
+ io.read.b.should == "abc".b
+ end
+
+ it "returns the result encoding if found BOM UTF_32LE sequence" do
+ io = StringIO.new("\xFF\xFE\x00\x00".b, "rb")
+
+ io.set_encoding_by_bom.should == Encoding::UTF_32LE
+ io.external_encoding.should == Encoding::UTF_32LE
+ io.read.b.should == "".b
+
+ io = StringIO.new("\xFF\xFE\x00\x00abc".b, "rb")
+
+ io.set_encoding_by_bom.should == Encoding::UTF_32LE
+ io.external_encoding.should == Encoding::UTF_32LE
+ io.read.b.should == "abc".b
+ end
+
+ it "returns the result encoding if found BOM UTF_32BE sequence" do
+ io = StringIO.new("\x00\x00\xFE\xFF".b, "rb")
+
+ io.set_encoding_by_bom.should == Encoding::UTF_32BE
+ io.external_encoding.should == Encoding::UTF_32BE
+ io.read.b.should == "".b
+
+ io = StringIO.new("\x00\x00\xFE\xFFabc".b, "rb")
+
+ io.set_encoding_by_bom.should == Encoding::UTF_32BE
+ io.external_encoding.should == Encoding::UTF_32BE
+ io.read.b.should == "abc".b
+ end
+
+ it "returns nil if io is empty" do
+ io = StringIO.new("".b, "rb")
+ io.set_encoding_by_bom.should be_nil
+ io.external_encoding.should == Encoding::ASCII_8BIT
+ end
+
+ it "returns nil if UTF-8 BOM sequence is incomplete" do
+ io = StringIO.new("\xEF".b, "rb")
+
+ io.set_encoding_by_bom.should == nil
+ io.external_encoding.should == Encoding::ASCII_8BIT
+ io.read.b.should == "\xEF".b
+
+ io = StringIO.new("\xEFa".b, "rb")
+
+ io.set_encoding_by_bom.should == nil
+ io.external_encoding.should == Encoding::ASCII_8BIT
+ io.read.b.should == "\xEFa".b
+
+ io = StringIO.new("\xEF\xBB".b, "rb")
+
+ io.set_encoding_by_bom.should == nil
+ io.external_encoding.should == Encoding::ASCII_8BIT
+ io.read.b.should == "\xEF\xBB".b
+
+ io = StringIO.new("\xEF\xBBa".b, "rb")
+
+ io.set_encoding_by_bom.should == nil
+ io.external_encoding.should == Encoding::ASCII_8BIT
+ io.read.b.should == "\xEF\xBBa".b
+ end
+
+ it "returns nil if UTF-16BE BOM sequence is incomplete" do
+ io = StringIO.new("\xFE".b, "rb")
+
+ io.set_encoding_by_bom.should == nil
+ io.external_encoding.should == Encoding::ASCII_8BIT
+ io.read.b.should == "\xFE".b
+
+ io = StringIO.new("\xFEa".b, "rb")
+
+ io.set_encoding_by_bom.should == nil
+ io.external_encoding.should == Encoding::ASCII_8BIT
+ io.read.b.should == "\xFEa".b
+ end
+
+ it "returns nil if UTF-16LE/UTF-32LE BOM sequence is incomplete" do
+ io = StringIO.new("\xFF".b, "rb")
+
+ io.set_encoding_by_bom.should == nil
+ io.external_encoding.should == Encoding::ASCII_8BIT
+ io.read.b.should == "\xFF".b
+
+ io = StringIO.new("\xFFa".b, "rb")
+
+ io.set_encoding_by_bom.should == nil
+ io.external_encoding.should == Encoding::ASCII_8BIT
+ io.read.b.should == "\xFFa".b
+ end
+
+ it "returns UTF-16LE if UTF-32LE BOM sequence is incomplete" do
+ io = StringIO.new("\xFF\xFE".b, "rb")
+
+ io.set_encoding_by_bom.should == Encoding::UTF_16LE
+ io.external_encoding.should == Encoding::UTF_16LE
+ io.read.b.should == "".b
+
+ io = StringIO.new("\xFF\xFE\x00".b, "rb")
+
+ io.set_encoding_by_bom.should == Encoding::UTF_16LE
+ io.external_encoding.should == Encoding::UTF_16LE
+ io.read.b.should == "\x00".b
+
+ io = StringIO.new("\xFF\xFE\x00a".b, "rb")
+
+ io.set_encoding_by_bom.should == Encoding::UTF_16LE
+ io.external_encoding.should == Encoding::UTF_16LE
+ io.read.b.should == "\x00a".b
+ end
+
+ it "returns nil if UTF-32BE BOM sequence is incomplete" do
+ io = StringIO.new("\x00".b, "rb")
+
+ io.set_encoding_by_bom.should == nil
+ io.external_encoding.should == Encoding::ASCII_8BIT
+ io.read.b.should == "\x00".b
+
+ io = StringIO.new("\x00a".b, "rb")
+
+ io.set_encoding_by_bom.should == nil
+ io.external_encoding.should == Encoding::ASCII_8BIT
+ io.read.b.should == "\x00a".b
+
+ io = StringIO.new("\x00\x00".b, "rb")
+
+ io.set_encoding_by_bom.should == nil
+ io.external_encoding.should == Encoding::ASCII_8BIT
+ io.read.b.should == "\x00\x00".b
+
+ io = StringIO.new("\x00\x00a".b, "rb")
+
+ io.set_encoding_by_bom.should == nil
+ io.external_encoding.should == Encoding::ASCII_8BIT
+ io.read.b.should == "\x00\x00a".b
+
+ io = StringIO.new("\x00\x00\xFE".b, "rb")
+
+ io.set_encoding_by_bom.should == nil
+ io.external_encoding.should == Encoding::ASCII_8BIT
+ io.read.b.should == "\x00\x00\xFE".b
+
+ io = StringIO.new("\x00\x00\xFEa".b, "rb")
+
+ io.set_encoding_by_bom.should == nil
+ io.external_encoding.should == Encoding::ASCII_8BIT
+ io.read.b.should == "\x00\x00\xFEa".b
+ end
+
+ it "returns nil if found BOM sequence not provided" do
+ io = StringIO.new("abc".b, "rb")
+
+ io.set_encoding_by_bom.should == nil
+ io.external_encoding.should == Encoding::ASCII_8BIT
+ io.read(3).should == "abc".b
+ end
+
+ it "does not raise exception if io not in binary mode" do
+ io = StringIO.new("", 'r')
+ io.set_encoding_by_bom.should == nil
+ end
+
+ it "does not raise exception if encoding already set" do
+ io = StringIO.new("".b, "rb")
+ io.set_encoding("utf-8")
+ io.set_encoding_by_bom.should == nil
+ end
+
+ it "does not raise exception if encoding conversion is already set" do
+ io = StringIO.new("".b, "rb")
+ io.set_encoding(Encoding::UTF_8, Encoding::UTF_16BE)
+
+ io.set_encoding_by_bom.should == nil
+ end
+
+ it "raises FrozenError when io is frozen" do
+ io = StringIO.new()
+ io.freeze
+ -> { io.set_encoding_by_bom }.should raise_error(FrozenError)
+ end
+
+ it "does not raise FrozenError when initial string is frozen" do
+ io = StringIO.new("".freeze)
+ io.set_encoding_by_bom.should == nil
+ end
+end
diff --git a/spec/ruby/library/stringio/shared/each.rb b/spec/ruby/library/stringio/shared/each.rb
index e0dd3f9b8f..626b41a4d3 100644
--- a/spec/ruby/library/stringio/shared/each.rb
+++ b/spec/ruby/library/stringio/shared/each.rb
@@ -36,22 +36,11 @@ describe :stringio_each_separator, shared: true do
seen.should == ["2 1 2 1 2"]
end
- version_is StringIO::VERSION, ""..."3.0.4" do #ruby_version_is ""..."3.2" do
- it "yields each paragraph with two separation characters when passed an empty String as separator" do
- seen = []
- io = StringIO.new("para1\n\npara2\n\n\npara3")
- io.send(@method, "") {|s| seen << s}
- seen.should == ["para1\n\n", "para2\n\n", "para3"]
- end
- end
-
- version_is StringIO::VERSION, "3.0.4" do #ruby_version_is "3.2" do
- it "yields each paragraph with all separation characters when passed an empty String as separator" do
- seen = []
- io = StringIO.new("para1\n\npara2\n\n\npara3")
- io.send(@method, "") {|s| seen << s}
- seen.should == ["para1\n\n", "para2\n\n\n", "para3"]
- end
+ it "yields each paragraph with all separation characters when passed an empty String as separator" do
+ seen = []
+ io = StringIO.new("para1\n\npara2\n\n\npara3")
+ io.send(@method, "") {|s| seen << s}
+ seen.should == ["para1\n\n", "para2\n\n\n", "para3"]
end
end
@@ -161,3 +150,60 @@ describe :stringio_each_limit, shared: true do
seen.should == ["a b ", "c d ", "e\n", "1 2 ", "3 4 ", "5"]
end
end
+
+describe :stringio_each_separator_and_limit, shared: true do
+ before :each do
+ @io = StringIO.new("this>is>an>example")
+ end
+
+ it "returns the data read until the limit is consumed or the separator is met" do
+ @io.send(@method, '>', 8) { |s| break s }.should == "this>"
+ @io.send(@method, '>', 2) { |s| break s }.should == "is"
+ @io.send(@method, '>', 10) { |s| break s }.should == ">"
+ @io.send(@method, '>', 6) { |s| break s }.should == "an>"
+ @io.send(@method, '>', 5) { |s| break s }.should == "examp"
+ end
+
+ it "truncates the multi-character separator at the end to meet the limit" do
+ @io.send(@method, "is>an", 7) { |s| break s }.should == "this>is"
+ end
+
+ it "does not change $_" do
+ $_ = "test"
+ @io.send(@method, '>', 8) { |s| s }
+ $_.should == "test"
+ end
+
+ it "updates self's lineno by one" do
+ @io.send(@method, '>', 3) { |s| break s }
+ @io.lineno.should eql(1)
+
+ @io.send(@method, '>', 3) { |s| break s }
+ @io.lineno.should eql(2)
+
+ @io.send(@method, '>', 3) { |s| break s }
+ @io.lineno.should eql(3)
+ end
+
+ it "tries to convert the passed separator to a String using #to_str" do # TODO
+ obj = mock('to_str')
+ obj.should_receive(:to_str).and_return('>')
+
+ seen = []
+ @io.send(@method, obj, 5) { |s| seen << s }
+ seen.should == ["this>", "is>", "an>", "examp", "le"]
+ end
+
+ it "does not raise TypeError if passed separator is nil" do
+ @io.send(@method, nil, 5) { |s| break s }.should == "this>"
+ end
+
+ it "tries to convert the passed limit to an Integer using #to_int" do # TODO
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(5)
+
+ seen = []
+ @io.send(@method, '>', obj) { |s| seen << s }
+ seen.should == ["this>", "is>", "an>", "examp", "le"]
+ end
+end
diff --git a/spec/ruby/library/stringio/shared/gets.rb b/spec/ruby/library/stringio/shared/gets.rb
new file mode 100644
index 0000000000..8396b161f1
--- /dev/null
+++ b/spec/ruby/library/stringio/shared/gets.rb
@@ -0,0 +1,249 @@
+describe :stringio_gets_separator, shared: true do
+ describe "when passed [separator]" do
+ before :each do
+ @io = StringIO.new("this>is>an>example")
+ end
+
+ it "returns the data read till the next occurrence of the passed separator" do
+ @io.send(@method, ">").should == "this>"
+ @io.send(@method, ">").should == "is>"
+ @io.send(@method, ">").should == "an>"
+ @io.send(@method, ">").should == "example"
+ end
+
+ it "sets $_ to the read content" do
+ @io.send(@method, ">")
+ $_.should == "this>"
+ @io.send(@method, ">")
+ $_.should == "is>"
+ @io.send(@method, ">")
+ $_.should == "an>"
+ @io.send(@method, ">")
+ $_.should == "example"
+ end
+
+ it "accepts string as separator" do
+ @io.send(@method, "is>")
+ $_.should == "this>"
+ @io.send(@method, "an>")
+ $_.should == "is>an>"
+ @io.send(@method, "example")
+ $_.should == "example"
+ end
+
+ it "updates self's lineno by one" do
+ @io.send(@method, ">")
+ @io.lineno.should eql(1)
+
+ @io.send(@method, ">")
+ @io.lineno.should eql(2)
+
+ @io.send(@method, ">")
+ @io.lineno.should eql(3)
+ end
+
+ it "returns the next paragraph when the passed separator is an empty String" do
+ io = StringIO.new("this is\n\nan example")
+ io.send(@method, "").should == "this is\n\n"
+ io.send(@method, "").should == "an example"
+ end
+
+ it "returns the remaining content starting at the current position when passed nil" do
+ io = StringIO.new("this is\n\nan example")
+ io.pos = 5
+ io.send(@method, nil).should == "is\n\nan example"
+ end
+
+ it "tries to convert the passed separator to a String using #to_str" do
+ obj = mock('to_str')
+ obj.should_receive(:to_str).and_return(">")
+ @io.send(@method, obj).should == "this>"
+ end
+ end
+end
+
+describe :stringio_gets_limit, shared: true do
+ describe "when passed [limit]" do
+ before :each do
+ @io = StringIO.new("this>is>an>example")
+ end
+
+ it "returns the data read until the limit is met" do
+ @io.send(@method, 4).should == "this"
+ @io.send(@method, 3).should == ">is"
+ @io.send(@method, 5).should == ">an>e"
+ @io.send(@method, 6).should == "xample"
+ end
+
+ it "sets $_ to the read content" do
+ @io.send(@method, 4)
+ $_.should == "this"
+ @io.send(@method, 3)
+ $_.should == ">is"
+ @io.send(@method, 5)
+ $_.should == ">an>e"
+ @io.send(@method, 6)
+ $_.should == "xample"
+ end
+
+ it "updates self's lineno by one" do
+ @io.send(@method, 3)
+ @io.lineno.should eql(1)
+
+ @io.send(@method, 3)
+ @io.lineno.should eql(2)
+
+ @io.send(@method, 3)
+ @io.lineno.should eql(3)
+ end
+
+ it "tries to convert the passed limit to an Integer using #to_int" do
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(4)
+ @io.send(@method, obj).should == "this"
+ end
+
+ it "returns a blank string when passed a limit of 0" do
+ @io.send(@method, 0).should == ""
+ end
+
+ it "ignores it when passed a negative limit" do
+ @io.send(@method, -4).should == "this>is>an>example"
+ end
+ end
+end
+
+describe :stringio_gets_separator_and_limit, shared: true do
+ describe "when passed [separator] and [limit]" do
+ before :each do
+ @io = StringIO.new("this>is>an>example")
+ end
+
+ it "returns the data read until the limit is consumed or the separator is met" do
+ @io.send(@method, '>', 8).should == "this>"
+ @io.send(@method, '>', 2).should == "is"
+ @io.send(@method, '>', 10).should == ">"
+ @io.send(@method, '>', 6).should == "an>"
+ @io.send(@method, '>', 5).should == "examp"
+ end
+
+ it "truncates the multi-character separator at the end to meet the limit" do
+ @io.send(@method, "is>an", 7).should == "this>is"
+ end
+
+ it "sets $_ to the read content" do
+ @io.send(@method, '>', 8)
+ $_.should == "this>"
+ @io.send(@method, '>', 2)
+ $_.should == "is"
+ @io.send(@method, '>', 10)
+ $_.should == ">"
+ @io.send(@method, '>', 6)
+ $_.should == "an>"
+ @io.send(@method, '>', 5)
+ $_.should == "examp"
+ end
+
+ it "updates self's lineno by one" do
+ @io.send(@method, '>', 3)
+ @io.lineno.should eql(1)
+
+ @io.send(@method, '>', 3)
+ @io.lineno.should eql(2)
+
+ @io.send(@method, '>', 3)
+ @io.lineno.should eql(3)
+ end
+
+ it "tries to convert the passed separator to a String using #to_str" do
+ obj = mock('to_str')
+ obj.should_receive(:to_str).and_return('>')
+ @io.send(@method, obj, 5).should == "this>"
+ end
+
+ it "does not raise TypeError if passed separator is nil" do
+ @io.send(@method, nil, 5).should == "this>"
+ end
+
+ it "tries to convert the passed limit to an Integer using #to_int" do
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(5)
+ @io.send(@method, '>', obj).should == "this>"
+ end
+ end
+end
+
+describe :stringio_gets_no_argument, shared: true do
+ describe "when passed no argument" do
+ before :each do
+ @io = StringIO.new("this is\nan example\nfor StringIO#gets")
+ end
+
+ it "returns the data read till the next occurrence of $/ or till eof" do
+ @io.send(@method).should == "this is\n"
+
+ begin
+ old_sep = $/
+ suppress_warning {$/ = " "}
+ @io.send(@method).should == "an "
+ @io.send(@method).should == "example\nfor "
+ @io.send(@method).should == "StringIO#gets"
+ ensure
+ suppress_warning {$/ = old_sep}
+ end
+ end
+
+ it "sets $_ to the read content" do
+ @io.send(@method)
+ $_.should == "this is\n"
+ @io.send(@method)
+ $_.should == "an example\n"
+ @io.send(@method)
+ $_.should == "for StringIO#gets"
+ end
+
+ it "updates self's position" do
+ @io.send(@method)
+ @io.pos.should eql(8)
+
+ @io.send(@method)
+ @io.pos.should eql(19)
+
+ @io.send(@method)
+ @io.pos.should eql(36)
+ end
+
+ it "updates self's lineno" do
+ @io.send(@method)
+ @io.lineno.should eql(1)
+
+ @io.send(@method)
+ @io.lineno.should eql(2)
+
+ @io.send(@method)
+ @io.lineno.should eql(3)
+ end
+ end
+end
+
+describe :stringio_gets_chomp, shared: true do
+ describe "when passed [chomp]" do
+ it "returns the data read without a trailing newline character" do
+ io = StringIO.new("this>is>an>example\n")
+ io.send(@method, chomp: true).should == "this>is>an>example"
+ end
+ end
+end
+
+describe :stringio_gets_write_only, shared: true do
+ describe "when in write-only mode" do
+ it "raises an IOError" do
+ io = StringIO.new(+"xyz", "w")
+ -> { io.send(@method) }.should raise_error(IOError)
+
+ io = StringIO.new("xyz")
+ io.close_read
+ -> { io.send(@method) }.should raise_error(IOError)
+ end
+ end
+end
diff --git a/spec/ruby/library/stringio/shared/read.rb b/spec/ruby/library/stringio/shared/read.rb
index 8ef6ec2734..22f76b0fb0 100644
--- a/spec/ruby/library/stringio/shared/read.rb
+++ b/spec/ruby/library/stringio/shared/read.rb
@@ -15,7 +15,7 @@ describe :stringio_read, shared: true do
buffer.should == "example"
end
- ruby_version_is ""..."3.4" do
+ guard -> { StringIO::VERSION < "3.1.2" } do
it "does not preserve the encoding of the given buffer" do
buffer = ''.encode(Encoding::ISO_8859_1)
@io.send(@method, 7, buffer)
@@ -24,7 +24,7 @@ describe :stringio_read, shared: true do
end
end
- ruby_version_is "3.4" do
+ guard -> { StringIO::VERSION >= "3.1.2" } do
it "preserves the encoding of the given buffer" do
buffer = ''.encode(Encoding::ISO_8859_1)
@io.send(@method, 7, buffer)
diff --git a/spec/ruby/library/stringio/shared/sysread.rb b/spec/ruby/library/stringio/shared/sysread.rb
index 937bac705c..3e23fbc233 100644
--- a/spec/ruby/library/stringio/shared/sysread.rb
+++ b/spec/ruby/library/stringio/shared/sysread.rb
@@ -10,6 +10,6 @@ describe :stringio_sysread_length, shared: true do
it "raises an EOFError when passed length > 0 and no data remains" do
@io.read.should == "example"
- -> { @io.sysread(1) }.should raise_error(EOFError)
+ -> { @io.send(@method, 1) }.should raise_error(EOFError)
end
end
diff --git a/spec/ruby/library/stringio/shared/write.rb b/spec/ruby/library/stringio/shared/write.rb
index 404e08b93d..4661658baf 100644
--- a/spec/ruby/library/stringio/shared/write.rb
+++ b/spec/ruby/library/stringio/shared/write.rb
@@ -94,7 +94,7 @@ describe :stringio_write_string, shared: true do
end
it "does not transcode the given string when the external encoding is set and the string encoding is BINARY" do
- str = "été".b
+ str = "été_".b
io = StringIO.new.set_encoding(Encoding::UTF_16BE)
io.external_encoding.should == Encoding::UTF_16BE
diff --git a/spec/ruby/library/stringio/sysread_spec.rb b/spec/ruby/library/stringio/sysread_spec.rb
index 8f78073f42..fabb06dd9a 100644
--- a/spec/ruby/library/stringio/sysread_spec.rb
+++ b/spec/ruby/library/stringio/sysread_spec.rb
@@ -1,6 +1,7 @@
require_relative '../../spec_helper'
require "stringio"
require_relative 'shared/read'
+require_relative 'shared/sysread'
describe "StringIO#sysread when passed length, buffer" do
it_behaves_like :stringio_read, :sysread
@@ -32,6 +33,10 @@ describe "StringIO#sysread when passed nil" do
end
end
+describe "StringIO#sysread when passed length" do
+ it_behaves_like :stringio_sysread_length, :sysread
+end
+
describe "StringIO#sysread when passed [length]" do
before :each do
@io = StringIO.new("example")