aboutsummaryrefslogtreecommitdiffstats
path: root/spec/rubyspec/core/io/shared
diff options
context:
space:
mode:
Diffstat (limited to 'spec/rubyspec/core/io/shared')
-rw-r--r--spec/rubyspec/core/io/shared/binwrite.rb78
-rw-r--r--spec/rubyspec/core/io/shared/chars.rb73
-rw-r--r--spec/rubyspec/core/io/shared/codepoints.rb54
-rw-r--r--spec/rubyspec/core/io/shared/each.rb135
-rw-r--r--spec/rubyspec/core/io/shared/gets_ascii.rb19
-rw-r--r--spec/rubyspec/core/io/shared/new.rb378
-rw-r--r--spec/rubyspec/core/io/shared/pos.rb72
-rw-r--r--spec/rubyspec/core/io/shared/readlines.rb204
-rw-r--r--spec/rubyspec/core/io/shared/tty.rb25
-rw-r--r--spec/rubyspec/core/io/shared/write.rb72
10 files changed, 1110 insertions, 0 deletions
diff --git a/spec/rubyspec/core/io/shared/binwrite.rb b/spec/rubyspec/core/io/shared/binwrite.rb
new file mode 100644
index 0000000000..67f0fd5c86
--- /dev/null
+++ b/spec/rubyspec/core/io/shared/binwrite.rb
@@ -0,0 +1,78 @@
+require File.expand_path('../../fixtures/classes', __FILE__)
+
+describe :io_binwrite, shared: true do
+ before :each do
+ @filename = tmp("IO_binwrite_file") + $$.to_s
+ File.open(@filename, "w") do |file|
+ file << "012345678901234567890123456789"
+ end
+ end
+
+ after :each do
+ rm_r @filename
+ end
+
+ it "coerces the argument to a string using to_s" do
+ (obj = mock('test')).should_receive(:to_s).and_return('a string')
+ IO.send(@method, @filename, obj)
+ end
+
+ it "returns the number of bytes written" do
+ IO.send(@method, @filename, "abcde").should == 5
+ end
+
+ it "creates a file if missing" do
+ fn = @filename + "xxx"
+ begin
+ File.exist?(fn).should be_false
+ IO.send(@method, fn, "test")
+ File.exist?(fn).should be_true
+ ensure
+ rm_r fn
+ end
+ end
+
+ it "creates file if missing even if offset given" do
+ fn = @filename + "xxx"
+ begin
+ File.exist?(fn).should be_false
+ IO.send(@method, fn, "test", 0)
+ File.exist?(fn).should be_true
+ ensure
+ rm_r fn
+ end
+ end
+
+ it "truncates the file and writes the given string" do
+ IO.send(@method, @filename, "hello, world!")
+ File.read(@filename).should == "hello, world!"
+ end
+
+ it "doesn't truncate the file and writes the given string if an offset is given" do
+ IO.send(@method, @filename, "hello, world!", 0)
+ File.read(@filename).should == "hello, world!34567890123456789"
+ IO.send(@method, @filename, "hello, world!", 20)
+ File.read(@filename).should == "hello, world!3456789hello, world!"
+ end
+
+ it "doesn't truncate and writes at the given offset after passing empty opts" do
+ IO.send(@method, @filename, "hello world!", 1, {})
+ File.read(@filename).should == "0hello world!34567890123456789"
+ end
+
+ it "accepts a :mode option" do
+ IO.send(@method, @filename, "hello, world!", mode: 'a')
+ File.read(@filename).should == "012345678901234567890123456789hello, world!"
+ IO.send(@method, @filename, "foo", 2, mode: 'w')
+ File.read(@filename).should == "\0\0foo"
+ end
+
+ it "raises an error if readonly mode is specified" do
+ lambda { IO.send(@method, @filename, "abcde", mode: "r") }.should raise_error(IOError)
+ end
+
+ it "truncates if empty :opts provided and offset skipped" do
+ IO.send(@method, @filename, "hello, world!", {})
+ File.read(@filename).should == "hello, world!"
+ end
+end
diff --git a/spec/rubyspec/core/io/shared/chars.rb b/spec/rubyspec/core/io/shared/chars.rb
new file mode 100644
index 0000000000..7f2edd2b6d
--- /dev/null
+++ b/spec/rubyspec/core/io/shared/chars.rb
@@ -0,0 +1,73 @@
+# -*- encoding: utf-8 -*-
+describe :io_chars, shared: true do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ ScratchPad.record []
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ end
+
+ it "yields each character" do
+ @io.readline.should == "Voici la ligne une.\n"
+
+ count = 0
+ @io.send(@method) do |c|
+ ScratchPad << c
+ break if 4 < count += 1
+ end
+
+ ScratchPad.recorded.should == ["Q", "u", "i", " ", "รจ"]
+ end
+
+ describe "when no block is given" do
+ it "returns an Enumerator" do
+ enum = @io.send(@method)
+ enum.should be_an_instance_of(Enumerator)
+ enum.first(5).should == ["V", "o", "i", "c", "i"]
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return nil" do
+ @io.send(@method).size.should == nil
+ end
+ end
+ end
+ end
+
+ it "returns itself" do
+ @io.send(@method) { |c| }.should equal(@io)
+ end
+
+ it "returns an enumerator for a closed stream" do
+ IOSpecs.closed_io.send(@method).should be_an_instance_of(Enumerator)
+ end
+
+ it "raises an IOError when an enumerator created on a closed stream is accessed" do
+ lambda { IOSpecs.closed_io.send(@method).first }.should raise_error(IOError)
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.send(@method) {} }.should raise_error(IOError)
+ end
+end
+
+describe :io_chars_empty, shared: true do
+ before :each do
+ @name = tmp("io_each_char")
+ @io = new_io @name, "w+:utf-8"
+ ScratchPad.record []
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ rm_r @name
+ end
+
+ it "does not yield any characters on an empty stream" do
+ @io.send(@method) { |c| ScratchPad << c }
+ ScratchPad.recorded.should == []
+ end
+end
diff --git a/spec/rubyspec/core/io/shared/codepoints.rb b/spec/rubyspec/core/io/shared/codepoints.rb
new file mode 100644
index 0000000000..3bb3dce939
--- /dev/null
+++ b/spec/rubyspec/core/io/shared/codepoints.rb
@@ -0,0 +1,54 @@
+# -*- encoding: utf-8 -*-
+require File.expand_path('../../fixtures/classes', __FILE__)
+
+describe :io_codepoints, shared: true do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ @enum = @io.send(@method)
+ end
+
+ after :each do
+ @io.close
+ end
+
+ describe "when no block is given" do
+ it "returns an Enumerator" do
+ @enum.should be_an_instance_of(Enumerator)
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return nil" do
+ @enum.size.should == nil
+ end
+ end
+ end
+ end
+
+ it "yields each codepoint" do
+ @enum.first(25).should == [
+ 86, 111, 105, 99, 105, 32, 108, 97, 32, 108, 105, 103, 110,
+ 101, 32, 117, 110, 101, 46, 10, 81, 117, 105, 32, 232
+ ]
+ end
+
+ it "yields each codepoint starting from the current position" do
+ @io.pos = 130
+ @enum.to_a.should == [101, 32, 115, 105, 120, 46, 10]
+ end
+
+ it "raises an error if reading invalid sequence" do
+ @io.pos = 60 # inside of a multibyte sequence
+ lambda { @enum.first }.should raise_error(ArgumentError)
+ end
+
+ it "does not change $_" do
+ $_ = "test"
+ @enum.to_a
+ $_.should == "test"
+ end
+
+ it "raises an IOError when self is not readable" do
+ lambda { IOSpecs.closed_io.send(@method).to_a }.should raise_error(IOError)
+ end
+end
diff --git a/spec/rubyspec/core/io/shared/each.rb b/spec/rubyspec/core/io/shared/each.rb
new file mode 100644
index 0000000000..dc07434ecd
--- /dev/null
+++ b/spec/rubyspec/core/io/shared/each.rb
@@ -0,0 +1,135 @@
+# -*- encoding: utf-8 -*-
+require File.expand_path('../../fixtures/classes', __FILE__)
+
+describe :io_each, shared: true do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ ScratchPad.record []
+ end
+
+ after :each do
+ @io.close if @io
+ end
+
+ describe "with no separator" do
+ it "yields each line to the passed block" do
+ @io.send(@method) { |s| ScratchPad << s }
+ ScratchPad.recorded.should == IOSpecs.lines
+ end
+
+ it "yields each line starting from the current position" do
+ @io.pos = 41
+ @io.send(@method) { |s| ScratchPad << s }
+ ScratchPad.recorded.should == IOSpecs.lines[2..-1]
+ end
+
+ it "returns self" do
+ @io.send(@method) { |l| l }.should equal(@io)
+ 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 "raises an IOError when self is not readable" do
+ lambda { IOSpecs.closed_io.send(@method) {} }.should raise_error(IOError)
+ end
+
+ it "makes line count accessible via lineno" do
+ @io.send(@method) { ScratchPad << @io.lineno }
+ ScratchPad.recorded.should == [ 1,2,3,4,5,6,7,8,9 ]
+ end
+
+ it "makes line count accessible via $." do
+ @io.send(@method) { ScratchPad << $. }
+ ScratchPad.recorded.should == [ 1,2,3,4,5,6,7,8,9 ]
+ end
+
+ describe "when no block is given" do
+ it "returns an Enumerator" do
+ enum = @io.send(@method)
+ enum.should be_an_instance_of(Enumerator)
+
+ enum.each { |l| ScratchPad << l }
+ ScratchPad.recorded.should == IOSpecs.lines
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return nil" do
+ @io.send(@method).size.should == nil
+ end
+ end
+ end
+ end
+ end
+
+ describe "with limit" do
+ describe "when limit is 0" do
+ it "raises an ArgumentError" do
+ # must pass block so Enumerator is evaluated and raises
+ lambda { @io.send(@method, 0){} }.should raise_error(ArgumentError)
+ end
+ end
+ end
+
+ describe "when passed a String containing one space as a separator" do
+ it "uses the passed argument as the line separator" do
+ @io.send(@method, " ") { |s| ScratchPad << s }
+ ScratchPad.recorded.should == IOSpecs.lines_space_separator
+ end
+
+ it "does not change $_" do
+ $_ = "test"
+ @io.send(@method, " ") { |s| }
+ $_.should == "test"
+ 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.send(@method, obj) { |l| ScratchPad << l }
+ ScratchPad.recorded.should == IOSpecs.lines_space_separator
+ end
+ end
+
+ describe "when passed nil as a separator" do
+ it "yields self's content starting from the current position when the passed separator is nil" do
+ @io.pos = 100
+ @io.send(@method, nil) { |s| ScratchPad << s }
+ ScratchPad.recorded.should == ["qui a linha cinco.\nHere is line six.\n"]
+ end
+ end
+
+ describe "when passed an empty String as a separator" do
+ it "yields each paragraph" do
+ @io.send(@method, "") { |s| ScratchPad << s }
+ ScratchPad.recorded.should == IOSpecs.paragraphs
+ end
+ end
+end
+
+describe :io_each_default_separator, shared: true do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ ScratchPad.record []
+ @sep, $/ = $/, " "
+ end
+
+ after :each do
+ @io.close if @io
+ $/ = @sep
+ end
+
+ it "uses $/ as the default line separator" do
+ @io.send(@method) { |s| ScratchPad << s }
+ ScratchPad.recorded.should == IOSpecs.lines_space_separator
+ end
+end
diff --git a/spec/rubyspec/core/io/shared/gets_ascii.rb b/spec/rubyspec/core/io/shared/gets_ascii.rb
new file mode 100644
index 0000000000..2a8fe3c9a5
--- /dev/null
+++ b/spec/rubyspec/core/io/shared/gets_ascii.rb
@@ -0,0 +1,19 @@
+# -*- encoding: binary -*-
+describe :io_gets_ascii, shared: true do
+ describe "with ASCII separator" do
+ before :each do
+ @name = tmp("gets_specs.txt")
+ touch(@name, "wb") { |f| f.print "this is a test\xFFtesty\ntestier" }
+
+ File.open(@name, "rb") { |f| @data = f.send(@method, "\xFF") }
+ end
+
+ after :each do
+ rm_r @name
+ end
+
+ it "returns the separator's character representation" do
+ @data.should == "this is a test\xFF"
+ end
+ end
+end
diff --git a/spec/rubyspec/core/io/shared/new.rb b/spec/rubyspec/core/io/shared/new.rb
new file mode 100644
index 0000000000..12f889f646
--- /dev/null
+++ b/spec/rubyspec/core/io/shared/new.rb
@@ -0,0 +1,378 @@
+require File.expand_path('../../fixtures/classes', __FILE__)
+
+# This group of specs may ONLY contain specs that do successfully create
+# an IO instance from the file descriptor returned by #new_fd helper.
+describe :io_new, shared: true do
+ before :each do
+ @name = tmp("io_new.txt")
+ @fd = new_fd @name
+ @io = nil
+ end
+
+ after :each do
+ if @io
+ @io.close
+ elsif @fd
+ IO.new(@fd, "w").close
+ end
+ rm_r @name
+ end
+
+ it "creates an IO instance from a Fixnum argument" do
+ @io = IO.send(@method, @fd, "w")
+ @io.should be_an_instance_of(IO)
+ end
+
+ it "creates an IO instance when STDOUT is closed" do
+ verbose, $VERBOSE = $VERBOSE, nil
+ stdout = STDOUT
+ stdout_file = tmp("stdout.txt")
+
+ begin
+ @io = IO.send(@method, @fd, "w")
+ @io.should be_an_instance_of(IO)
+ ensure
+ STDOUT = stdout
+ $VERBOSE = verbose
+ rm_r stdout_file
+ end
+ end
+
+ it "creates an IO instance when STDERR is closed" do
+ verbose, $VERBOSE = $VERBOSE, nil
+ stderr = STDERR
+ stderr_file = tmp("stderr.txt")
+ STDERR = new_io stderr_file
+ STDERR.close
+
+ begin
+ @io = IO.send(@method, @fd, "w")
+ @io.should be_an_instance_of(IO)
+ ensure
+ STDERR = stderr
+ $VERBOSE = verbose
+ rm_r stderr_file
+ end
+ end
+
+ it "calls #to_int on an object to convert to a Fixnum" do
+ obj = mock("file descriptor")
+ obj.should_receive(:to_int).and_return(@fd)
+ @io = IO.send(@method, obj, "w")
+ @io.should be_an_instance_of(IO)
+ end
+
+ it "accepts a :mode option" do
+ @io = IO.send(@method, @fd, mode: "w")
+ @io.write("foo").should == 3
+ end
+
+ it "accepts a mode argument set to nil with a valid :mode option" do
+ @io = IO.send(@method, @fd, nil, mode: "w")
+ @io.write("foo").should == 3
+ end
+
+ it "accepts a mode argument with a :mode option set to nil" do
+ @io = IO.send(@method, @fd, "w", mode: nil)
+ @io.write("foo").should == 3
+ end
+
+ it "uses the external encoding specified in the mode argument" do
+ @io = IO.send(@method, @fd, 'w:utf-8')
+ @io.external_encoding.to_s.should == 'UTF-8'
+ end
+
+ it "uses the external and the internal encoding specified in the mode argument" do
+ @io = IO.send(@method, @fd, 'w:utf-8:ISO-8859-1')
+ @io.external_encoding.to_s.should == 'UTF-8'
+ @io.internal_encoding.to_s.should == 'ISO-8859-1'
+ end
+
+ it "uses the external encoding specified via the :external_encoding option" do
+ @io = IO.send(@method, @fd, 'w', {external_encoding: 'utf-8'})
+ @io.external_encoding.to_s.should == 'UTF-8'
+ end
+
+ it "uses the internal encoding specified via the :internal_encoding option" do
+ @io = IO.send(@method, @fd, 'w', {internal_encoding: 'ibm866'})
+ @io.internal_encoding.to_s.should == 'IBM866'
+ end
+
+ it "uses the colon-separated encodings specified via the :encoding option" do
+ @io = IO.send(@method, @fd, 'w', {encoding: 'utf-8:ISO-8859-1'})
+ @io.external_encoding.to_s.should == 'UTF-8'
+ @io.internal_encoding.to_s.should == 'ISO-8859-1'
+ end
+
+ it "uses the :encoding option as the external encoding when only one is given" do
+ @io = IO.send(@method, @fd, 'w', {encoding: 'ISO-8859-1'})
+ @io.external_encoding.to_s.should == 'ISO-8859-1'
+ end
+
+ it "uses the :encoding options as the external encoding when it's an Encoding object" do
+ @io = IO.send(@method, @fd, 'w', {encoding: Encoding::ISO_8859_1})
+ @io.external_encoding.should == Encoding::ISO_8859_1
+ end
+
+ it "ignores the :encoding option when the :external_encoding option is present" do
+ lambda {
+ @io = IO.send(@method, @fd, 'w', {external_encoding: 'utf-8', encoding: 'iso-8859-1:iso-8859-1'})
+ }.should complain(/Ignoring encoding parameter/)
+ @io.external_encoding.to_s.should == 'UTF-8'
+ end
+
+ it "ignores the :encoding option when the :internal_encoding option is present" do
+ lambda {
+ @io = IO.send(@method, @fd, 'w', {internal_encoding: 'ibm866', encoding: 'iso-8859-1:iso-8859-1'})
+ }.should complain(/Ignoring encoding parameter/)
+ @io.internal_encoding.to_s.should == 'IBM866'
+ end
+
+ it "uses the encoding specified via the :mode option hash" do
+ @io = IO.send(@method, @fd, {mode: 'w:utf-8:ISO-8859-1'})
+ @io.external_encoding.to_s.should == 'UTF-8'
+ @io.internal_encoding.to_s.should == 'ISO-8859-1'
+ end
+
+ it "ignores the :internal_encoding option when the same as the external encoding" do
+ @io = IO.send(@method, @fd, 'w', {external_encoding: 'utf-8', internal_encoding: 'utf-8'})
+ @io.external_encoding.to_s.should == 'UTF-8'
+ @io.internal_encoding.to_s.should == ''
+ end
+
+ it "sets internal encoding to nil when passed '-'" do
+ @io = IO.send(@method, @fd, 'w', {external_encoding: 'utf-8', internal_encoding: '-'})
+ @io.external_encoding.to_s.should == 'UTF-8'
+ @io.internal_encoding.to_s.should == ''
+ end
+
+ it "sets binmode from mode string" do
+ @io = IO.send(@method, @fd, 'wb')
+ @io.binmode?.should == true
+ end
+
+ it "does not set binmode without being asked" do
+ @io = IO.send(@method, @fd, 'w')
+ @io.binmode?.should == false
+ end
+
+ it "sets binmode from :binmode option" do
+ @io = IO.send(@method, @fd, 'w', {binmode: true})
+ @io.binmode?.should == true
+ end
+
+ it "does not set binmode from false :binmode" do
+ @io = IO.send(@method, @fd, 'w', {binmode: false})
+ @io.binmode?.should == false
+ end
+
+ it "sets external encoding to binary with binmode in mode string" do
+ @io = IO.send(@method, @fd, 'wb')
+ @io.external_encoding.to_s.should == 'ASCII-8BIT'
+ end
+
+ # #5917
+ it "sets external encoding to binary with :binmode option" do
+ @io = IO.send(@method, @fd, 'w', {binmode: true})
+ @io.external_encoding.to_s.should == 'ASCII-8BIT'
+ end
+
+ it "does not use binary encoding when mode encoding is specified" do
+ @io = IO.send(@method, @fd, 'wb:iso-8859-1')
+ @io.external_encoding.to_s.should == 'ISO-8859-1'
+ end
+
+ it "does not use binary encoding when :encoding option is specified" do
+ @io = IO.send(@method, @fd, 'wb', encoding: "iso-8859-1")
+ @io.external_encoding.to_s.should == 'ISO-8859-1'
+ end
+
+ it "does not use binary encoding when :external_encoding option is specified" do
+ @io = IO.send(@method, @fd, 'wb', external_encoding: "iso-8859-1")
+ @io.external_encoding.to_s.should == 'ISO-8859-1'
+ end
+
+ it "does not use binary encoding when :internal_encoding option is specified" do
+ @io = IO.send(@method, @fd, 'wb', internal_encoding: "ibm866")
+ @io.internal_encoding.to_s.should == 'IBM866'
+ end
+
+ it "accepts nil options" do
+ @io = IO.send(@method, @fd, 'w', nil)
+ @io.write("foo").should == 3
+ end
+
+ it "coerces mode with #to_str" do
+ mode = mock("mode")
+ mode.should_receive(:to_str).and_return('w')
+ @io = IO.send(@method, @fd, mode)
+ end
+
+ it "coerces mode with #to_int" do
+ mode = mock("mode")
+ mode.should_receive(:to_int).and_return(File::WRONLY)
+ @io = IO.send(@method, @fd, mode)
+ end
+
+ it "coerces mode with #to_str when passed in options" do
+ mode = mock("mode")
+ mode.should_receive(:to_str).and_return('w')
+ @io = IO.send(@method, @fd, mode: mode)
+ end
+
+ it "coerces mode with #to_int when passed in options" do
+ mode = mock("mode")
+ mode.should_receive(:to_int).and_return(File::WRONLY)
+ @io = IO.send(@method, @fd, mode: mode)
+ end
+
+ it "coerces :encoding option with #to_str" do
+ encoding = mock("encoding")
+ encoding.should_receive(:to_str).and_return('utf-8')
+ @io = IO.send(@method, @fd, 'w', encoding: encoding)
+ end
+
+ it "coerces :external_encoding option with #to_str" do
+ encoding = mock("encoding")
+ encoding.should_receive(:to_str).and_return('utf-8')
+ @io = IO.send(@method, @fd, 'w', external_encoding: encoding)
+ end
+
+ it "coerces :internal_encoding option with #to_str" do
+ encoding = mock("encoding")
+ encoding.should_receive(:to_str).at_least(:once).and_return('utf-8')
+ @io = IO.send(@method, @fd, 'w', internal_encoding: encoding)
+ end
+
+ it "coerces options as third argument with #to_hash" do
+ options = mock("options")
+ options.should_receive(:to_hash).and_return({})
+ @io = IO.send(@method, @fd, 'w', options)
+ end
+
+ it "coerces options as second argument with #to_hash" do
+ options = mock("options")
+ options.should_receive(:to_hash).and_return({})
+ @io = IO.send(@method, @fd, options)
+ end
+
+ it "accepts an :autoclose option" do
+ @io = IO.send(@method, @fd, 'w', autoclose: false)
+ @io.autoclose?.should == false
+ @io.autoclose = true
+ end
+
+ it "accepts any truthy option :autoclose" do
+ @io = IO.send(@method, @fd, 'w', autoclose: 42)
+ @io.autoclose?.should == true
+ end
+end
+
+# This group of specs may ONLY contain specs that do not actually create
+# an IO instance from the file descriptor returned by #new_fd helper.
+describe :io_new_errors, shared: true do
+ before :each do
+ @name = tmp("io_new.txt")
+ @fd = new_fd @name
+ end
+
+ after :each do
+ IO.new(@fd, "w").close if @fd
+ rm_r @name
+ end
+
+ it "raises an Errno::EBADF if the file descriptor is not valid" do
+ lambda { IO.send(@method, -1, "w") }.should raise_error(Errno::EBADF)
+ end
+
+ it "raises an IOError if passed a closed stream" do
+ lambda { IO.send(@method, IOSpecs.closed_io.fileno, 'w') }.should raise_error(IOError)
+ end
+
+ platform_is_not :windows do
+ it "raises an Errno::EINVAL if the new mode is not compatible with the descriptor's current mode" do
+ lambda { IO.send(@method, @fd, "r") }.should raise_error(Errno::EINVAL)
+ end
+ end
+
+ it "raises ArgumentError if passed an empty mode string" do
+ lambda { IO.send(@method, @fd, "") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error if passed modes two ways" do
+ lambda {
+ IO.send(@method, @fd, "w", mode: "w")
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error if passed encodings two ways" do
+ lambda {
+ @io = IO.send(@method, @fd, 'w:ISO-8859-1', {encoding: 'ISO-8859-1'})
+ }.should raise_error(ArgumentError)
+ lambda {
+ @io = IO.send(@method, @fd, 'w:ISO-8859-1', {external_encoding: 'ISO-8859-1'})
+ }.should raise_error(ArgumentError)
+ lambda {
+ @io = IO.send(@method, @fd, 'w:ISO-8859-1:UTF-8', {internal_encoding: 'ISO-8859-1'})
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error if passed matching binary/text mode two ways" do
+ lambda {
+ @io = IO.send(@method, @fd, "wb", binmode: true)
+ }.should raise_error(ArgumentError)
+ lambda {
+ @io = IO.send(@method, @fd, "wt", textmode: true)
+ }.should raise_error(ArgumentError)
+
+ lambda {
+ @io = IO.send(@method, @fd, "wb", textmode: false)
+ }.should raise_error(ArgumentError)
+ lambda {
+ @io = IO.send(@method, @fd, "wt", binmode: false)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error if passed conflicting binary/text mode two ways" do
+ lambda {
+ @io = IO.send(@method, @fd, "wb", binmode: false)
+ }.should raise_error(ArgumentError)
+ lambda {
+ @io = IO.send(@method, @fd, "wt", textmode: false)
+ }.should raise_error(ArgumentError)
+
+ lambda {
+ @io = IO.send(@method, @fd, "wb", textmode: true)
+ }.should raise_error(ArgumentError)
+ lambda {
+ @io = IO.send(@method, @fd, "wt", binmode: true)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error when trying to set both binmode and textmode" do
+ lambda {
+ @io = IO.send(@method, @fd, "w", textmode: true, binmode: true)
+ }.should raise_error(ArgumentError)
+ lambda {
+ @io = IO.send(@method, @fd, File::Constants::WRONLY, textmode: true, binmode: true)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if not passed a hash or nil for options" do
+ lambda {
+ @io = IO.send(@method, @fd, 'w', false)
+ }.should raise_error(ArgumentError)
+ lambda {
+ @io = IO.send(@method, @fd, false, false)
+ }.should raise_error(ArgumentError)
+ lambda {
+ @io = IO.send(@method, @fd, nil, false)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises TypeError if passed a hash for mode and nil for options" do
+ lambda {
+ @io = IO.send(@method, @fd, {mode: 'w'}, nil)
+ }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/rubyspec/core/io/shared/pos.rb b/spec/rubyspec/core/io/shared/pos.rb
new file mode 100644
index 0000000000..fef7ab2bf7
--- /dev/null
+++ b/spec/rubyspec/core/io/shared/pos.rb
@@ -0,0 +1,72 @@
+describe :io_pos, shared: true do
+ before :each do
+ @fname = tmp('test.txt')
+ File.open(@fname, 'w') { |f| f.write "123" }
+ end
+
+ after :each do
+ rm_r @fname
+ end
+
+ it "gets the offset" do
+ File.open @fname do |f|
+ f.send(@method).should == 0
+ f.read 1
+ f.send(@method).should == 1
+ f.read 2
+ f.send(@method).should == 3
+ end
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.send(@method) }.should raise_error(IOError)
+ end
+
+ it "resets #eof?" do
+ open @fname do |io|
+ io.read 1
+ io.read 1
+ io.send(@method)
+ io.eof?.should == false
+ end
+ end
+end
+
+describe :io_set_pos, shared: true do
+ before :each do
+ @fname = tmp('test.txt')
+ File.open(@fname, 'w') { |f| f.write "123" }
+ end
+
+ after :each do
+ rm_r @fname
+ end
+
+ it "sets the offset" do
+ File.open @fname do |f|
+ val1 = f.read 1
+ f.send @method, 0
+ f.read(1).should == val1
+ end
+ end
+
+ it "converts arguments to Integers" do
+ File.open @fname do |io|
+ o = mock("o")
+ o.should_receive(:to_int).and_return(1)
+
+ io.send @method, o
+ io.pos.should == 1
+ end
+ end
+
+ it "does not accept Bignums that don't fit in a C long" do
+ File.open @fname do |io|
+ lambda { io.send @method, 2**128 }.should raise_error(RangeError)
+ end
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.send @method, 0 }.should raise_error(IOError)
+ end
+end
diff --git a/spec/rubyspec/core/io/shared/readlines.rb b/spec/rubyspec/core/io/shared/readlines.rb
new file mode 100644
index 0000000000..4cb821274a
--- /dev/null
+++ b/spec/rubyspec/core/io/shared/readlines.rb
@@ -0,0 +1,204 @@
+describe :io_readlines, shared: true do
+ it "raises TypeError if the first parameter is nil" do
+ lambda { IO.send(@method, nil, &@object) }.should raise_error(TypeError)
+ end
+
+ it "raises an Errno::ENOENT if the file does not exist" do
+ name = tmp("nonexistent.txt")
+ lambda { IO.send(@method, name, &@object) }.should raise_error(Errno::ENOENT)
+ end
+
+ it "yields a single string with entire content when the separator is nil" do
+ result = IO.send(@method, @name, nil, &@object)
+ (result ? result : ScratchPad.recorded).should == [IO.read(@name)]
+ end
+
+ it "yields a sequence of paragraphs when the separator is an empty string" do
+ result = IO.send(@method, @name, "", &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_empty_separator
+ end
+end
+
+describe :io_readlines_options_19, shared: true do
+ before :each do
+ @filename = tmp("io readlines options")
+ end
+
+ after :each do
+ rm_r @filename
+ end
+
+ describe "when passed name" do
+ it "calls #to_path to convert the name" do
+ name = mock("io name to_path")
+ name.should_receive(:to_path).and_return(@name)
+ IO.send(@method, name, &@object)
+ end
+
+ it "defaults to $/ as the separator" do
+ result = IO.send(@method, @name, &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines
+ end
+ end
+
+ describe "when passed name, object" do
+ it "calls #to_str to convert the object to a separator" do
+ sep = mock("io readlines separator")
+ sep.should_receive(:to_str).at_least(1).and_return(" ")
+ result = IO.send(@method, @name, sep, &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator
+ end
+
+ describe "when the object is a Fixnum" do
+ before :each do
+ @sep = $/
+ end
+
+ after :each do
+ $/ = @sep
+ end
+
+ it "defaults to $/ as the separator" do
+ $/ = " "
+ result = IO.send(@method, @name, 10, &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
+ end
+
+ it "uses the object as a limit if it is a Fixnum" do
+ result = IO.send(@method, @name, 10, &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_limit
+ end
+ end
+
+ describe "when the object is a String" do
+ it "uses the value as the separator" do
+ result = IO.send(@method, @name, " ", &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator
+ end
+
+ it "accepts non-ASCII data as separator" do
+ result = IO.send(@method, @name, "\303\250".force_encoding("utf-8"), &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_arbitrary_separator
+ end
+ end
+
+ describe "when the object is a Hash" do
+ it "uses the value as the options hash" do
+ result = IO.send(@method, @name, mode: "r", &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines
+ end
+ end
+ end
+
+ describe "when passed name, object, object" do
+ describe "when the first object is a Fixnum" do
+ it "uses the second object as an options Hash" do
+ lambda do
+ IO.send(@method, @filename, 10, mode: "w", &@object)
+ end.should raise_error(IOError)
+ end
+
+ it "calls #to_hash to convert the second object to a Hash" do
+ options = mock("io readlines options Hash")
+ options.should_receive(:to_hash).and_return({ mode: "w" })
+ lambda do
+ IO.send(@method, @filename, 10, options, &@object)
+ end.should raise_error(IOError)
+ end
+ end
+
+ describe "when the first object is a String" do
+ it "uses the second object as a limit if it is a Fixnum" do
+ result = IO.send(@method, @name, " ", 10, &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
+ end
+
+ it "calls #to_int to convert the second object" do
+ limit = mock("io readlines limit")
+ limit.should_receive(:to_int).at_least(1).and_return(10)
+ result = IO.send(@method, @name, " ", limit, &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
+ end
+
+ it "uses the second object as an options Hash" do
+ lambda do
+ IO.send(@method, @filename, " ", mode: "w", &@object)
+ end.should raise_error(IOError)
+ end
+
+ it "calls #to_hash to convert the second object to a Hash" do
+ options = mock("io readlines options Hash")
+ options.should_receive(:to_hash).and_return({ mode: "w" })
+ lambda do
+ IO.send(@method, @filename, " ", options, &@object)
+ end.should raise_error(IOError)
+ end
+ end
+
+ describe "when the first object is not a String or Fixnum" do
+ it "calls #to_str to convert the object to a String" do
+ sep = mock("io readlines separator")
+ sep.should_receive(:to_str).at_least(1).and_return(" ")
+ result = IO.send(@method, @name, sep, 10, mode: "r", &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
+ end
+
+ it "uses the second object as a limit if it is a Fixnum" do
+ result = IO.send(@method, @name, " ", 10, mode: "r", &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
+ end
+
+ it "calls #to_int to convert the second object" do
+ limit = mock("io readlines limit")
+ limit.should_receive(:to_int).at_least(1).and_return(10)
+ result = IO.send(@method, @name, " ", limit, &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
+ end
+
+ it "uses the second object as an options Hash" do
+ lambda do
+ IO.send(@method, @filename, " ", mode: "w", &@object)
+ end.should raise_error(IOError)
+ end
+
+ it "calls #to_hash to convert the second object to a Hash" do
+ options = mock("io readlines options Hash")
+ options.should_receive(:to_hash).and_return({ mode: "w" })
+ lambda do
+ IO.send(@method, @filename, " ", options, &@object)
+ end.should raise_error(IOError)
+ end
+ end
+ end
+
+ describe "when passed name, separator, limit, options" do
+ it "calls #to_path to convert the name object" do
+ name = mock("io name to_path")
+ name.should_receive(:to_path).and_return(@name)
+ result = IO.send(@method, name, " ", 10, mode: "r", &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
+ end
+
+ it "calls #to_str to convert the separator object" do
+ sep = mock("io readlines separator")
+ sep.should_receive(:to_str).at_least(1).and_return(" ")
+ result = IO.send(@method, @name, sep, 10, mode: "r", &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
+ end
+
+ it "calls #to_int to convert the limit argument" do
+ limit = mock("io readlines limit")
+ limit.should_receive(:to_int).at_least(1).and_return(10)
+ result = IO.send(@method, @name, " ", limit, mode: "r", &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
+ end
+
+ it "calls #to_hash to convert the options object" do
+ options = mock("io readlines options Hash")
+ options.should_receive(:to_hash).and_return({ mode: "w" })
+ lambda do
+ IO.send(@method, @filename, " ", 10, options, &@object)
+ end.should raise_error(IOError)
+ end
+ end
+end
diff --git a/spec/rubyspec/core/io/shared/tty.rb b/spec/rubyspec/core/io/shared/tty.rb
new file mode 100644
index 0000000000..eddc5d15af
--- /dev/null
+++ b/spec/rubyspec/core/io/shared/tty.rb
@@ -0,0 +1,25 @@
+require File.expand_path('../../fixtures/classes', __FILE__)
+
+describe :io_tty, shared: true do
+ platform_is_not :windows do
+ it "returns true if this stream is a terminal device (TTY)" do
+ begin
+ # check to enabled tty
+ File.open('/dev/tty') {}
+ rescue Errno::ENXIO
+ # workaround for not configured environment like OS X
+ 1.should == 1
+ else
+ File.open('/dev/tty') { |f| f.send(@method) }.should == true
+ end
+ end
+ end
+
+ it "returns false if this stream is not a terminal device (TTY)" do
+ File.open(__FILE__) { |f| f.send(@method) }.should == false
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.send @method }.should raise_error(IOError)
+ end
+end
diff --git a/spec/rubyspec/core/io/shared/write.rb b/spec/rubyspec/core/io/shared/write.rb
new file mode 100644
index 0000000000..fd4b0af30e
--- /dev/null
+++ b/spec/rubyspec/core/io/shared/write.rb
@@ -0,0 +1,72 @@
+# encoding: utf-8
+require File.expand_path('../../fixtures/classes', __FILE__)
+
+describe :io_write, shared: true do
+ before :each do
+ @filename = tmp("IO_syswrite_file") + $$.to_s
+ File.open(@filename, "w") do |file|
+ file.send(@method, "012345678901234567890123456789")
+ end
+ @file = File.open(@filename, "r+")
+ @readonly_file = File.open(@filename)
+ end
+
+ after :each do
+ @readonly_file.close if @readonly_file
+ @file.close if @file
+ rm_r @filename
+ end
+
+ it "coerces the argument to a string using to_s" do
+ (obj = mock('test')).should_receive(:to_s).and_return('a string')
+ @file.send(@method, obj)
+ end
+
+ it "checks if the file is writable if writing more than zero bytes" do
+ lambda { @readonly_file.send(@method, "abcde") }.should raise_error(IOError)
+ end
+
+ it "returns the number of bytes written" do
+ written = @file.send(@method, "abcde")
+ written.should == 5
+ end
+
+ it "invokes to_s on non-String argument" do
+ data = "abcdefgh9876"
+ (obj = mock(data)).should_receive(:to_s).and_return(data)
+ @file.send(@method, obj)
+ @file.seek(0)
+ @file.read(data.size).should == data
+ end
+
+ it "writes all of the string's bytes without buffering if mode is sync" do
+ @file.sync = true
+ written = @file.send(@method, "abcde")
+ written.should == 5
+ File.open(@filename) do |file|
+ file.read(10).should == "abcde56789"
+ end
+ end
+
+ it "does not warn if called after IO#read" do
+ @file.read(5)
+ lambda { @file.send(@method, "fghij") }.should_not complain
+ end
+
+ it "writes to the current position after IO#read" do
+ @file.read(5)
+ @file.send(@method, "abcd")
+ @file.rewind
+ @file.read.should == "01234abcd901234567890123456789"
+ end
+
+ it "advances the file position by the count of given bytes" do
+ @file.send(@method, "abcde")
+ @file.read(10).should == "5678901234"
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.send(@method, "hello") }.should raise_error(IOError)
+ end
+
+end