diff options
Diffstat (limited to 'spec/ruby/library/stringio')
80 files changed, 3675 insertions, 0 deletions
diff --git a/spec/ruby/library/stringio/append_spec.rb b/spec/ruby/library/stringio/append_spec.rb new file mode 100644 index 0000000000..d0cf5550cd --- /dev/null +++ b/spec/ruby/library/stringio/append_spec.rb @@ -0,0 +1,88 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "StringIO#<< when passed [Object]" do + before :each do + @io = StringIO.new("example") + end + + it "returns self" do + (@io << "just testing").should equal(@io) + end + + it "writes the passed argument onto self" do + (@io << "just testing") + @io.string.should == "just testing" + (@io << " and more testing") + @io.string.should == "just testing and more testing" + end + + it "writes the passed argument at the current position" do + @io.pos = 5 + @io << "<test>" + @io.string.should == "examp<test>" + end + + it "pads self with \\000 when the current position is after the end" do + @io.pos = 15 + @io << "just testing" + @io.string.should == "example\000\000\000\000\000\000\000\000just testing" + end + + ruby_version_is ""..."2.7" do + it "taints self's String when the passed argument is tainted" do + (@io << "test".taint) + @io.string.tainted?.should be_true + end + end + + ruby_version_is ""..."3.0" do + it "does not taint self when the passed argument is tainted" do + (@io << "test".taint) + @io.tainted?.should be_false + end + end + + it "updates self's position" do + @io << "test" + @io.pos.should eql(4) + end + + it "tries to convert the passed argument to a String using #to_s" do + obj = mock("to_s") + obj.should_receive(:to_s).and_return("Test") + + (@io << obj).string.should == "Testple" + end +end + +describe "StringIO#<< when self is not writable" do + it "raises an IOError" do + io = StringIO.new("test", "r") + -> { io << "test" }.should raise_error(IOError) + + io = StringIO.new("test") + io.close_write + -> { io << "test" }.should raise_error(IOError) + end +end + +describe "StringIO#<< when in append mode" do + before :each do + @io = StringIO.new("example", "a") + end + + it "appends the passed argument to the end of self, ignoring current position" do + (@io << ", just testing") + @io.string.should == "example, just testing" + + @io.pos = 3 + (@io << " and more testing") + @io.string.should == "example, just testing and more testing" + end + + it "correctly updates self's position" do + @io << ", testing" + @io.pos.should eql(16) + end +end diff --git a/spec/ruby/library/stringio/binmode_spec.rb b/spec/ruby/library/stringio/binmode_spec.rb new file mode 100644 index 0000000000..853d9c9bd6 --- /dev/null +++ b/spec/ruby/library/stringio/binmode_spec.rb @@ -0,0 +1,23 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "StringIO#binmode" do + it "returns self" do + io = StringIO.new("example") + io.binmode.should equal(io) + end + + it "changes external encoding to BINARY" do + io = StringIO.new + io.external_encoding.should == Encoding.find('external') + io.binmode + io.external_encoding.should == Encoding::BINARY + end + + it "does not set internal encoding" do + io = StringIO.new + io.internal_encoding.should == nil + io.binmode + io.internal_encoding.should == nil + end +end diff --git a/spec/ruby/library/stringio/bytes_spec.rb b/spec/ruby/library/stringio/bytes_spec.rb new file mode 100644 index 0000000000..4ef7a490a5 --- /dev/null +++ b/spec/ruby/library/stringio/bytes_spec.rb @@ -0,0 +1,29 @@ +require_relative '../../spec_helper' +require 'stringio' +require_relative 'shared/each_byte' + +ruby_version_is ''...'3.0' do + describe "StringIO#bytes" do + before :each do + @verbose, $VERBOSE = $VERBOSE, nil + end + + after :each do + $VERBOSE = @verbose + end + + it_behaves_like :stringio_each_byte, :bytes + end + + describe "StringIO#bytes when self is not readable" do + before :each do + @verbose, $VERBOSE = $VERBOSE, nil + end + + after :each do + $VERBOSE = @verbose + end + + it_behaves_like :stringio_each_byte_not_readable, :bytes + end +end diff --git a/spec/ruby/library/stringio/chars_spec.rb b/spec/ruby/library/stringio/chars_spec.rb new file mode 100644 index 0000000000..58cba77634 --- /dev/null +++ b/spec/ruby/library/stringio/chars_spec.rb @@ -0,0 +1,29 @@ +require_relative '../../spec_helper' +require 'stringio' +require_relative 'shared/each_char' + +ruby_version_is ''...'3.0' do + describe "StringIO#chars" do + before :each do + @verbose, $VERBOSE = $VERBOSE, nil + end + + after :each do + $VERBOSE = @verbose + end + + it_behaves_like :stringio_each_char, :chars + end + + describe "StringIO#chars when self is not readable" do + before :each do + @verbose, $VERBOSE = $VERBOSE, nil + end + + after :each do + $VERBOSE = @verbose + end + + it_behaves_like :stringio_each_char_not_readable, :chars + end +end diff --git a/spec/ruby/library/stringio/close_read_spec.rb b/spec/ruby/library/stringio/close_read_spec.rb new file mode 100644 index 0000000000..80bd547e85 --- /dev/null +++ b/spec/ruby/library/stringio/close_read_spec.rb @@ -0,0 +1,31 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "StringIO#close_read" do + before :each do + @io = StringIO.new("example") + end + + it "returns nil" do + @io.close_read.should be_nil + end + + it "prevents further reading" do + @io.close_read + -> { @io.read(1) }.should raise_error(IOError) + end + + it "allows further writing" do + @io.close_read + @io.write("x").should == 1 + end + + it "raises an IOError when in write-only mode" do + io = StringIO.new("example", "w") + -> { io.close_read }.should raise_error(IOError) + + io = StringIO.new("example") + io.close_read + io.close_read.should == nil + end +end diff --git a/spec/ruby/library/stringio/close_spec.rb b/spec/ruby/library/stringio/close_spec.rb new file mode 100644 index 0000000000..520a8de782 --- /dev/null +++ b/spec/ruby/library/stringio/close_spec.rb @@ -0,0 +1,23 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "StringIO#close" do + before :each do + @io = StringIOSpecs.build + end + + it "returns nil" do + @io.close.should be_nil + end + + it "prevents further reading and/or writing" do + @io.close + -> { @io.read(1) }.should raise_error(IOError) + -> { @io.write('x') }.should raise_error(IOError) + end + + it "does not raise anything when self was already closed" do + @io.close + -> { @io.close }.should_not raise_error(IOError) + end +end diff --git a/spec/ruby/library/stringio/close_write_spec.rb b/spec/ruby/library/stringio/close_write_spec.rb new file mode 100644 index 0000000000..1a4cfa113e --- /dev/null +++ b/spec/ruby/library/stringio/close_write_spec.rb @@ -0,0 +1,31 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "StringIO#close_write" do + before :each do + @io = StringIO.new("example") + end + + it "returns nil" do + @io.close_write.should be_nil + end + + it "prevents further writing" do + @io.close_write + -> { @io.write('x') }.should raise_error(IOError) + end + + it "allows further reading" do + @io.close_write + @io.read(1).should == 'e' + end + + it "raises an IOError when in read-only mode" do + io = StringIO.new("example", "r") + -> { io.close_write }.should raise_error(IOError) + + io = StringIO.new("example") + io.close_write + io.close_write.should == nil + end +end diff --git a/spec/ruby/library/stringio/closed_read_spec.rb b/spec/ruby/library/stringio/closed_read_spec.rb new file mode 100644 index 0000000000..cb4267ac98 --- /dev/null +++ b/spec/ruby/library/stringio/closed_read_spec.rb @@ -0,0 +1,12 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "StringIO#closed_read?" do + it "returns true if self is not readable" do + io = StringIO.new("example", "r+") + io.close_write + io.closed_read?.should be_false + io.close_read + io.closed_read?.should be_true + end +end diff --git a/spec/ruby/library/stringio/closed_spec.rb b/spec/ruby/library/stringio/closed_spec.rb new file mode 100644 index 0000000000..ca8a2232a8 --- /dev/null +++ b/spec/ruby/library/stringio/closed_spec.rb @@ -0,0 +1,16 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "StringIO#closed?" do + it "returns true if self is completely closed" do + io = StringIO.new("example", "r+") + io.close_read + io.closed?.should be_false + io.close_write + io.closed?.should be_true + + io = StringIO.new("example", "r+") + io.close + io.closed?.should be_true + end +end diff --git a/spec/ruby/library/stringio/closed_write_spec.rb b/spec/ruby/library/stringio/closed_write_spec.rb new file mode 100644 index 0000000000..5c111affd8 --- /dev/null +++ b/spec/ruby/library/stringio/closed_write_spec.rb @@ -0,0 +1,12 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "StringIO#closed_write?" do + it "returns true if self is not writable" do + io = StringIO.new("example", "r+") + io.close_read + io.closed_write?.should be_false + io.close_write + io.closed_write?.should be_true + end +end diff --git a/spec/ruby/library/stringio/codepoints_spec.rb b/spec/ruby/library/stringio/codepoints_spec.rb new file mode 100644 index 0000000000..ceaadefc32 --- /dev/null +++ b/spec/ruby/library/stringio/codepoints_spec.rb @@ -0,0 +1,19 @@ +# -*- encoding: utf-8 -*- +require_relative '../../spec_helper' +require_relative 'fixtures/classes' +require_relative 'shared/codepoints' + +ruby_version_is ''...'3.0' do + # See redmine #1667 + describe "StringIO#codepoints" do + before :each do + @verbose, $VERBOSE = $VERBOSE, nil + end + + after :each do + $VERBOSE = @verbose + end + + it_behaves_like :stringio_codepoints, :codepoints + end +end diff --git a/spec/ruby/library/stringio/each_byte_spec.rb b/spec/ruby/library/stringio/each_byte_spec.rb new file mode 100644 index 0000000000..6f82a32441 --- /dev/null +++ b/spec/ruby/library/stringio/each_byte_spec.rb @@ -0,0 +1,11 @@ +require_relative '../../spec_helper' +require 'stringio' +require_relative 'shared/each_byte' + +describe "StringIO#each_byte" do + it_behaves_like :stringio_each_byte, :each_byte +end + +describe "StringIO#each_byte when self is not readable" do + it_behaves_like :stringio_each_byte_not_readable, :each_byte +end diff --git a/spec/ruby/library/stringio/each_char_spec.rb b/spec/ruby/library/stringio/each_char_spec.rb new file mode 100644 index 0000000000..14b2f09a17 --- /dev/null +++ b/spec/ruby/library/stringio/each_char_spec.rb @@ -0,0 +1,11 @@ +require_relative '../../spec_helper' +require 'stringio' +require_relative 'shared/each_char' + +describe "StringIO#each_char" do + it_behaves_like :stringio_each_char, :each_char +end + +describe "StringIO#each_char when self is not readable" do + it_behaves_like :stringio_each_char_not_readable, :each_char +end diff --git a/spec/ruby/library/stringio/each_codepoint_spec.rb b/spec/ruby/library/stringio/each_codepoint_spec.rb new file mode 100644 index 0000000000..f18de22aad --- /dev/null +++ b/spec/ruby/library/stringio/each_codepoint_spec.rb @@ -0,0 +1,9 @@ +# -*- encoding: utf-8 -*- +require_relative '../../spec_helper' +require_relative 'fixtures/classes' +require_relative 'shared/codepoints' + +# See redmine #1667 +describe "StringIO#each_codepoint" do + it_behaves_like :stringio_codepoints, :each_codepoint +end diff --git a/spec/ruby/library/stringio/each_line_spec.rb b/spec/ruby/library/stringio/each_line_spec.rb new file mode 100644 index 0000000000..1389408399 --- /dev/null +++ b/spec/ruby/library/stringio/each_line_spec.rb @@ -0,0 +1,19 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' +require_relative 'shared/each' + +describe "StringIO#each_line when passed a separator" do + it_behaves_like :stringio_each_separator, :each_line +end + +describe "StringIO#each_line when passed no arguments" do + it_behaves_like :stringio_each_no_arguments, :each_line +end + +describe "StringIO#each_line when self is not readable" do + it_behaves_like :stringio_each_not_readable, :each_line +end + +describe "StringIO#each_line when passed chomp" do + it_behaves_like :stringio_each_chomp, :each_line +end diff --git a/spec/ruby/library/stringio/each_spec.rb b/spec/ruby/library/stringio/each_spec.rb new file mode 100644 index 0000000000..a76460049b --- /dev/null +++ b/spec/ruby/library/stringio/each_spec.rb @@ -0,0 +1,19 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' +require_relative 'shared/each' + +describe "StringIO#each when passed a separator" do + it_behaves_like :stringio_each_separator, :each +end + +describe "StringIO#each when passed no arguments" do + it_behaves_like :stringio_each_no_arguments, :each +end + +describe "StringIO#each when self is not readable" do + it_behaves_like :stringio_each_not_readable, :each +end + +describe "StringIO#each when passed chomp" do + it_behaves_like :stringio_each_chomp, :each +end diff --git a/spec/ruby/library/stringio/eof_spec.rb b/spec/ruby/library/stringio/eof_spec.rb new file mode 100644 index 0000000000..af0170977c --- /dev/null +++ b/spec/ruby/library/stringio/eof_spec.rb @@ -0,0 +1,11 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' +require_relative 'shared/eof' + +describe "StringIO#eof?" do + it_behaves_like :stringio_eof, :eof? +end + +describe "StringIO#eof" do + it_behaves_like :stringio_eof, :eof +end diff --git a/spec/ruby/library/stringio/external_encoding_spec.rb b/spec/ruby/library/stringio/external_encoding_spec.rb new file mode 100644 index 0000000000..6c5edb1713 --- /dev/null +++ b/spec/ruby/library/stringio/external_encoding_spec.rb @@ -0,0 +1,25 @@ +require 'stringio' +require_relative '../../spec_helper' + +describe "StringIO#external_encoding" do + it "gets the encoding of the underlying String" do + io = StringIO.new + io.set_encoding Encoding::EUC_JP + io.external_encoding.should == Encoding::EUC_JP + end + + it "changes to match string if string's encoding is changed" do + io = StringIO.new + io.string.force_encoding(Encoding::EUC_JP) + io.external_encoding.should == Encoding::EUC_JP + end + + it "does not set the encoding of its buffer string if the string is frozen" do + str = "foo".freeze + enc = str.encoding + io = StringIO.new(str) + io.set_encoding Encoding::EUC_JP + io.external_encoding.should == Encoding::EUC_JP + str.encoding.should == enc + end +end diff --git a/spec/ruby/library/stringio/fcntl_spec.rb b/spec/ruby/library/stringio/fcntl_spec.rb new file mode 100644 index 0000000000..a78004d868 --- /dev/null +++ b/spec/ruby/library/stringio/fcntl_spec.rb @@ -0,0 +1,8 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "StringIO#fcntl" do + it "raises a NotImplementedError" do + -> { StringIO.new("boom").fcntl }.should raise_error(NotImplementedError) + end +end diff --git a/spec/ruby/library/stringio/fileno_spec.rb b/spec/ruby/library/stringio/fileno_spec.rb new file mode 100644 index 0000000000..eea03a5af3 --- /dev/null +++ b/spec/ruby/library/stringio/fileno_spec.rb @@ -0,0 +1,9 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' +require_relative 'shared/each' + +describe "StringIO#fileno" do + it "returns nil" do + StringIO.new("nuffin").fileno.should be_nil + end +end diff --git a/spec/ruby/library/stringio/fixtures/classes.rb b/spec/ruby/library/stringio/fixtures/classes.rb new file mode 100644 index 0000000000..bb8dc354cc --- /dev/null +++ b/spec/ruby/library/stringio/fixtures/classes.rb @@ -0,0 +1,15 @@ +require 'stringio' + +class StringSubclass < String; end + +module StringIOSpecs + def self.build + str = <<-EOS + each + peach + pear + plum + EOS + StringIO.new(str) + end +end diff --git a/spec/ruby/library/stringio/flush_spec.rb b/spec/ruby/library/stringio/flush_spec.rb new file mode 100644 index 0000000000..17a16dfdd5 --- /dev/null +++ b/spec/ruby/library/stringio/flush_spec.rb @@ -0,0 +1,9 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "StringIO#flush" do + it "returns self" do + io = StringIO.new("flush") + io.flush.should equal(io) + end +end diff --git a/spec/ruby/library/stringio/fsync_spec.rb b/spec/ruby/library/stringio/fsync_spec.rb new file mode 100644 index 0000000000..8fb2b59a24 --- /dev/null +++ b/spec/ruby/library/stringio/fsync_spec.rb @@ -0,0 +1,9 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "StringIO#fsync" do + it "returns zero" do + io = StringIO.new("fsync") + io.fsync.should eql(0) + end +end diff --git a/spec/ruby/library/stringio/getbyte_spec.rb b/spec/ruby/library/stringio/getbyte_spec.rb new file mode 100644 index 0000000000..3daa3d8e02 --- /dev/null +++ b/spec/ruby/library/stringio/getbyte_spec.rb @@ -0,0 +1,19 @@ +require_relative '../../spec_helper' +require 'stringio' +require_relative 'shared/getc' + +describe "StringIO#getbyte" do + it_behaves_like :stringio_getc, :getbyte + + it "returns the 8-bit byte at the current position" do + io = StringIO.new("example") + + io.getbyte.should == 101 + io.getbyte.should == 120 + io.getbyte.should == 97 + end +end + +describe "StringIO#getbyte when self is not readable" do + it_behaves_like :stringio_getc_not_readable, :getbyte +end diff --git a/spec/ruby/library/stringio/getc_spec.rb b/spec/ruby/library/stringio/getc_spec.rb new file mode 100644 index 0000000000..263d418316 --- /dev/null +++ b/spec/ruby/library/stringio/getc_spec.rb @@ -0,0 +1,19 @@ +require_relative '../../spec_helper' +require 'stringio' +require_relative 'shared/getc' + +describe "StringIO#getc" do + it_behaves_like :stringio_getc, :getc + + it "returns the character at the current position" do + io = StringIO.new("example") + + io.getc.should == ?e + io.getc.should == ?x + io.getc.should == ?a + end +end + +describe "StringIO#getc when self is not readable" do + it_behaves_like :stringio_getc_not_readable, :getc +end diff --git a/spec/ruby/library/stringio/getch_spec.rb b/spec/ruby/library/stringio/getch_spec.rb new file mode 100644 index 0000000000..113b4971bf --- /dev/null +++ b/spec/ruby/library/stringio/getch_spec.rb @@ -0,0 +1,44 @@ +# -*- encoding: utf-8 -*- +require_relative '../../spec_helper' +require 'stringio' +require_relative 'shared/getc' + +# This method is added by io/console on require. +describe "StringIO#getch" do + require 'io/console' + + it_behaves_like :stringio_getc, :getch + + it "returns the character at the current position" do + io = StringIO.new("example") + + io.getch.should == ?e + io.getch.should == ?x + io.getch.should == ?a + end + + it "increments #pos by the byte size of the character in multibyte strings" do + io = StringIO.new("föóbar") + + io.getch; io.pos.should == 1 # "f" has byte size 1 + io.getch; io.pos.should == 3 # "ö" has byte size 2 + io.getch; io.pos.should == 5 # "ó" has byte size 2 + io.getch; io.pos.should == 6 # "b" has byte size 1 + end + + it "returns nil at the end of the string" do + # empty string case + io = StringIO.new("") + io.getch.should == nil + io.getch.should == nil + + # non-empty string case + io = StringIO.new("a") + io.getch # skip one + io.getch.should == nil + end + + describe "StringIO#getch when self is not readable" do + it_behaves_like :stringio_getc_not_readable, :getch + end +end diff --git a/spec/ruby/library/stringio/getpass_spec.rb b/spec/ruby/library/stringio/getpass_spec.rb new file mode 100644 index 0000000000..60fc64f0c5 --- /dev/null +++ b/spec/ruby/library/stringio/getpass_spec.rb @@ -0,0 +1,11 @@ +require_relative '../../spec_helper' +require 'stringio' + +# This method is added by io/console on require. +describe "StringIO#getpass" do + require 'io/console' + + it "is defined by io/console" do + StringIO.new("example").should.respond_to?(:getpass) + end +end diff --git a/spec/ruby/library/stringio/gets_spec.rb b/spec/ruby/library/stringio/gets_spec.rb new file mode 100644 index 0000000000..97429e6a29 --- /dev/null +++ b/spec/ruby/library/stringio/gets_spec.rb @@ -0,0 +1,246 @@ +require_relative '../../spec_helper' +require "stringio" + +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 + + 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 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} + 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 +end + +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 "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" + 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 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 + + it "does not raise TypeError if passed separator is nil" do + @io.gets(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.gets('>', obj).should == "this>" + 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) + 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" + end +end diff --git a/spec/ruby/library/stringio/initialize_spec.rb b/spec/ruby/library/stringio/initialize_spec.rb new file mode 100644 index 0000000000..1e8096e3bb --- /dev/null +++ b/spec/ruby/library/stringio/initialize_spec.rb @@ -0,0 +1,222 @@ +require_relative '../../spec_helper' +require 'stringio' + +describe "StringIO#initialize when passed [Object, mode]" do + before :each do + @io = StringIO.allocate + end + + it "uses the passed Object as the StringIO backend" do + @io.send(:initialize, str = "example", "r") + @io.string.should equal(str) + end + + it "sets the mode based on the passed mode" do + io = StringIO.allocate + io.send(:initialize, "example", "r") + io.closed_read?.should be_false + io.closed_write?.should be_true + + io = StringIO.allocate + io.send(:initialize, "example", "rb") + io.closed_read?.should be_false + io.closed_write?.should be_true + + io = StringIO.allocate + io.send(:initialize, "example", "r+") + io.closed_read?.should be_false + io.closed_write?.should be_false + + io = StringIO.allocate + io.send(:initialize, "example", "rb+") + io.closed_read?.should be_false + io.closed_write?.should be_false + + io = StringIO.allocate + io.send(:initialize, "example", "w") + io.closed_read?.should be_true + io.closed_write?.should be_false + + io = StringIO.allocate + io.send(:initialize, "example", "wb") + io.closed_read?.should be_true + io.closed_write?.should be_false + + io = StringIO.allocate + io.send(:initialize, "example", "w+") + io.closed_read?.should be_false + io.closed_write?.should be_false + + io = StringIO.allocate + io.send(:initialize, "example", "wb+") + io.closed_read?.should be_false + io.closed_write?.should be_false + + io = StringIO.allocate + io.send(:initialize, "example", "a") + io.closed_read?.should be_true + io.closed_write?.should be_false + + io = StringIO.allocate + io.send(:initialize, "example", "ab") + io.closed_read?.should be_true + io.closed_write?.should be_false + + io = StringIO.allocate + io.send(:initialize, "example", "a+") + io.closed_read?.should be_false + io.closed_write?.should be_false + + io = StringIO.allocate + io.send(:initialize, "example", "ab+") + io.closed_read?.should be_false + io.closed_write?.should be_false + end + + it "allows passing the mode as an Integer" do + io = StringIO.allocate + io.send(:initialize, "example", IO::RDONLY) + io.closed_read?.should be_false + io.closed_write?.should be_true + + io = StringIO.allocate + io.send(:initialize, "example", IO::RDWR) + io.closed_read?.should be_false + io.closed_write?.should be_false + + io = StringIO.allocate + io.send(:initialize, "example", IO::WRONLY) + io.closed_read?.should be_true + io.closed_write?.should be_false + + io = StringIO.allocate + io.send(:initialize, "example", IO::WRONLY | IO::TRUNC) + io.closed_read?.should be_true + io.closed_write?.should be_false + + io = StringIO.allocate + io.send(:initialize, "example", IO::RDWR | IO::TRUNC) + io.closed_read?.should be_false + io.closed_write?.should be_false + + io = StringIO.allocate + io.send(:initialize, "example", IO::WRONLY | IO::APPEND) + io.closed_read?.should be_true + io.closed_write?.should be_false + + io = StringIO.allocate + io.send(:initialize, "example", IO::RDWR | IO::APPEND) + io.closed_read?.should be_false + io.closed_write?.should be_false + end + + it "raises a FrozenError when passed a frozen String in truncate mode as StringIO backend" do + io = StringIO.allocate + -> { io.send(:initialize, "example".freeze, IO::TRUNC) }.should raise_error(FrozenError) + end + + it "tries to convert the passed mode to a String using #to_str" do + obj = mock('to_str') + obj.should_receive(:to_str).and_return("r") + @io.send(:initialize, "example", obj) + + @io.closed_read?.should be_false + @io.closed_write?.should be_true + end + + it "raises an Errno::EACCES error when passed a frozen string with a write-mode" do + (str = "example").freeze + -> { @io.send(:initialize, str, "r+") }.should raise_error(Errno::EACCES) + -> { @io.send(:initialize, str, "w") }.should raise_error(Errno::EACCES) + -> { @io.send(:initialize, str, "a") }.should raise_error(Errno::EACCES) + end +end + +describe "StringIO#initialize when passed [Object]" do + before :each do + @io = StringIO.allocate + end + + it "uses the passed Object as the StringIO backend" do + @io.send(:initialize, str = "example") + @io.string.should equal(str) + end + + it "sets the mode to read-write" do + @io.send(:initialize, "example") + @io.closed_read?.should be_false + @io.closed_write?.should be_false + end + + it "tries to convert the passed Object to a String using #to_str" do + obj = mock('to_str') + obj.should_receive(:to_str).and_return("example") + @io.send(:initialize, obj) + @io.string.should == "example" + end + + it "automatically sets the mode to read-only when passed a frozen string" do + (str = "example").freeze + @io.send(:initialize, str) + @io.closed_read?.should be_false + @io.closed_write?.should be_true + end +end + +describe "StringIO#initialize when passed no arguments" do + before :each do + @io = StringIO.allocate + end + + it "is private" do + StringIO.should have_private_instance_method(:initialize) + end + + it "sets the mode to read-write" do + @io.send(:initialize, "example") + @io.closed_read?.should be_false + @io.closed_write?.should be_false + end + + it "uses an empty String as the StringIO backend" do + @io.send(:initialize) + @io.string.should == "" + end +end + +describe "StringIO#initialize sets" do + before :each do + @external = Encoding.default_external + @internal = Encoding.default_internal + Encoding.default_external = Encoding::ISO_8859_2 + Encoding.default_internal = Encoding::ISO_8859_2 + end + + after :each do + Encoding.default_external = @external + Encoding.default_internal = @internal + end + + it "the encoding to Encoding.default_external when passed no arguments" do + io = StringIO.new + io.external_encoding.should == Encoding::ISO_8859_2 + io.string.encoding.should == Encoding::ISO_8859_2 + end + + it "the encoding to the encoding of the String when passed a String" do + s = ''.force_encoding(Encoding::EUC_JP) + io = StringIO.new(s) + io.string.encoding.should == Encoding::EUC_JP + end + + guard_not -> { # [Bug #16497] + stringio_version = StringIO.const_defined?(:VERSION) ? StringIO::VERSION : "0.0.2" + version_is(stringio_version, "0.0.3"..."0.1.1") + } do + it "the #external_encoding to the encoding of the String when passed a String" do + s = ''.force_encoding(Encoding::EUC_JP) + io = StringIO.new(s) + io.external_encoding.should == Encoding::EUC_JP + end + end +end diff --git a/spec/ruby/library/stringio/inspect_spec.rb b/spec/ruby/library/stringio/inspect_spec.rb new file mode 100644 index 0000000000..7c02f8d360 --- /dev/null +++ b/spec/ruby/library/stringio/inspect_spec.rb @@ -0,0 +1,19 @@ +require_relative '../../spec_helper' +require "stringio" + +describe "StringIO#inspect" do + it "returns the same as #to_s" do + io = StringIO.new("example") + io.inspect.should == io.to_s + end + + it "does not include the contents" do + io = StringIO.new("contents") + io.inspect.should_not include("contents") + end + + it "uses the regular Object#inspect without any instance variable" do + io = StringIO.new("example") + io.inspect.should =~ /\A#<StringIO:0x\h+>\z/ + end +end diff --git a/spec/ruby/library/stringio/internal_encoding_spec.rb b/spec/ruby/library/stringio/internal_encoding_spec.rb new file mode 100644 index 0000000000..2035cf25a9 --- /dev/null +++ b/spec/ruby/library/stringio/internal_encoding_spec.rb @@ -0,0 +1,10 @@ +require 'stringio' +require_relative '../../spec_helper' + +describe "StringIO#internal_encoding" do + it "returns nil" do + io = StringIO.new + io.set_encoding Encoding::UTF_8 + io.internal_encoding.should == nil + end +end diff --git a/spec/ruby/library/stringio/isatty_spec.rb b/spec/ruby/library/stringio/isatty_spec.rb new file mode 100644 index 0000000000..1ef33978b5 --- /dev/null +++ b/spec/ruby/library/stringio/isatty_spec.rb @@ -0,0 +1,7 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' +require_relative 'shared/isatty' + +describe "StringIO#isatty" do + it_behaves_like :stringio_isatty, :isatty +end diff --git a/spec/ruby/library/stringio/length_spec.rb b/spec/ruby/library/stringio/length_spec.rb new file mode 100644 index 0000000000..d3070f50a7 --- /dev/null +++ b/spec/ruby/library/stringio/length_spec.rb @@ -0,0 +1,7 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' +require_relative 'shared/length' + +describe "StringIO#length" do + it_behaves_like :stringio_length, :length +end diff --git a/spec/ruby/library/stringio/lineno_spec.rb b/spec/ruby/library/stringio/lineno_spec.rb new file mode 100644 index 0000000000..c620a1a686 --- /dev/null +++ b/spec/ruby/library/stringio/lineno_spec.rb @@ -0,0 +1,30 @@ +require_relative '../../spec_helper' +require "stringio" + +describe "StringIO#lineno" do + before :each do + @io = StringIO.new("this\nis\nan\nexample") + end + + it "returns the number of lines read" do + @io.gets + @io.gets + @io.gets + @io.lineno.should eql(3) + end +end + +describe "StringIO#lineno=" do + before :each do + @io = StringIO.new("this\nis\nan\nexample") + end + + it "sets the current line number, but has no impact on the position" do + @io.lineno = 3 + @io.pos.should eql(0) + + @io.gets.should == "this\n" + @io.lineno.should eql(4) + @io.pos.should eql(5) + end +end diff --git a/spec/ruby/library/stringio/lines_spec.rb b/spec/ruby/library/stringio/lines_spec.rb new file mode 100644 index 0000000000..42d11772ae --- /dev/null +++ b/spec/ruby/library/stringio/lines_spec.rb @@ -0,0 +1,53 @@ +require_relative '../../spec_helper' +require 'stringio' +require_relative 'shared/each' + +ruby_version_is ''...'3.0' do + describe "StringIO#lines when passed a separator" do + before :each do + @verbose, $VERBOSE = $VERBOSE, nil + end + + after :each do + $VERBOSE = @verbose + end + + it_behaves_like :stringio_each_separator, :lines + end + + describe "StringIO#lines when passed no arguments" do + before :each do + @verbose, $VERBOSE = $VERBOSE, nil + end + + after :each do + $VERBOSE = @verbose + end + + it_behaves_like :stringio_each_no_arguments, :lines + end + + describe "StringIO#lines when self is not readable" do + before :each do + @verbose, $VERBOSE = $VERBOSE, nil + end + + after :each do + $VERBOSE = @verbose + end + + it_behaves_like :stringio_each_not_readable, :lines + end + + describe "StringIO#lines when passed chomp" do + before :each do + @verbose, $VERBOSE = $VERBOSE, nil + end + + after :each do + $VERBOSE = @verbose + end + + it_behaves_like :stringio_each_chomp, :lines + end +end diff --git a/spec/ruby/library/stringio/open_spec.rb b/spec/ruby/library/stringio/open_spec.rb new file mode 100644 index 0000000000..acab6e9056 --- /dev/null +++ b/spec/ruby/library/stringio/open_spec.rb @@ -0,0 +1,207 @@ +require_relative '../../spec_helper' +require 'stringio' + +describe "StringIO.open when passed [Object, mode]" do + it "uses the passed Object as the StringIO backend" do + io = StringIO.open(str = "example", "r") + io.string.should equal(str) + end + + it "returns the blocks return value when yielding" do + ret = StringIO.open("example", "r") { :test } + ret.should equal(:test) + end + + it "yields self to the passed block" do + io = nil + StringIO.open("example", "r") { |strio| io = strio } + io.should be_kind_of(StringIO) + end + + it "closes self after yielding" do + io = nil + StringIO.open("example", "r") { |strio| io = strio } + io.closed?.should be_true + end + + it "even closes self when an exception is raised while yielding" do + io = nil + begin + StringIO.open("example", "r") do |strio| + io = strio + raise "Error" + end + rescue + end + io.closed?.should be_true + end + + it "sets self's string to nil after yielding" do + io = nil + StringIO.open("example", "r") { |strio| io = strio } + io.string.should be_nil + end + + it "even sets self's string to nil when an exception is raised while yielding" do + io = nil + begin + StringIO.open("example", "r") do |strio| + io = strio + raise "Error" + end + rescue + end + io.string.should be_nil + end + + it "sets the mode based on the passed mode" do + io = StringIO.open("example", "r") + io.closed_read?.should be_false + io.closed_write?.should be_true + + io = StringIO.open("example", "rb") + io.closed_read?.should be_false + io.closed_write?.should be_true + + io = StringIO.open("example", "r+") + io.closed_read?.should be_false + io.closed_write?.should be_false + + io = StringIO.open("example", "rb+") + io.closed_read?.should be_false + io.closed_write?.should be_false + + io = StringIO.open("example", "w") + io.closed_read?.should be_true + io.closed_write?.should be_false + + io = StringIO.open("example", "wb") + io.closed_read?.should be_true + io.closed_write?.should be_false + + io = StringIO.open("example", "w+") + io.closed_read?.should be_false + io.closed_write?.should be_false + + io = StringIO.open("example", "wb+") + io.closed_read?.should be_false + io.closed_write?.should be_false + + io = StringIO.open("example", "a") + io.closed_read?.should be_true + io.closed_write?.should be_false + + io = StringIO.open("example", "ab") + io.closed_read?.should be_true + io.closed_write?.should be_false + + io = StringIO.open("example", "a+") + io.closed_read?.should be_false + io.closed_write?.should be_false + + io = StringIO.open("example", "ab+") + io.closed_read?.should be_false + io.closed_write?.should be_false + end + + it "allows passing the mode as an Integer" do + io = StringIO.open("example", IO::RDONLY) + io.closed_read?.should be_false + io.closed_write?.should be_true + + io = StringIO.open("example", IO::RDWR) + io.closed_read?.should be_false + io.closed_write?.should be_false + + io = StringIO.open("example", IO::WRONLY) + io.closed_read?.should be_true + io.closed_write?.should be_false + + io = StringIO.open("example", IO::WRONLY | IO::TRUNC) + io.closed_read?.should be_true + io.closed_write?.should be_false + + io = StringIO.open("example", IO::RDWR | IO::TRUNC) + io.closed_read?.should be_false + io.closed_write?.should be_false + + io = StringIO.open("example", IO::WRONLY | IO::APPEND) + io.closed_read?.should be_true + io.closed_write?.should be_false + + io = StringIO.open("example", IO::RDWR | IO::APPEND) + io.closed_read?.should be_false + io.closed_write?.should be_false + end + + it "raises a FrozenError when passed a frozen String in truncate mode as StringIO backend" do + -> { StringIO.open("example".freeze, IO::TRUNC) }.should raise_error(FrozenError) + end + + it "tries to convert the passed mode to a String using #to_str" do + obj = mock('to_str') + obj.should_receive(:to_str).and_return("r") + io = StringIO.open("example", obj) + + io.closed_read?.should be_false + io.closed_write?.should be_true + end + + it "raises an Errno::EACCES error when passed a frozen string with a write-mode" do + (str = "example").freeze + -> { StringIO.open(str, "r+") }.should raise_error(Errno::EACCES) + -> { StringIO.open(str, "w") }.should raise_error(Errno::EACCES) + -> { StringIO.open(str, "a") }.should raise_error(Errno::EACCES) + end +end + +describe "StringIO.open when passed [Object]" do + it "uses the passed Object as the StringIO backend" do + io = StringIO.open(str = "example") + io.string.should equal(str) + end + + it "yields self to the passed block" do + io = nil + ret = StringIO.open("example") { |strio| io = strio } + io.should equal(ret) + end + + it "sets the mode to read-write" do + io = StringIO.open("example") + io.closed_read?.should be_false + io.closed_write?.should be_false + end + + it "tries to convert the passed Object to a String using #to_str" do + obj = mock('to_str') + obj.should_receive(:to_str).and_return("example") + io = StringIO.open(obj) + io.string.should == "example" + end + + it "automatically sets the mode to read-only when passed a frozen string" do + (str = "example").freeze + io = StringIO.open(str) + io.closed_read?.should be_false + io.closed_write?.should be_true + end +end + +describe "StringIO.open when passed no arguments" do + it "yields self to the passed block" do + io = nil + ret = StringIO.open { |strio| io = strio } + io.should equal(ret) + end + + it "sets the mode to read-write" do + io = StringIO.open + io.closed_read?.should be_false + io.closed_write?.should be_false + end + + it "uses an empty String as the StringIO backend" do + StringIO.open.string.should == "" + end +end diff --git a/spec/ruby/library/stringio/path_spec.rb b/spec/ruby/library/stringio/path_spec.rb new file mode 100644 index 0000000000..1184ca523f --- /dev/null +++ b/spec/ruby/library/stringio/path_spec.rb @@ -0,0 +1,8 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "StringIO#path" do + it "is not defined" do + -> { StringIO.new("path").path }.should raise_error(NoMethodError) + end +end diff --git a/spec/ruby/library/stringio/pid_spec.rb b/spec/ruby/library/stringio/pid_spec.rb new file mode 100644 index 0000000000..08f2d7ab1a --- /dev/null +++ b/spec/ruby/library/stringio/pid_spec.rb @@ -0,0 +1,8 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "StringIO#pid" do + it "returns nil" do + StringIO.new("pid").pid.should be_nil + end +end diff --git a/spec/ruby/library/stringio/pos_spec.rb b/spec/ruby/library/stringio/pos_spec.rb new file mode 100644 index 0000000000..81be5f01a5 --- /dev/null +++ b/spec/ruby/library/stringio/pos_spec.rb @@ -0,0 +1,28 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' +require_relative 'shared/tell' + +describe "StringIO#pos" do + it_behaves_like :stringio_tell, :pos +end + +describe "StringIO#pos=" do + before :each do + @io = StringIOSpecs.build + end + + it "updates the current byte offset" do + @io.pos = 26 + @io.read(1).should == "r" + end + + it "raises an EINVAL if given a negative argument" do + -> { @io.pos = -10 }.should raise_error(Errno::EINVAL) + end + + it "updates the current byte offset after reaching EOF" do + @io.read + @io.pos = 26 + @io.read(1).should == "r" + end +end diff --git a/spec/ruby/library/stringio/print_spec.rb b/spec/ruby/library/stringio/print_spec.rb new file mode 100644 index 0000000000..6ac6430900 --- /dev/null +++ b/spec/ruby/library/stringio/print_spec.rb @@ -0,0 +1,102 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "StringIO#print" do + before :each do + @io = StringIO.new('example') + end + + it "prints $_ when passed no arguments" do + $_ = nil + @io.print + @io.string.should == "example" + + $_ = "blah" + @io.print + @io.string.should == "blahple" + end + + it "prints the passed arguments to self" do + @io.print(5, 6, 7, 8) + @io.string.should == "5678ple" + end + + it "tries to convert the passed Object to a String using #to_s" do + obj = mock("to_s") + obj.should_receive(:to_s).and_return("to_s") + @io.print(obj) + @io.string.should == "to_sple" + end + + it "returns nil" do + @io.print(1, 2, 3).should be_nil + end + + it "pads self with \\000 when the current position is after the end" do + @io.pos = 10 + @io.print(1, 2, 3) + @io.string.should == "example\000\000\000123" + end + + it "honors the output record separator global" do + old_rs = $\ + suppress_warning {$\ = 'x'} + + begin + @io.print(5, 6, 7, 8) + @io.string.should == '5678xle' + ensure + suppress_warning {$\ = old_rs} + end + end + + it "updates the current position" do + @io.print(1, 2, 3) + @io.pos.should eql(3) + + @io.print(1, 2, 3) + @io.pos.should eql(6) + end + + it "correctly updates the current position when honoring the output record separator global" do + old_rs = $\ + suppress_warning {$\ = 'x'} + + begin + @io.print(5, 6, 7, 8) + @io.pos.should eql(5) + ensure + suppress_warning {$\ = old_rs} + end + end +end + +describe "StringIO#print when in append mode" do + before :each do + @io = StringIO.new("example", "a") + end + + it "appends the passed argument to the end of self" do + @io.print(", just testing") + @io.string.should == "example, just testing" + + @io.print(" and more testing") + @io.string.should == "example, just testing and more testing" + end + + it "correctly updates self's position" do + @io.print(", testing") + @io.pos.should eql(16) + end +end + +describe "StringIO#print when self is not writable" do + it "raises an IOError" do + io = StringIO.new("test", "r") + -> { io.print("test") }.should raise_error(IOError) + + io = StringIO.new("test") + io.close_write + -> { io.print("test") }.should raise_error(IOError) + end +end diff --git a/spec/ruby/library/stringio/printf_spec.rb b/spec/ruby/library/stringio/printf_spec.rb new file mode 100644 index 0000000000..9dd1a3b410 --- /dev/null +++ b/spec/ruby/library/stringio/printf_spec.rb @@ -0,0 +1,70 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' +require_relative '../../core/kernel/shared/sprintf' + +describe "StringIO#printf" do + before :each do + @io = StringIO.new('example') + end + + it "returns nil" do + @io.printf("%d %04x", 123, 123).should be_nil + end + + it "pads self with \\000 when the current position is after the end" do + @io.pos = 10 + @io.printf("%d", 123) + @io.string.should == "example\000\000\000123" + end + + it "performs format conversion" do + @io.printf("%d %04x", 123, 123) + @io.string.should == "123 007b" + end + + it "updates the current position" do + @io.printf("%d %04x", 123, 123) + @io.pos.should eql(8) + + @io.printf("%d %04x", 123, 123) + @io.pos.should eql(16) + end + + describe "formatting" do + it_behaves_like :kernel_sprintf, -> format, *args { + io = StringIO.new(+"") + io.printf(format, *args) + io.string + } + end +end + +describe "StringIO#printf when in append mode" do + before :each do + @io = StringIO.new("example", "a") + end + + it "appends the passed argument to the end of self" do + @io.printf("%d %04x", 123, 123) + @io.string.should == "example123 007b" + + @io.printf("%d %04x", 123, 123) + @io.string.should == "example123 007b123 007b" + end + + it "correctly updates self's position" do + @io.printf("%d %04x", 123, 123) + @io.pos.should eql(15) + end +end + +describe "StringIO#printf when self is not writable" do + it "raises an IOError" do + io = StringIO.new("test", "r") + -> { io.printf("test") }.should raise_error(IOError) + + io = StringIO.new("test") + io.close_write + -> { io.printf("test") }.should raise_error(IOError) + end +end diff --git a/spec/ruby/library/stringio/putc_spec.rb b/spec/ruby/library/stringio/putc_spec.rb new file mode 100644 index 0000000000..223b3523e5 --- /dev/null +++ b/spec/ruby/library/stringio/putc_spec.rb @@ -0,0 +1,88 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "StringIO#putc when passed [String]" do + before :each do + @io = StringIO.new('example') + end + + it "overwrites the character at the current position" do + @io.putc("t") + @io.string.should == "txample" + + @io.pos = 3 + @io.putc("t") + @io.string.should == "txatple" + end + + it "only writes the first character from the passed String" do + @io.putc("test") + @io.string.should == "txample" + end + + it "returns the passed String" do + str = "test" + @io.putc(str).should equal(str) + end + + it "correctly updates the current position" do + @io.putc("t") + @io.pos.should == 1 + + @io.putc("test") + @io.pos.should == 2 + + @io.putc("t") + @io.pos.should == 3 + end +end + +describe "StringIO#putc when passed [Object]" do + before :each do + @io = StringIO.new('example') + end + + it "it writes the passed Integer % 256 to self" do + @io.putc(333) # 333 % 256 == ?M + @io.string.should == "Mxample" + + @io.putc(-450) # -450 % 256 == ?> + @io.string.should == "M>ample" + end + + it "pads self with \\000 when the current position is after the end" do + @io.pos = 10 + @io.putc(?A) + @io.string.should == "example\000\000\000A" + end + + it "tries to convert the passed argument to an Integer using #to_int" do + obj = mock('to_int') + obj.should_receive(:to_int).and_return(116) + @io.putc(obj) + @io.string.should == "txample" + end + + it "raises a TypeError when the passed argument can't be coerced to Integer" do + -> { @io.putc(Object.new) }.should raise_error(TypeError) + end +end + +describe "StringIO#putc when in append mode" do + it "appends to the end of self" do + io = StringIO.new("test", "a") + io.putc(?t) + io.string.should == "testt" + end +end + +describe "StringIO#putc when self is not writable" do + it "raises an IOError" do + io = StringIO.new("test", "r") + -> { io.putc(?a) }.should raise_error(IOError) + + io = StringIO.new("test") + io.close_write + -> { io.putc("t") }.should raise_error(IOError) + end +end diff --git a/spec/ruby/library/stringio/puts_spec.rb b/spec/ruby/library/stringio/puts_spec.rb new file mode 100644 index 0000000000..a9f289a5a5 --- /dev/null +++ b/spec/ruby/library/stringio/puts_spec.rb @@ -0,0 +1,170 @@ +# -*- encoding: utf-8 -*- + +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "StringIO#puts when passed an Array" do + before :each do + @io = StringIO.new + end + + it "writes each element of the passed Array to self, separated by a newline" do + @io.puts([1, 2, 3, 4]) + @io.string.should == "1\n2\n3\n4\n" + + @io.puts([1, 2], [3, 4]) + @io.string.should == "1\n2\n3\n4\n1\n2\n3\n4\n" + end + + it "flattens nested Arrays" do + @io.puts([1, [2, [3, [4]]]]) + @io.string.should == "1\n2\n3\n4\n" + end + + it "handles self-recursive arrays correctly" do + (ary = [5]) + ary << ary + @io.puts(ary) + @io.string.should == "5\n[...]\n" + end + + it "does not honor the global output record separator $\\" do + begin + old_rs = $\ + suppress_warning {$\ = "test"} + @io.puts([1, 2, 3, 4]) + @io.string.should == "1\n2\n3\n4\n" + ensure + suppress_warning {$\ = old_rs} + end + end + + it "first tries to convert each Array element to an Array using #to_ary" do + obj = mock("Object") + obj.should_receive(:to_ary).and_return(["to_ary"]) + @io.puts([obj]) + @io.string.should == "to_ary\n" + end + + it "then tries to convert each Array element to a String using #to_s" do + obj = mock("Object") + obj.should_receive(:to_s).and_return("to_s") + @io.puts([obj]) + @io.string.should == "to_s\n" + end + + it "returns general object info if :to_s does not return a string" do + object = mock('hola') + object.should_receive(:to_s).and_return(false) + + @io.puts(object).should == nil + @io.string.should == object.inspect.split(" ")[0] + ">\n" + end +end + +describe "StringIO#puts when passed 1 or more objects" do + before :each do + @io = StringIO.new + end + + it "does not honor the global output record separator $\\" do + begin + old_rs = $\ + suppress_warning {$\ = "test"} + @io.puts(1, 2, 3, 4) + @io.string.should == "1\n2\n3\n4\n" + ensure + suppress_warning {$\ = old_rs} + end + end + + it "does not put a \\n after each Objects that end in a newline" do + @io.puts("1\n", "2\n", "3\n") + @io.string.should == "1\n2\n3\n" + end + + it "first tries to convert each Object to an Array using #to_ary" do + obj = mock("Object") + obj.should_receive(:to_ary).and_return(["to_ary"]) + @io.puts(obj) + @io.string.should == "to_ary\n" + end + + it "then tries to convert each Object to a String using #to_s" do + obj = mock("Object") + obj.should_receive(:to_s).and_return("to_s") + @io.puts(obj) + @io.string.should == "to_s\n" + end + + it "prints a newline when passed an empty string" do + @io.puts '' + @io.string.should == "\n" + end +end + +describe "StringIO#puts when passed no arguments" do + before :each do + @io = StringIO.new + end + + it "returns nil" do + @io.puts.should be_nil + end + + it "prints a newline" do + @io.puts + @io.string.should == "\n" + end + + it "does not honor the global output record separator $\\" do + begin + old_rs = $\ + suppress_warning {$\ = "test"} + @io.puts + @io.string.should == "\n" + ensure + suppress_warning {$\ = old_rs} + end + end +end + +describe "StringIO#puts when in append mode" do + before :each do + @io = StringIO.new("example", "a") + end + + it "appends the passed argument to the end of self" do + @io.puts(", just testing") + @io.string.should == "example, just testing\n" + + @io.puts(" and more testing") + @io.string.should == "example, just testing\n and more testing\n" + end + + it "correctly updates self's position" do + @io.puts(", testing") + @io.pos.should eql(17) + end +end + +describe "StringIO#puts when self is not writable" do + it "raises an IOError" do + io = StringIO.new("test", "r") + -> { io.puts }.should raise_error(IOError) + + io = StringIO.new("test") + io.close_write + -> { io.puts }.should raise_error(IOError) + end +end + +describe "StringIO#puts when passed an encoded string" do + it "stores the bytes unmodified" do + io = StringIO.new("") + io.puts "\x00\x01\x02" + io.puts "æåø" + + io.string.should == "\x00\x01\x02\næåø\n" + end +end diff --git a/spec/ruby/library/stringio/read_nonblock_spec.rb b/spec/ruby/library/stringio/read_nonblock_spec.rb new file mode 100644 index 0000000000..2a8f926bd0 --- /dev/null +++ b/spec/ruby/library/stringio/read_nonblock_spec.rb @@ -0,0 +1,42 @@ +require_relative '../../spec_helper' +require "stringio" +require_relative 'shared/read' +require_relative 'shared/sysread' + +describe "StringIO#read_nonblock when passed length, buffer" do + it_behaves_like :stringio_read, :read_nonblock +end + +describe "StringIO#read_nonblock when passed length" do + it_behaves_like :stringio_read_length, :read_nonblock +end + +describe "StringIO#read_nonblock when passed nil" do + it_behaves_like :stringio_read_nil, :read_nonblock +end + +describe "StringIO#read_nonblock when passed length" do + it_behaves_like :stringio_sysread_length, :read_nonblock +end + +describe "StringIO#read_nonblock" do + + it "accepts an exception option" do + stringio = StringIO.new('foo') + stringio.read_nonblock(3, exception: false).should == 'foo' + end + + context "when exception option is set to false" do + context "when the end is reached" do + it "returns nil" do + stringio = StringIO.new('') + stringio << "hello" + stringio.rewind + + stringio.read_nonblock(5).should == "hello" + stringio.read_nonblock(5, exception: false).should be_nil + end + end + end + +end diff --git a/spec/ruby/library/stringio/read_spec.rb b/spec/ruby/library/stringio/read_spec.rb new file mode 100644 index 0000000000..52ab3dcf47 --- /dev/null +++ b/spec/ruby/library/stringio/read_spec.rb @@ -0,0 +1,62 @@ +require_relative '../../spec_helper' +require "stringio" +require_relative 'shared/read' + +describe "StringIO#read when passed length, buffer" do + it_behaves_like :stringio_read, :read +end + +describe "StringIO#read when passed [length]" do + it_behaves_like :stringio_read_length, :read +end + +describe "StringIO#read when passed no arguments" do + it_behaves_like :stringio_read_no_arguments, :read + + it "returns an empty string if at EOF" do + @io.read.should == "example" + @io.read.should == "" + end +end + +describe "StringIO#read when passed nil" do + it_behaves_like :stringio_read_nil, :read + + it "returns an empty string if at EOF" do + @io.read(nil).should == "example" + @io.read(nil).should == "" + end +end + +describe "StringIO#read when self is not readable" do + it_behaves_like :stringio_read_not_readable, :read +end + +describe "StringIO#read when passed [length]" do + before :each do + @io = StringIO.new("example") + end + + it "returns nil when self's position is at the end" do + @io.pos = 7 + @io.read(10).should be_nil + end + + it "returns an empty String when length is 0" do + @io.read(0).should == "" + end +end + +describe "StringIO#read when passed length and a buffer" do + before :each do + @io = StringIO.new("abcdefghijklmnopqrstuvwxyz") + end + + it "reads [length] characters into the buffer" do + buf = "foo" + result = @io.read(10, buf) + + buf.should == "abcdefghij" + result.should equal(buf) + end +end diff --git a/spec/ruby/library/stringio/readbyte_spec.rb b/spec/ruby/library/stringio/readbyte_spec.rb new file mode 100644 index 0000000000..41a0911293 --- /dev/null +++ b/spec/ruby/library/stringio/readbyte_spec.rb @@ -0,0 +1,20 @@ +require_relative '../../spec_helper' +require 'stringio' +require_relative 'shared/readchar' + +describe "StringIO#readbyte" do + it_behaves_like :stringio_readchar, :readbyte + + it "reads the next 8-bit byte from self's current position" do + io = StringIO.new("example") + + io.readbyte.should == 101 + + io.pos = 4 + io.readbyte.should == 112 + end +end + +describe "StringIO#readbyte when self is not readable" do + it_behaves_like :stringio_readchar_not_readable, :readbyte +end diff --git a/spec/ruby/library/stringio/readchar_spec.rb b/spec/ruby/library/stringio/readchar_spec.rb new file mode 100644 index 0000000000..38944819a2 --- /dev/null +++ b/spec/ruby/library/stringio/readchar_spec.rb @@ -0,0 +1,20 @@ +require_relative '../../spec_helper' +require 'stringio' +require_relative 'shared/readchar' + +describe "StringIO#readchar" do + it_behaves_like :stringio_readchar, :readchar + + it "reads the next 8-bit byte from self's current position" do + io = StringIO.new("example") + + io.readchar.should == ?e + + io.pos = 4 + io.readchar.should == ?p + end +end + +describe "StringIO#readchar when self is not readable" do + it_behaves_like :stringio_readchar_not_readable, :readchar +end diff --git a/spec/ruby/library/stringio/readline_spec.rb b/spec/ruby/library/stringio/readline_spec.rb new file mode 100644 index 0000000000..94b67bc92d --- /dev/null +++ b/spec/ruby/library/stringio/readline_spec.rb @@ -0,0 +1,130 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + + +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" + + begin + old_sep = $/ + suppress_warning {$/ = " "} + @io.readline.should == "an " + @io.readline.should == "example\nfor " + @io.readline.should == "StringIO#readline" + ensure + suppress_warning {$/ = old_sep} + 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 + + it "updates self's position" do + @io.readline + @io.pos.should eql(8) + + @io.readline + @io.pos.should eql(19) + + @io.readline + @io.pos.should eql(40) + end + + it "updates self's lineno" do + @io.readline + @io.lineno.should eql(1) + + @io.readline + @io.lineno.should eql(2) + + @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) + 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 diff --git a/spec/ruby/library/stringio/readlines_spec.rb b/spec/ruby/library/stringio/readlines_spec.rb new file mode 100644 index 0000000000..4b007787e2 --- /dev/null +++ b/spec/ruby/library/stringio/readlines_spec.rb @@ -0,0 +1,100 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "StringIO#readlines when passed [separator]" do + before :each do + @io = StringIO.new("this>is>an>example") + end + + it "returns an Array containing lines based on the passed separator" do + @io.readlines(">").should == ["this>", "is>", "an>", "example"] + end + + it "updates self's position based on the number of read bytes" do + @io.readlines(">") + @io.pos.should eql(18) + end + + it "updates self's lineno based on the number of read lines" do + @io.readlines(">") + @io.lineno.should eql(4) + end + + it "does not change $_" do + $_ = "test" + @io.readlines(">") + $_.should == "test" + end + + it "returns an Array containing all paragraphs when the passed separator is an empty String" do + io = StringIO.new("this is\n\nan example") + io.readlines("").should == ["this is\n\n", "an example"] + end + + it "returns the remaining content as one line starting at the current position when passed nil" do + io = StringIO.new("this is\n\nan example") + io.pos = 5 + io.readlines(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.stub!(:to_str).and_return(">") + @io.readlines(obj).should == ["this>", "is>", "an>", "example"] + end +end + +describe "StringIO#readlines when passed no argument" do + before :each do + @io = StringIO.new("this is\nan example\nfor StringIO#readlines") + end + + it "returns an Array containing lines based on $/" do + begin + old_sep = $/; + suppress_warning {$/ = " "} + @io.readlines.should == ["this ", "is\nan ", "example\nfor ", "StringIO#readlines"] + ensure + suppress_warning {$/ = old_sep} + end + end + + it "updates self's position based on the number of read bytes" do + @io.readlines + @io.pos.should eql(41) + end + + it "updates self's lineno based on the number of read lines" do + @io.readlines + @io.lineno.should eql(3) + end + + it "does not change $_" do + $_ = "test" + @io.readlines(">") + $_.should == "test" + end + + it "returns an empty Array when self is at the end" do + @io.pos = 41 + @io.readlines.should == [] + end +end + +describe "StringIO#readlines when in write-only mode" do + it "raises an IOError" do + io = StringIO.new("xyz", "w") + -> { io.readlines }.should raise_error(IOError) + + io = StringIO.new("xyz") + io.close_read + -> { io.readlines }.should raise_error(IOError) + end +end + +describe "StringIO#readlines when passed [chomp]" do + it "returns the data read without a trailing newline character" do + io = StringIO.new("this>is\nan>example\r\n") + io.readlines(chomp: true).should == ["this>is", "an>example"] + end +end diff --git a/spec/ruby/library/stringio/readpartial_spec.rb b/spec/ruby/library/stringio/readpartial_spec.rb new file mode 100644 index 0000000000..2601fe8c42 --- /dev/null +++ b/spec/ruby/library/stringio/readpartial_spec.rb @@ -0,0 +1,80 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "StringIO#readpartial" do + before :each do + @string = StringIO.new('Stop, look, listen') + end + + after :each 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 + @string.readpartial(1).should == "t" + end + + it "reads after ungetc with data in the buffer" do + c = @string.getc + @string.ungetc(c) + @string.readpartial(4).should == "Stop" + @string.readpartial(3).should == ", l" + end + + it "reads after ungetc without data in the buffer" do + @string = StringIO.new + @string.write("f").should == 1 + @string.rewind + c = @string.getc + c.should == 'f' + @string.ungetc(c).should == nil + + @string.readpartial(2).should == "f" + @string.rewind + # now, also check that the ungot char is cleared and + # not returned again + @string.write("b").should == 1 + @string.rewind + @string.readpartial(2).should == "b" + end + + it "discards the existing buffer content upon successful read" do + buffer = "existing" + @string.readpartial(11, buffer) + buffer.should == "Stop, look," + end + + it "raises EOFError on EOF" do + @string.readpartial(18).should == 'Stop, look, listen' + -> { @string.readpartial(10) }.should raise_error(EOFError) + end + + it "discards the existing buffer content upon error" do + buffer = 'hello' + @string.readpartial(100) + -> { @string.readpartial(1, buffer) }.should raise_error(EOFError) + buffer.should be_empty + end + + it "raises IOError if the stream is closed" do + @string.close + -> { @string.readpartial(1) }.should raise_error(IOError) + end + + it "raises ArgumentError if the negative argument is provided" do + -> { @string.readpartial(-1) }.should raise_error(ArgumentError) + end + + it "immediately returns an empty string if the length argument is 0" do + @string.readpartial(0).should == "" + end +end diff --git a/spec/ruby/library/stringio/reopen_spec.rb b/spec/ruby/library/stringio/reopen_spec.rb new file mode 100644 index 0000000000..6752cf9970 --- /dev/null +++ b/spec/ruby/library/stringio/reopen_spec.rb @@ -0,0 +1,298 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "StringIO#reopen when passed [Object, Integer]" do + before :each do + @io = StringIO.new("example") + end + + it "reopens self with the passed Object in the passed mode" do + @io.reopen("reopened", IO::RDONLY) + @io.closed_read?.should be_false + @io.closed_write?.should be_true + @io.string.should == "reopened" + + @io.reopen("reopened, twice", IO::WRONLY) + @io.closed_read?.should be_true + @io.closed_write?.should be_false + @io.string.should == "reopened, twice" + + @io.reopen("reopened, another time", IO::RDWR) + @io.closed_read?.should be_false + @io.closed_write?.should be_false + @io.string.should == "reopened, another time" + end + + ruby_version_is ""..."3.0" do + # NOTE: WEIRD! + it "does not taint self when the passed Object was tainted" do + @io.reopen("reopened".taint, IO::RDONLY) + @io.tainted?.should be_false + + @io.reopen("reopened".taint, IO::WRONLY) + @io.tainted?.should be_false + end + end + + it "tries to convert the passed Object to a String using #to_str" do + obj = mock("to_str") + obj.should_receive(:to_str).and_return("to_str") + @io.reopen(obj, IO::RDWR) + @io.string.should == "to_str" + end + + it "raises a TypeError when the passed Object can't be converted to a String" do + -> { @io.reopen(Object.new, IO::RDWR) }.should raise_error(TypeError) + end + + it "raises an Errno::EACCES when trying to reopen self with a frozen String in write-mode" do + -> { @io.reopen("burn".freeze, IO::WRONLY) }.should raise_error(Errno::EACCES) + -> { @io.reopen("burn".freeze, IO::WRONLY | IO::APPEND) }.should raise_error(Errno::EACCES) + end + + it "raises a FrozenError when trying to reopen self with a frozen String in truncate-mode" do + -> { @io.reopen("burn".freeze, IO::RDONLY | IO::TRUNC) }.should raise_error(FrozenError) + end + + it "does not raise IOError when passed a frozen String in read-mode" do + @io.reopen("burn".freeze, IO::RDONLY) + @io.string.should == "burn" + end +end + +describe "StringIO#reopen when passed [Object, Object]" do + before :each do + @io = StringIO.new("example") + end + + it "reopens self with the passed Object in the passed mode" do + @io.reopen("reopened", "r") + @io.closed_read?.should be_false + @io.closed_write?.should be_true + @io.string.should == "reopened" + + @io.reopen("reopened, twice", "r+") + @io.closed_read?.should be_false + @io.closed_write?.should be_false + @io.string.should == "reopened, twice" + + @io.reopen("reopened, another", "w+") + @io.closed_read?.should be_false + @io.closed_write?.should be_false + @io.string.should == "" + + @io.reopen("reopened, another time", "r+") + @io.closed_read?.should be_false + @io.closed_write?.should be_false + @io.string.should == "reopened, another time" + end + + it "truncates the passed String when opened in truncate mode" do + @io.reopen(str = "reopened", "w") + str.should == "" + end + + ruby_version_is ""..."3.0" do + # NOTE: WEIRD! + it "does not taint self when the passed Object was tainted" do + @io.reopen("reopened".taint, "r") + @io.tainted?.should be_false + + @io.reopen("reopened".taint, "w") + @io.tainted?.should be_false + end + end + + it "tries to convert the passed Object to a String using #to_str" do + obj = mock("to_str") + obj.should_receive(:to_str).and_return("to_str") + @io.reopen(obj, "r") + @io.string.should == "to_str" + end + + it "raises a TypeError when the passed Object can't be converted to a String using #to_str" do + -> { @io.reopen(Object.new, "r") }.should raise_error(TypeError) + end + + it "resets self's position to 0" do + @io.read(5) + @io.reopen("reopened") + @io.pos.should eql(0) + end + + it "resets self's line number to 0" do + @io.gets + @io.reopen("reopened") + @io.lineno.should eql(0) + end + + it "tries to convert the passed mode Object to an Integer using #to_str" do + obj = mock("to_str") + obj.should_receive(:to_str).and_return("r") + @io.reopen("reopened", obj) + @io.closed_read?.should be_false + @io.closed_write?.should be_true + @io.string.should == "reopened" + end + + it "raises an Errno::EACCES error when trying to reopen self with a frozen String in write-mode" do + -> { @io.reopen("burn".freeze, 'w') }.should raise_error(Errno::EACCES) + -> { @io.reopen("burn".freeze, 'w+') }.should raise_error(Errno::EACCES) + -> { @io.reopen("burn".freeze, 'a') }.should raise_error(Errno::EACCES) + -> { @io.reopen("burn".freeze, "r+") }.should raise_error(Errno::EACCES) + end + + it "does not raise IOError if a frozen string is passed in read mode" do + @io.reopen("burn".freeze, "r") + @io.string.should == "burn" + end +end + +describe "StringIO#reopen when passed [String]" do + before :each do + @io = StringIO.new("example") + end + + it "reopens self with the passed String in read-write mode" do + @io.close + + @io.reopen("reopened") + + @io.closed_write?.should be_false + @io.closed_read?.should be_false + + @io.string.should == "reopened" + end + + ruby_version_is ""..."3.0" do + # NOTE: WEIRD! + it "does not taint self when the passed Object was tainted" do + @io.reopen("reopened".taint) + @io.tainted?.should be_false + end + end + + it "resets self's position to 0" do + @io.read(5) + @io.reopen("reopened") + @io.pos.should eql(0) + end + + it "resets self's line number to 0" do + @io.gets + @io.reopen("reopened") + @io.lineno.should eql(0) + end +end + +describe "StringIO#reopen when passed [Object]" do + before :each do + @io = StringIO.new("example") + end + + it "raises a TypeError when passed an Object that can't be converted to a StringIO" do + -> { @io.reopen(Object.new) }.should raise_error(TypeError) + end + + it "does not try to convert the passed Object to a String using #to_str" do + obj = mock("not to_str") + obj.should_not_receive(:to_str) + -> { @io.reopen(obj) }.should raise_error(TypeError) + end + + it "tries to convert the passed Object to a StringIO using #to_strio" do + obj = mock("to_strio") + obj.should_receive(:to_strio).and_return(StringIO.new("to_strio")) + @io.reopen(obj) + @io.string.should == "to_strio" + end + + # NOTE: WEIRD! + ruby_version_is ""..."2.7" do + it "taints self when the passed Object was tainted" do + @io.reopen(StringIO.new("reopened").taint) + @io.tainted?.should be_true + end + end +end + +describe "StringIO#reopen when passed no arguments" do + before :each do + @io = StringIO.new("example\nsecond line") + end + + it "resets self's mode to read-write" do + @io.close + @io.reopen + @io.closed_read?.should be_false + @io.closed_write?.should be_false + end + + it "resets self's position to 0" do + @io.read(5) + @io.reopen + @io.pos.should eql(0) + end + + it "resets self's line number to 0" do + @io.gets + @io.reopen + @io.lineno.should eql(0) + end +end + +# NOTE: Some reopen specs disabled due to MRI bugs. See: +# http://rubyforge.org/tracker/index.php?func=detail&aid=13919&group_id=426&atid=1698 +# for details. +describe "StringIO#reopen" do + before :each do + @io = StringIO.new('hello','a') + end + + # TODO: find out if this is really a bug + it "reopens a stream when given a String argument" do + @io.reopen('goodbye').should == @io + @io.string.should == 'goodbye' + @io << 'x' + @io.string.should == 'xoodbye' + end + + it "reopens a stream in append mode when flagged as such" do + @io.reopen('goodbye', 'a').should == @io + @io.string.should == 'goodbye' + @io << 'x' + @io.string.should == 'goodbyex' + end + + it "reopens and truncate when reopened in write mode" do + @io.reopen('goodbye', 'wb').should == @io + @io.string.should == '' + @io << 'x' + @io.string.should == 'x' + end + + it "truncates the given string, not a copy" do + str = 'goodbye' + @io.reopen(str, 'w') + @io.string.should == '' + str.should == '' + end + + ruby_version_is ""..."2.7" do + it "taints self if the provided StringIO argument is tainted" do + new_io = StringIO.new("tainted") + new_io.taint + @io.reopen(new_io) + @io.should.tainted? + end + end + + it "does not truncate the content even when the StringIO argument is in the truncate mode" do + orig_io = StringIO.new("Original StringIO", IO::RDWR|IO::TRUNC) + orig_io.write("BLAH") # make sure the content is not empty + + @io.reopen(orig_io) + @io.string.should == "BLAH" + end + +end diff --git a/spec/ruby/library/stringio/rewind_spec.rb b/spec/ruby/library/stringio/rewind_spec.rb new file mode 100644 index 0000000000..5f885ecf81 --- /dev/null +++ b/spec/ruby/library/stringio/rewind_spec.rb @@ -0,0 +1,24 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "StringIO#rewind" do + before :each do + @io = StringIO.new("hello\nworld") + @io.pos = 3 + @io.lineno = 1 + end + + it "returns 0" do + @io.rewind.should eql(0) + end + + it "resets the position" do + @io.rewind + @io.pos.should == 0 + end + + it "resets the line number" do + @io.rewind + @io.lineno.should == 0 + end +end diff --git a/spec/ruby/library/stringio/seek_spec.rb b/spec/ruby/library/stringio/seek_spec.rb new file mode 100644 index 0000000000..253b5027a9 --- /dev/null +++ b/spec/ruby/library/stringio/seek_spec.rb @@ -0,0 +1,67 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "StringIO#seek" do + before :each do + @io = StringIO.new("12345678") + end + + it "seeks from the current position when whence is IO::SEEK_CUR" do + @io.pos = 1 + @io.seek(1, IO::SEEK_CUR) + @io.pos.should eql(2) + + @io.seek(-1, IO::SEEK_CUR) + @io.pos.should eql(1) + end + + it "seeks from the end of self when whence is IO::SEEK_END" do + @io.seek(3, IO::SEEK_END) + @io.pos.should eql(11) # Outside of the StringIO's content + + @io.seek(-2, IO::SEEK_END) + @io.pos.should eql(6) + end + + it "seeks to an absolute position when whence is IO::SEEK_SET" do + @io.seek(5, IO::SEEK_SET) + @io.pos.should == 5 + + @io.pos = 3 + @io.seek(5, IO::SEEK_SET) + @io.pos.should == 5 + end + + it "raises an Errno::EINVAL error on negative amounts when whence is IO::SEEK_SET" do + -> { @io.seek(-5, IO::SEEK_SET) }.should raise_error(Errno::EINVAL) + end + + it "raises an Errno::EINVAL error on incorrect whence argument" do + -> { @io.seek(0, 3) }.should raise_error(Errno::EINVAL) + -> { @io.seek(0, -1) }.should raise_error(Errno::EINVAL) + -> { @io.seek(0, 2**16) }.should raise_error(Errno::EINVAL) + -> { @io.seek(0, -2**16) }.should raise_error(Errno::EINVAL) + end + + it "tries to convert the passed Object to a String using #to_int" do + obj = mock("to_int") + obj.should_receive(:to_int).and_return(2) + @io.seek(obj) + @io.pos.should eql(2) + end + + it "raises a TypeError when the passed Object can't be converted to an Integer" do + -> { @io.seek(Object.new) }.should raise_error(TypeError) + end +end + +describe "StringIO#seek when self is closed" do + before :each do + @io = StringIO.new("example") + @io.close + end + + it "raises an IOError" do + -> { @io.seek(5) }.should raise_error(IOError) + end +end diff --git a/spec/ruby/library/stringio/set_encoding_spec.rb b/spec/ruby/library/stringio/set_encoding_spec.rb new file mode 100644 index 0000000000..21d45750f3 --- /dev/null +++ b/spec/ruby/library/stringio/set_encoding_spec.rb @@ -0,0 +1,20 @@ +require 'stringio' +require_relative '../../spec_helper' + +describe "StringIO#set_encoding" do + it "sets the encoding of the underlying String if the String is not frozen" do + str = "".encode(Encoding::US_ASCII) + + io = StringIO.new(str) + io.set_encoding Encoding::UTF_8 + io.string.encoding.should == Encoding::UTF_8 + end + + it "does not set the encoding of the underlying String if the String is frozen" do + str = "".encode(Encoding::US_ASCII).freeze + + io = StringIO.new(str) + io.set_encoding Encoding::UTF_8 + io.string.encoding.should == Encoding::US_ASCII + end +end diff --git a/spec/ruby/library/stringio/shared/codepoints.rb b/spec/ruby/library/stringio/shared/codepoints.rb new file mode 100644 index 0000000000..9d84aa4919 --- /dev/null +++ b/spec/ruby/library/stringio/shared/codepoints.rb @@ -0,0 +1,45 @@ +# -*- encoding: utf-8 -*- +describe :stringio_codepoints, shared: true do + before :each do + @io = StringIO.new("∂φ/∂x = gaîté") + @enum = @io.send(@method) + end + + it "returns an Enumerator" do + @enum.should be_an_instance_of(Enumerator) + end + + it "yields each codepoint code in turn" do + @enum.to_a.should == [8706, 966, 47, 8706, 120, 32, 61, 32, 103, 97, 238, 116, 233] + end + + it "yields each codepoint starting from the current position" do + @io.pos = 15 + @enum.to_a.should == [238, 116, 233] + end + + it "raises an error if reading invalid sequence" do + @io.pos = 1 # inside of a multibyte sequence + -> { @enum.first }.should raise_error(ArgumentError) + end + + it "raises an IOError if not readable" do + @io.close_read + -> { @enum.to_a }.should raise_error(IOError) + + io = StringIO.new("xyz", "w") + -> { io.send(@method).to_a }.should raise_error(IOError) + end + + + it "calls the given block" do + r = [] + @io.send(@method){|c| r << c } + r.should == [8706, 966, 47, 8706, 120, 32, 61, 32, 103, 97, 238, 116, 233] + end + + it "returns self" do + @io.send(@method) {|l| l }.should equal(@io) + end + +end diff --git a/spec/ruby/library/stringio/shared/each.rb b/spec/ruby/library/stringio/shared/each.rb new file mode 100644 index 0000000000..14b0a013b3 --- /dev/null +++ b/spec/ruby/library/stringio/shared/each.rb @@ -0,0 +1,115 @@ +describe :stringio_each_separator, shared: true do + before :each do + @io = StringIO.new("a b c d e\n1 2 3 4 5") + end + + it "uses the passed argument as the line separator" do + seen = [] + @io.send(@method, " ") {|s| seen << s} + seen.should == ["a ", "b ", "c ", "d ", "e\n1 ", "2 ", "3 ", "4 ", "5"] + end + + it "does not change $_" do + $_ = "test" + @io.send(@method, " ") { |s| s} + $_.should == "test" + end + + it "returns self" do + @io.send(@method) {|l| l }.should equal(@io) + end + + it "tries to convert the passed separator to a String using #to_str" do + obj = mock("to_str") + obj.stub!(:to_str).and_return(" ") + + seen = [] + @io.send(@method, obj) { |l| seen << l } + seen.should == ["a ", "b ", "c ", "d ", "e\n1 ", "2 ", "3 ", "4 ", "5"] + end + + it "yields self's content starting from the current position when the passed separator is nil" do + seen = [] + io = StringIO.new("1 2 1 2 1 2") + io.pos = 2 + io.send(@method, nil) {|s| seen << s} + seen.should == ["2 1 2 1 2"] + end + + it "yields each paragraph 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 + +describe :stringio_each_no_arguments, shared: true do + before :each do + @io = StringIO.new("a b c d e\n1 2 3 4 5") + end + + it "yields each line to the passed block" do + seen = [] + @io.send(@method) {|s| seen << s } + seen.should == ["a b c d e\n", "1 2 3 4 5"] + end + + it "yields each line starting from the current position" do + seen = [] + @io.pos = 4 + @io.send(@method) {|s| seen << s } + seen.should == ["c d e\n", "1 2 3 4 5"] + end + + it "does not change $_" do + $_ = "test" + @io.send(@method) { |s| s} + $_.should == "test" + end + + it "uses $/ as the default line separator" do + seen = [] + begin + old_rs = $/ + suppress_warning {$/ = " "} + @io.send(@method) {|s| seen << s } + seen.should eql(["a ", "b ", "c ", "d ", "e\n1 ", "2 ", "3 ", "4 ", "5"]) + ensure + suppress_warning {$/ = old_rs} + end + end + + it "returns self" do + @io.send(@method) {|l| l }.should equal(@io) + end + + it "returns an Enumerator when passed no block" do + enum = @io.send(@method) + enum.instance_of?(Enumerator).should be_true + + seen = [] + enum.each { |b| seen << b } + seen.should == ["a b c d e\n", "1 2 3 4 5"] + end +end + +describe :stringio_each_not_readable, shared: true do + it "raises an IOError" do + io = StringIO.new("a b c d e", "w") + -> { io.send(@method) { |b| b } }.should raise_error(IOError) + + io = StringIO.new("a b c d e") + io.close_read + -> { io.send(@method) { |b| b } }.should raise_error(IOError) + end +end + +describe :stringio_each_chomp, shared: true do + it "yields each line with removed newline characters to the passed block" do + seen = [] + io = StringIO.new("a b \rc d e\n1 2 3 4 5\r\nthe end") + io.send(@method, chomp: true) {|s| seen << s } + seen.should == ["a b \rc d e", "1 2 3 4 5", "the end"] + end +end diff --git a/spec/ruby/library/stringio/shared/each_byte.rb b/spec/ruby/library/stringio/shared/each_byte.rb new file mode 100644 index 0000000000..56734ff99d --- /dev/null +++ b/spec/ruby/library/stringio/shared/each_byte.rb @@ -0,0 +1,48 @@ +describe :stringio_each_byte, shared: true do + before :each do + @io = StringIO.new("xyz") + end + + it "yields each character code in turn" do + seen = [] + @io.send(@method) { |b| seen << b } + seen.should == [120, 121, 122] + end + + it "updates the position before each yield" do + seen = [] + @io.send(@method) { |b| seen << @io.pos } + seen.should == [1, 2, 3] + end + + it "does not yield if the current position is out of bounds" do + @io.pos = 1000 + seen = nil + @io.send(@method) { |b| seen = b } + seen.should be_nil + end + + it "returns self" do + @io.send(@method) {}.should equal(@io) + end + + it "returns an Enumerator when passed no block" do + enum = @io.send(@method) + enum.instance_of?(Enumerator).should be_true + + seen = [] + enum.each { |b| seen << b } + seen.should == [120, 121, 122] + end +end + +describe :stringio_each_byte_not_readable, shared: true do + it "raises an IOError" do + io = StringIO.new("xyz", "w") + -> { io.send(@method) { |b| b } }.should raise_error(IOError) + + io = StringIO.new("xyz") + io.close_read + -> { io.send(@method) { |b| b } }.should raise_error(IOError) + end +end diff --git a/spec/ruby/library/stringio/shared/each_char.rb b/spec/ruby/library/stringio/shared/each_char.rb new file mode 100644 index 0000000000..bcdac53282 --- /dev/null +++ b/spec/ruby/library/stringio/shared/each_char.rb @@ -0,0 +1,36 @@ +# -*- encoding: utf-8 -*- +describe :stringio_each_char, shared: true do + before :each do + @io = StringIO.new("xyz äöü") + end + + it "yields each character code in turn" do + seen = [] + @io.send(@method) { |c| seen << c } + seen.should == ["x", "y", "z", " ", "ä", "ö", "ü"] + end + + it "returns self" do + @io.send(@method) {}.should equal(@io) + end + + it "returns an Enumerator when passed no block" do + enum = @io.send(@method) + enum.instance_of?(Enumerator).should be_true + + seen = [] + enum.each { |c| seen << c } + seen.should == ["x", "y", "z", " ", "ä", "ö", "ü"] + end +end + +describe :stringio_each_char_not_readable, shared: true do + it "raises an IOError" do + io = StringIO.new("xyz", "w") + -> { io.send(@method) { |b| b } }.should raise_error(IOError) + + io = StringIO.new("xyz") + io.close_read + -> { io.send(@method) { |b| b } }.should raise_error(IOError) + end +end diff --git a/spec/ruby/library/stringio/shared/eof.rb b/spec/ruby/library/stringio/shared/eof.rb new file mode 100644 index 0000000000..e0368a2892 --- /dev/null +++ b/spec/ruby/library/stringio/shared/eof.rb @@ -0,0 +1,24 @@ +describe :stringio_eof, shared: true do + before :each do + @io = StringIO.new("eof") + end + + it "returns true when self's position is greater than or equal to self's size" do + @io.pos = 3 + @io.send(@method).should be_true + + @io.pos = 6 + @io.send(@method).should be_true + end + + it "returns false when self's position is less than self's size" do + @io.pos = 0 + @io.send(@method).should be_false + + @io.pos = 1 + @io.send(@method).should be_false + + @io.pos = 2 + @io.send(@method).should be_false + end +end diff --git a/spec/ruby/library/stringio/shared/getc.rb b/spec/ruby/library/stringio/shared/getc.rb new file mode 100644 index 0000000000..6318bcc30f --- /dev/null +++ b/spec/ruby/library/stringio/shared/getc.rb @@ -0,0 +1,43 @@ +describe :stringio_getc, shared: true do + before :each do + @io = StringIO.new("example") + end + + it "increases self's position by one" do + @io.send(@method) + @io.pos.should eql(1) + + @io.send(@method) + @io.pos.should eql(2) + + @io.send(@method) + @io.pos.should eql(3) + end + + it "returns nil when called at the end of self" do + @io.pos = 7 + @io.send(@method).should be_nil + @io.send(@method).should be_nil + @io.send(@method).should be_nil + end + + it "does not increase self's position when called at the end of file" do + @io.pos = 7 + @io.send(@method) + @io.pos.should eql(7) + + @io.send(@method) + @io.pos.should eql(7) + end +end + +describe :stringio_getc_not_readable, shared: true 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 diff --git a/spec/ruby/library/stringio/shared/isatty.rb b/spec/ruby/library/stringio/shared/isatty.rb new file mode 100644 index 0000000000..3da5999953 --- /dev/null +++ b/spec/ruby/library/stringio/shared/isatty.rb @@ -0,0 +1,5 @@ +describe :stringio_isatty, shared: true do + it "returns false" do + StringIO.new('tty').send(@method).should be_false + end +end diff --git a/spec/ruby/library/stringio/shared/length.rb b/spec/ruby/library/stringio/shared/length.rb new file mode 100644 index 0000000000..60a4eb1bdd --- /dev/null +++ b/spec/ruby/library/stringio/shared/length.rb @@ -0,0 +1,5 @@ +describe :stringio_length, shared: true do + it "returns the length of the wrapped string" do + StringIO.new("example").send(@method).should == 7 + end +end diff --git a/spec/ruby/library/stringio/shared/read.rb b/spec/ruby/library/stringio/shared/read.rb new file mode 100644 index 0000000000..c60677bba7 --- /dev/null +++ b/spec/ruby/library/stringio/shared/read.rb @@ -0,0 +1,121 @@ +describe :stringio_read, shared: true do + before :each do + @io = StringIO.new("example") + end + + it "returns the passed buffer String" do + # Note: Rubinius bug: + # @io.send(@method, 7, buffer = "").should equal(buffer) + ret = @io.send(@method, 7, buffer = "") + ret.should equal(buffer) + end + + it "reads length bytes and writes them to the buffer String" do + @io.send(@method, 7, buffer = "") + buffer.should == "example" + end + + it "tries to convert the passed buffer Object to a String using #to_str" do + obj = mock("to_str") + obj.should_receive(:to_str).and_return(buffer = "") + + @io.send(@method, 7, obj) + buffer.should == "example" + end + + it "raises a TypeError when the passed buffer Object can't be converted to a String" do + -> { @io.send(@method, 7, Object.new) }.should raise_error(TypeError) + end + + it "raises a FrozenError error when passed a frozen String as buffer" do + -> { @io.send(@method, 7, "".freeze) }.should raise_error(FrozenError) + end +end + +describe :stringio_read_length, shared: true do + before :each do + @io = StringIO.new("example") + end + + it "reads length bytes from the current position and returns them" do + @io.pos = 3 + @io.send(@method, 4).should == "mple" + end + + it "reads at most the whole content" do + @io.send(@method, 999).should == "example" + end + + it "correctly updates the position" do + @io.send(@method, 3) + @io.pos.should eql(3) + + @io.send(@method, 999) + @io.pos.should eql(7) + end + + it "tries to convert the passed length to an Integer using #to_int" do + obj = mock("to_int") + obj.should_receive(:to_int).and_return(7) + @io.send(@method, obj).should == "example" + end + + it "raises a TypeError when the passed length can't be converted to an Integer" do + -> { @io.send(@method, Object.new) }.should raise_error(TypeError) + end + + it "raises a TypeError when the passed length is negative" do + -> { @io.send(@method, -2) }.should raise_error(ArgumentError) + end + + it "returns a binary String" do + @io.send(@method, 4).encoding.should == Encoding::BINARY + end +end + +describe :stringio_read_no_arguments, shared: true do + before :each do + @io = StringIO.new("example") + end + + it "reads the whole content starting from the current position" do + @io.send(@method).should == "example" + + @io.pos = 3 + @io.send(@method).should == "mple" + end + + it "correctly updates the current position" do + @io.send(@method) + @io.pos.should eql(7) + end +end + +describe :stringio_read_nil, shared: true do + before :each do + @io = StringIO.new("example") + end + + it "returns the remaining content from the current position" do + @io.send(@method, nil).should == "example" + + @io.pos = 4 + @io.send(@method, nil).should == "ple" + end + + it "updates the current position" do + @io.send(@method, nil) + @io.pos.should eql(7) + end +end + +describe :stringio_read_not_readable, shared: true do + it "raises an IOError" do + io = StringIO.new("test", "w") + -> { io.send(@method) }.should raise_error(IOError) + + io = StringIO.new("test") + io.close_read + -> { io.send(@method) }.should raise_error(IOError) + end +end diff --git a/spec/ruby/library/stringio/shared/readchar.rb b/spec/ruby/library/stringio/shared/readchar.rb new file mode 100644 index 0000000000..4248e75420 --- /dev/null +++ b/spec/ruby/library/stringio/shared/readchar.rb @@ -0,0 +1,29 @@ +describe :stringio_readchar, shared: true do + before :each do + @io = StringIO.new("example") + end + + it "correctly updates the current position" do + @io.send(@method) + @io.pos.should == 1 + + @io.send(@method) + @io.pos.should == 2 + end + + it "raises an EOFError when self is at the end" do + @io.pos = 7 + -> { @io.send(@method) }.should raise_error(EOFError) + end +end + +describe :stringio_readchar_not_readable, shared: true do + it "raises an IOError" do + io = StringIO.new("a b c d e", "w") + -> { io.send(@method) }.should raise_error(IOError) + + io = StringIO.new("a b c d e") + io.close_read + -> { io.send(@method) }.should raise_error(IOError) + end +end diff --git a/spec/ruby/library/stringio/shared/sysread.rb b/spec/ruby/library/stringio/shared/sysread.rb new file mode 100644 index 0000000000..3376bd9907 --- /dev/null +++ b/spec/ruby/library/stringio/shared/sysread.rb @@ -0,0 +1,15 @@ +describe :stringio_sysread_length, :shared => true do + before :each do + @io = StringIO.new("example") + end + + it "returns an empty String when passed 0 and no data remains" do + @io.send(@method, 8).should == "example" + @io.send(@method, 0).should == "" + end + + it "raises an EOFError when passed length > 0 and no data remains" do + @io.read.should == "example" + -> { @io.sysread(1) }.should raise_error(EOFError) + end +end diff --git a/spec/ruby/library/stringio/shared/tell.rb b/spec/ruby/library/stringio/shared/tell.rb new file mode 100644 index 0000000000..852c51c192 --- /dev/null +++ b/spec/ruby/library/stringio/shared/tell.rb @@ -0,0 +1,12 @@ +describe :stringio_tell, shared: true do + before :each do + @io = StringIOSpecs.build + end + + it "returns the current byte offset" do + @io.getc + @io.send(@method).should == 1 + @io.read(7) + @io.send(@method).should == 8 + end +end diff --git a/spec/ruby/library/stringio/shared/write.rb b/spec/ruby/library/stringio/shared/write.rb new file mode 100644 index 0000000000..080729217b --- /dev/null +++ b/spec/ruby/library/stringio/shared/write.rb @@ -0,0 +1,91 @@ +describe :stringio_write, shared: true do + before :each do + @io = StringIO.new('12345') + end + + it "tries to convert the passed Object to a String using #to_s" do + obj = mock("to_s") + obj.should_receive(:to_s).and_return("to_s") + @io.send(@method, obj) + @io.string.should == "to_s5" + end +end + +describe :stringio_write_string, shared: true do + before :each do + @io = StringIO.new('12345') + end + + # TODO: RDoc says that #write appends at the current position. + it "writes the passed String at the current buffer position" do + @io.pos = 2 + @io.send(@method, 'x').should == 1 + @io.string.should == '12x45' + @io.send(@method, 7).should == 1 + @io.string.should == '12x75' + end + + it "pads self with \\000 when the current position is after the end" do + @io.pos = 8 + @io.send(@method, 'x') + @io.string.should == "12345\000\000\000x" + @io.send(@method, 9) + @io.string.should == "12345\000\000\000x9" + end + + it "returns the number of bytes written" do + @io.send(@method, '').should == 0 + @io.send(@method, nil).should == 0 + str = "1" * 100 + @io.send(@method, str).should == 100 + end + + it "updates self's position" do + @io.send(@method, 'test') + @io.pos.should eql(4) + end + + ruby_version_is ""..."2.7" do + it "taints self's String when the passed argument is tainted" do + @io.send(@method, "test".taint) + @io.string.tainted?.should be_true + end + end + + ruby_version_is ""..."3.0" do + it "does not taint self when the passed argument is tainted" do + @io.send(@method, "test".taint) + @io.tainted?.should be_false + end + end +end + +describe :stringio_write_not_writable, shared: true do + it "raises an IOError" do + io = StringIO.new("test", "r") + -> { io.send(@method, "test") }.should raise_error(IOError) + + io = StringIO.new("test") + io.close_write + -> { io.send(@method, "test") }.should raise_error(IOError) + end +end + +describe :stringio_write_append, shared: true do + before :each do + @io = StringIO.new("example", "a") + end + + it "appends the passed argument to the end of self" do + @io.send(@method, ", just testing") + @io.string.should == "example, just testing" + + @io.send(@method, " and more testing") + @io.string.should == "example, just testing and more testing" + end + + it "correctly updates self's position" do + @io.send(@method, ", testing") + @io.pos.should eql(16) + end +end diff --git a/spec/ruby/library/stringio/size_spec.rb b/spec/ruby/library/stringio/size_spec.rb new file mode 100644 index 0000000000..f674d22db9 --- /dev/null +++ b/spec/ruby/library/stringio/size_spec.rb @@ -0,0 +1,7 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' +require_relative 'shared/length' + +describe "StringIO#size" do + it_behaves_like :stringio_length, :size +end diff --git a/spec/ruby/library/stringio/string_spec.rb b/spec/ruby/library/stringio/string_spec.rb new file mode 100644 index 0000000000..1ed5233ba6 --- /dev/null +++ b/spec/ruby/library/stringio/string_spec.rb @@ -0,0 +1,50 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "StringIO#string" do + it "returns the underlying string" do + io = StringIO.new(str = "hello") + io.string.should equal(str) + end +end + +describe "StringIO#string=" do + before :each do + @io = StringIO.new("example\nstring") + end + + it "returns the passed String" do + str = "test" + (@io.string = str).should equal(str) + end + + it "changes the underlying string" do + str = "hello" + @io.string = str + @io.string.should equal(str) + end + + it "resets the position" do + @io.pos = 1 + @io.string = "other" + @io.pos.should eql(0) + end + + it "resets the line number" do + @io.lineno = 1 + @io.string = "other" + @io.lineno.should eql(0) + end + + it "tries to convert the passed Object to a String using #to_str" do + obj = mock("to_str") + obj.should_receive(:to_str).and_return("to_str") + + @io.string = obj + @io.string.should == "to_str" + end + + it "raises a TypeError when the passed Object can't be converted to an Integer" do + -> { @io.seek(Object.new) }.should raise_error(TypeError) + end +end diff --git a/spec/ruby/library/stringio/stringio_spec.rb b/spec/ruby/library/stringio/stringio_spec.rb new file mode 100644 index 0000000000..5ef42b3390 --- /dev/null +++ b/spec/ruby/library/stringio/stringio_spec.rb @@ -0,0 +1,8 @@ +require_relative '../../spec_helper' +require "stringio" + +describe "StringIO" do + it "includes the Enumerable module" do + StringIO.should include(Enumerable) + end +end diff --git a/spec/ruby/library/stringio/sync_spec.rb b/spec/ruby/library/stringio/sync_spec.rb new file mode 100644 index 0000000000..e717a5697b --- /dev/null +++ b/spec/ruby/library/stringio/sync_spec.rb @@ -0,0 +1,19 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "StringIO#sync" do + it "returns true" do + StringIO.new('').sync.should be_true + end +end + +describe "StringIO#sync=" do + before :each do + @io = StringIO.new('') + end + + it "does not change 'sync' status" do + @io.sync = false + @io.sync.should be_true + end +end diff --git a/spec/ruby/library/stringio/sysread_spec.rb b/spec/ruby/library/stringio/sysread_spec.rb new file mode 100644 index 0000000000..8f78073f42 --- /dev/null +++ b/spec/ruby/library/stringio/sysread_spec.rb @@ -0,0 +1,48 @@ +require_relative '../../spec_helper' +require "stringio" +require_relative 'shared/read' + +describe "StringIO#sysread when passed length, buffer" do + it_behaves_like :stringio_read, :sysread +end + +describe "StringIO#sysread when passed [length]" do + it_behaves_like :stringio_read_length, :sysread +end + +describe "StringIO#sysread when passed no arguments" do + it_behaves_like :stringio_read_no_arguments, :sysread + + it "returns an empty String if at EOF" do + @io.sysread.should == "example" + @io.sysread.should == "" + end +end + +describe "StringIO#sysread when self is not readable" do + it_behaves_like :stringio_read_not_readable, :sysread +end + +describe "StringIO#sysread when passed nil" do + it_behaves_like :stringio_read_nil, :sysread + + it "returns an empty String if at EOF" do + @io.sysread(nil).should == "example" + @io.sysread(nil).should == "" + end +end + +describe "StringIO#sysread when passed [length]" do + before :each do + @io = StringIO.new("example") + end + + it "raises an EOFError when self's position is at the end" do + @io.pos = 7 + -> { @io.sysread(10) }.should raise_error(EOFError) + end + + it "returns an empty String when length is 0" do + @io.sysread(0).should == "" + end +end diff --git a/spec/ruby/library/stringio/syswrite_spec.rb b/spec/ruby/library/stringio/syswrite_spec.rb new file mode 100644 index 0000000000..c4891e669b --- /dev/null +++ b/spec/ruby/library/stringio/syswrite_spec.rb @@ -0,0 +1,19 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' +require_relative 'shared/write' + +describe "StringIO#syswrite when passed [Object]" do + it_behaves_like :stringio_write, :syswrite +end + +describe "StringIO#syswrite when passed [String]" do + it_behaves_like :stringio_write_string, :syswrite +end + +describe "StringIO#syswrite when self is not writable" do + it_behaves_like :stringio_write_not_writable, :syswrite +end + +describe "StringIO#syswrite when in append mode" do + it_behaves_like :stringio_write_append, :syswrite +end diff --git a/spec/ruby/library/stringio/tell_spec.rb b/spec/ruby/library/stringio/tell_spec.rb new file mode 100644 index 0000000000..8350ee6f4d --- /dev/null +++ b/spec/ruby/library/stringio/tell_spec.rb @@ -0,0 +1,7 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' +require_relative 'shared/tell' + +describe "StringIO#tell" do + it_behaves_like :stringio_tell, :tell +end diff --git a/spec/ruby/library/stringio/truncate_spec.rb b/spec/ruby/library/stringio/truncate_spec.rb new file mode 100644 index 0000000000..ba910ee98c --- /dev/null +++ b/spec/ruby/library/stringio/truncate_spec.rb @@ -0,0 +1,70 @@ +require_relative '../../spec_helper' +require "stringio" + +describe "StringIO#truncate when passed [length]" do + before :each do + @io = StringIO.new('123456789') + end + + # TODO: Report to Ruby-Core: The RDoc says it always returns 0 + it "returns the passed length" do + @io.truncate(4).should eql(4) + @io.truncate(10).should eql(10) + end + + it "truncated the underlying string down to the passed length" do + @io.truncate(4) + @io.string.should == "1234" + end + + it "does not create a copy of the underlying string" do + io = StringIO.new(str = "123456789") + io.truncate(4) + io.string.should equal(str) + end + + it "does not change the position" do + @io.pos = 7 + @io.truncate(4) + @io.pos.should eql(7) + end + + it "can grow a string to a larger size, padding it with \\000" do + @io.truncate(12) + @io.string.should == "123456789\000\000\000" + end + + it "raises an Errno::EINVAL when the passed length is negative" do + -> { @io.truncate(-1) }.should raise_error(Errno::EINVAL) + -> { @io.truncate(-10) }.should raise_error(Errno::EINVAL) + end + + it "tries to convert the passed length to an Integer using #to_int" do + obj = mock("to_int") + obj.should_receive(:to_int).and_return(4) + + @io.truncate(obj) + @io.string.should == "1234" + end + + it "returns the passed length Object, NOT the result of #to_int" do + obj = mock("to_int") + obj.should_receive(:to_int).and_return(4) + @io.truncate(obj).should equal(obj) + end + + it "raises a TypeError when the passed length can't be converted to an Integer" do + -> { @io.truncate(Object.new) }.should raise_error(TypeError) + end +end + +describe "StringIO#truncate when self is not writable" do + it "raises an IOError" do + io = StringIO.new("test", "r") + -> { io.truncate(2) }.should raise_error(IOError) + + io = StringIO.new("test") + io.close_write + -> { io.truncate(2) }.should raise_error(IOError) + end +end diff --git a/spec/ruby/library/stringio/tty_spec.rb b/spec/ruby/library/stringio/tty_spec.rb new file mode 100644 index 0000000000..c6293dcbd7 --- /dev/null +++ b/spec/ruby/library/stringio/tty_spec.rb @@ -0,0 +1,7 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' +require_relative 'shared/isatty' + +describe "StringIO#tty?" do + it_behaves_like :stringio_isatty, :tty? +end diff --git a/spec/ruby/library/stringio/ungetbyte_spec.rb b/spec/ruby/library/stringio/ungetbyte_spec.rb new file mode 100644 index 0000000000..87b27b837e --- /dev/null +++ b/spec/ruby/library/stringio/ungetbyte_spec.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: false +require_relative '../../spec_helper' +require 'stringio' + +describe "StringIO#ungetbyte" do + it "ungets a single byte from a string starting with a single byte character" do + str = 'This is a simple string.' + io = StringIO.new("#{str}") + c = io.getc + c.should == 'T' + io.ungetbyte(83) + io.string.should == 'Shis is a simple string.' + end + + it "ungets a single byte from a string in the middle of a multibyte character" do + str = "\u01a9" + io = StringIO.new(str) + b = io.getbyte + b.should == 0xc6 # First byte of UTF-8 encoding of \u01a9 + io.ungetbyte(0xce) # First byte of UTF-8 encoding of \u03a9 + io.string.should == "\u03a9" + end + + it "constrains the value of a numeric argument to a single byte" do + str = 'This is a simple string.' + io = StringIO.new("#{str}") + c = io.getc + c.should == 'T' + io.ungetbyte(83 | 0xff00) + io.string.should == 'Shis is a simple string.' + end + + it "ungets the bytes of a string if given a string as an argument" do + str = "\u01a9" + io = StringIO.new(str) + b = io.getbyte + b.should == 0xc6 # First byte of UTF-8 encoding of \u01a9 + io.ungetbyte("\u01a9") + io.string.bytes.should == [198, 169, 169] + end + +end diff --git a/spec/ruby/library/stringio/ungetc_spec.rb b/spec/ruby/library/stringio/ungetc_spec.rb new file mode 100644 index 0000000000..91ef2100a1 --- /dev/null +++ b/spec/ruby/library/stringio/ungetc_spec.rb @@ -0,0 +1,72 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "StringIO#ungetc when passed [char]" do + before :each do + @io = StringIO.new('1234') + end + + it "writes the passed char before the current position" do + @io.pos = 1 + @io.ungetc(?A) + @io.string.should == 'A234' + end + + it "returns nil" do + @io.pos = 1 + @io.ungetc(?A).should be_nil + end + + it "decreases the current position by one" do + @io.pos = 2 + @io.ungetc(?A) + @io.pos.should eql(1) + end + + it "pads with \\000 when the current position is after the end" do + @io.pos = 15 + @io.ungetc(?A) + @io.string.should == "1234\000\000\000\000\000\000\000\000\000\000A" + end + + it "tries to convert the passed argument to an String using #to_str" do + obj = mock("to_str") + obj.should_receive(:to_str).and_return(?A) + + @io.pos = 1 + @io.ungetc(obj) + @io.string.should == "A234" + end + + it "raises a TypeError when the passed length can't be converted to an Integer or String" do + -> { @io.ungetc(Object.new) }.should raise_error(TypeError) + end +end + +describe "StringIO#ungetc when self is not readable" do + it "raises an IOError" do + io = StringIO.new("test", "w") + io.pos = 1 + -> { io.ungetc(?A) }.should raise_error(IOError) + + io = StringIO.new("test") + io.pos = 1 + io.close_read + -> { io.ungetc(?A) }.should raise_error(IOError) + end +end + +# Note: This is incorrect. +# +# describe "StringIO#ungetc when self is not writable" do +# it "raises an IOError" do +# io = StringIO.new("test", "r") +# io.pos = 1 +# lambda { io.ungetc(?A) }.should raise_error(IOError) +# +# io = StringIO.new("test") +# io.pos = 1 +# io.close_write +# lambda { io.ungetc(?A) }.should raise_error(IOError) +# end +# end diff --git a/spec/ruby/library/stringio/write_nonblock_spec.rb b/spec/ruby/library/stringio/write_nonblock_spec.rb new file mode 100644 index 0000000000..4f4c5039fe --- /dev/null +++ b/spec/ruby/library/stringio/write_nonblock_spec.rb @@ -0,0 +1,19 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' +require_relative 'shared/write' + +describe "StringIO#write_nonblock when passed [Object]" do + it_behaves_like :stringio_write, :write_nonblock +end + +describe "StringIO#write_nonblock when passed [String]" do + it_behaves_like :stringio_write_string, :write_nonblock +end + +describe "StringIO#write_nonblock when self is not writable" do + it_behaves_like :stringio_write_not_writable, :write_nonblock +end + +describe "StringIO#write_nonblock when in append mode" do + it_behaves_like :stringio_write_append, :write_nonblock +end diff --git a/spec/ruby/library/stringio/write_spec.rb b/spec/ruby/library/stringio/write_spec.rb new file mode 100644 index 0000000000..3f755c32b4 --- /dev/null +++ b/spec/ruby/library/stringio/write_spec.rb @@ -0,0 +1,19 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' +require_relative 'shared/write' + +describe "StringIO#write when passed [Object]" do + it_behaves_like :stringio_write, :write +end + +describe "StringIO#write when passed [String]" do + it_behaves_like :stringio_write_string, :write +end + +describe "StringIO#write when self is not writable" do + it_behaves_like :stringio_write_not_writable, :write +end + +describe "StringIO#write when in append mode" do + it_behaves_like :stringio_write_append, :write +end |
