Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support IO.copy_stream from a pipe IO instance. #2114

Merged
merged 6 commits into from Feb 4, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 18 additions & 1 deletion kernel/common/io19.rb
Expand Up @@ -274,7 +274,11 @@ def run
@from.ensure_open_and_readable
@to.ensure_open_and_writable

saved_pos = @from.pos if @from_io
if @from_io && !@from.pipe?
saved_pos = @from.pos
else
saved_pos = 0
end

@from.seek @offset, IO::SEEK_CUR if @offset

Expand Down Expand Up @@ -410,6 +414,8 @@ def initialize(fd, mode=undefined, options=undefined)
@external = Encoding.default_external
end
end

@pipe = false
end

private :initialize
Expand Down Expand Up @@ -945,6 +951,14 @@ def close_on_exec?
(fcntl(F_GETFD) & FD_CLOEXEC) != 0
end

def pipe=(v)
@pipe = !!v
end

def pipe?
@pipe
end

def self.pipe(external=nil, internal=nil, options=nil)
lhs = allocate
rhs = allocate
Expand All @@ -961,6 +975,9 @@ def self.pipe(external=nil, internal=nil, options=nil)
lhs.sync = true
rhs.sync = true

lhs.pipe = true
rhs.pipe = true

if block_given?
begin
yield lhs, rhs
Expand Down
66 changes: 55 additions & 11 deletions spec/ruby/core/io/copy_stream_spec.rb
Expand Up @@ -20,11 +20,6 @@
@to_name.should have_data("Line one")
end

it "copies only length bytes from the offset" do
IO.copy_stream(@object.from, @to_name, 8, 4).should == 8
@to_name.should have_data(" one\n\nLi")
end

it "calls #to_path to convert on object to a file name" do
obj = mock("io_copy_stream_to")
obj.should_receive(:to_path).and_return(@to_name)
Expand All @@ -41,12 +36,19 @@
end
end

describe :io_copy_stream_to_file_with_offset, :shared => true do
it "copies only length bytes from the offset" do
IO.copy_stream(@object.from, @to_name, 8, 4).should == 8
@to_name.should have_data(" one\n\nLi")
end
end

describe :io_copy_stream_to_io, :shared => true do
it "copies the entire IO contents to the IO" do
IO.copy_stream(@object.from, @to_io)
@to_name.should have_data(@content)
IO.copy_stream(@from_bigfile, @to_io)
@to_io.should have_data(@content + @content_bigfile)
@to_name.should have_data(@content + @content_bigfile)
end

it "returns the number of bytes copied" do
Expand All @@ -67,7 +69,7 @@

it "raises an IOError if the destination IO is not open for writing" do
@to_io.close
@to_io = File.open @to_name, "r"
@to_io = new_io @to_name, "r"
lambda { IO.copy_stream @object.from, @to_io }.should raise_error(IOError)
end

Expand All @@ -80,7 +82,9 @@
IO.copy_stream(@object.from, @to_io, 8).should == 8
@to_name.should have_data("Line one")
end
end

describe :io_copy_stream_to_io_with_offset, :shared => true do
it "copies only length bytes from the offset" do
IO.copy_stream(@object.from, @to_io, 8, 4).should == 8
@to_name.should have_data(" one\n\nLi")
Expand All @@ -107,7 +111,7 @@

describe "from an IO" do
before :each do
@from_io = File.open @from_name, "rb"
@from_io = new_io @from_name, "rb"
IOSpecs::CopyStream.from = @from_io
end

Expand All @@ -117,7 +121,7 @@

it "raises an IOError if the source IO is not open for reading" do
@from_io.close
@from_io = File.open @from_name, "a"
@from_io = new_io @from_name, "a"
lambda { IO.copy_stream @from_io, @to_name }.should raise_error(IOError)
end

Expand All @@ -140,18 +144,20 @@

describe "to a file name" do
it_behaves_like :io_copy_stream_to_file, nil, IOSpecs::CopyStream
it_behaves_like :io_copy_stream_to_file_with_offset, nil, IOSpecs::CopyStream
end

describe "to an IO" do
before :each do
@to_io = File.open @to_name, "wb"
@to_io = new_io @to_name, "wb"
end

after :each do
@to_io.close
end

it_behaves_like :io_copy_stream_to_io, nil, IOSpecs::CopyStream
it_behaves_like :io_copy_stream_to_io_with_offset, nil, IOSpecs::CopyStream
end
end

Expand All @@ -175,13 +181,51 @@
lambda { IO.copy_stream(obj, @to_name) }.should raise_error(TypeError)
end

describe "to a file name" do
it_behaves_like :io_copy_stream_to_file, nil, IOSpecs::CopyStream
it_behaves_like :io_copy_stream_to_file_with_offset, nil, IOSpecs::CopyStream
end

describe "to an IO" do
before :each do
@to_io = new_io @to_name, "wb"
end

after :each do
@to_io.close
end

it_behaves_like :io_copy_stream_to_io, nil, IOSpecs::CopyStream
it_behaves_like :io_copy_stream_to_io_with_offset, nil, IOSpecs::CopyStream
end
end

describe "from a pipe IO" do
before :each do
@from_io = IOSpecs.pipe_fixture(@content)
IOSpecs::CopyStream.from = @from_io
end

after :each do
@from_io.close
end

it "does not close the source IO" do
IO.copy_stream(@from_io, @to_name)
@from_io.closed?.should be_false
end

it "raises an error when an offset is specified" do
lambda { IO.copy_stream(@from_io, @to_name, 8, 4) }.should raise_error(Errno::ESPIPE)
end

describe "to a file name" do
it_behaves_like :io_copy_stream_to_file, nil, IOSpecs::CopyStream
end

describe "to an IO" do
before :each do
@to_io = File.open @to_name, "wb"
@to_io = new_io @to_name, "wb"
end

after :each do
Expand Down
9 changes: 9 additions & 0 deletions spec/ruby/core/io/fixtures/classes.rb
Expand Up @@ -121,6 +121,15 @@ def self.closed_io
io.close
io
end

# Creates a pipe-based IO fixture containing the specified
# contents
def self.pipe_fixture(content)
source, sink = IO.pipe
sink.write content
sink.close
source
end

# Defines +method+ on +obj+ using the provided +block+. This
# special helper is needed for e.g. IO.open specs to avoid
Expand Down