diff options
Diffstat (limited to 'spec/rubyspec/core/io/shared')
-rw-r--r-- | spec/rubyspec/core/io/shared/binwrite.rb | 78 | ||||
-rw-r--r-- | spec/rubyspec/core/io/shared/chars.rb | 73 | ||||
-rw-r--r-- | spec/rubyspec/core/io/shared/codepoints.rb | 54 | ||||
-rw-r--r-- | spec/rubyspec/core/io/shared/each.rb | 135 | ||||
-rw-r--r-- | spec/rubyspec/core/io/shared/gets_ascii.rb | 19 | ||||
-rw-r--r-- | spec/rubyspec/core/io/shared/new.rb | 378 | ||||
-rw-r--r-- | spec/rubyspec/core/io/shared/pos.rb | 72 | ||||
-rw-r--r-- | spec/rubyspec/core/io/shared/readlines.rb | 204 | ||||
-rw-r--r-- | spec/rubyspec/core/io/shared/tty.rb | 25 | ||||
-rw-r--r-- | spec/rubyspec/core/io/shared/write.rb | 72 |
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 |