Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 184 additions & 0 deletions core/io/buffer/fixtures/big_file.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
require_relative '../../../spec_helper'

describe "IO::Buffer.map" do
after :each do
@buffer&.free
@buffer = nil
@file&.close
@file = nil
end

def open_fixture
File.open("#{__dir__}/../fixtures/read_text.txt", "r+")
end

it "creates a new buffer mapped from a file" do
@file = open_fixture
@buffer = IO::Buffer.map(@file)

@buffer.size.should == 9
@buffer.get_string.force_encoding(Encoding::UTF_8).should == "abcâdef\n"
end

ruby_version_is ""..."3.3" do
it "creates a buffer with default state and expected flags" do
@file = open_fixture
@buffer = IO::Buffer.map(@file)

@buffer.should_not.internal?
@buffer.should.mapped?
@buffer.should.external?

@buffer.should_not.empty?
@buffer.should_not.null?

@buffer.should.shared?
@buffer.should_not.readonly?

@buffer.should_not.locked?
@buffer.should.valid?
end
end

ruby_version_is "3.3" do
it "creates a buffer with default state and expected flags" do
@file = open_fixture
@buffer = IO::Buffer.map(@file)

@buffer.should_not.internal?
@buffer.should.mapped?
@buffer.should.external?

@buffer.should_not.empty?
@buffer.should_not.null?

@buffer.should.shared?
@buffer.should_not.private?
@buffer.should_not.readonly?

@buffer.should_not.locked?
@buffer.should.valid?
end
end

context "with an empty file" do
it "raises Errno::EINVAL" do
-> { IO::Buffer.map(File.open("#{__dir__}/../fixtures/empty.txt", "r+")) }.should raise_error(Errno::EINVAL)
end
end

context "with size argument" do
it "limits the buffer to the specified size in bytes, starting from the start of the file" do
@file = open_fixture
@buffer = IO::Buffer.map(@file, 4)

@buffer.size.should == 4
@buffer.get_string.force_encoding(Encoding::UTF_8).should == "abc\xC3"
end

it "maps the whole file if size is nil" do
@file = open_fixture
@buffer = IO::Buffer.map(@file, nil)

@buffer.size.should == 9
end

it "raises Errno::EINVAL if size is 0" do
@file = open_fixture
-> { IO::Buffer.map(@file, 0) }.should raise_error(Errno::EINVAL)
end

it "raises TypeError if size is not an Integer or nil" do
@file = open_fixture
-> { IO::Buffer.map(@file, "10") }.should raise_error(TypeError, "not an Integer")
-> { IO::Buffer.map(@file, 10.0) }.should raise_error(TypeError, "not an Integer")
end

it "raises ArgumentError if size is negative" do
@file = open_fixture
-> { IO::Buffer.map(@file, -1) }.should raise_error(ArgumentError, "Size can't be negative!")
end
end

context "with size and offset arguments" do
context "if offset is a multiple of page size" do
it "maps the specified length starting from the offset" do
@file = File.open("#{__dir__}/fixtures/big_file.txt", "r+")
@buffer = IO::Buffer.map(@file, 14, IO::Buffer::PAGE_SIZE)

@buffer.size.should == 14
@buffer.get_string.force_encoding(Encoding::UTF_8).should == "-> { IO::Buffe"
end

it "maps the rest of the file if size is nil" do
@file = File.open("#{__dir__}/fixtures/big_file.txt", "r+")
@buffer = IO::Buffer.map(@file, nil, IO::Buffer::PAGE_SIZE * 2)

@buffer.size.should == 5895 # BUG: this is wrong
# @buffer.get_string.force_encoding(Encoding::UTF_8).should == "-> { IO::Buffe"
end
end

it "maps the file from the start if offset is 0" do
@file = open_fixture
@buffer = IO::Buffer.map(@file, 4, 0)

@buffer.size.should == 4
@buffer.get_string.force_encoding(Encoding::UTF_8).should == "abc\xC3"
end

it "raises TypeError if offset is not convertible to Integer" do
@file = open_fixture
-> { IO::Buffer.map(@file, 4, "3") }.should raise_error(TypeError, "no implicit conversion of String into Integer")
end

it "raises Errno::EINVAL if offset is not an Integer" do
@file = open_fixture
-> { IO::Buffer.map(@file, 4, 3.0) }.should raise_error(Errno::EINVAL)
end

it "raises Errno::EINVAL if offset is negative" do
@file = open_fixture
-> { IO::Buffer.map(@file, 4, -1) }.should raise_error(Errno::EINVAL)
end
end

context "with size and flags arguments" do
it "forces mapped buffer with IO::Buffer::MAPPED flag" do
@buffer = IO::Buffer.new(IO::Buffer::PAGE_SIZE - 1, IO::Buffer::MAPPED)
@buffer.should.mapped?
@buffer.should_not.internal?
@buffer.should_not.empty?
end

it "forces internal buffer with IO::Buffer::INTERNAL flag" do
@buffer = IO::Buffer.new(IO::Buffer::PAGE_SIZE, IO::Buffer::INTERNAL)
@buffer.should.internal?
@buffer.should_not.mapped?
@buffer.should_not.empty?
end

it "raises IO::Buffer::AllocationError if neither IO::Buffer::MAPPED nor IO::Buffer::INTERNAL is given" do
-> { IO::Buffer.new(10, IO::Buffer::READONLY) }.should raise_error(IO::Buffer::AllocationError, "Could not allocate buffer!")
-> { IO::Buffer.new(10, 0) }.should raise_error(IO::Buffer::AllocationError, "Could not allocate buffer!")
end

ruby_version_is "3.3" do
it "raises ArgumentError if flags is negative" do
-> { IO::Buffer.new(10, -1) }.should raise_error(ArgumentError, "Flags can't be negative!")
end
end

ruby_version_is ""..."3.3" do
it "raises IO::Buffer::AllocationError with non-Integer flags" do
-> { IO::Buffer.new(10, 0.0) }.should raise_error(IO::Buffer::AllocationError, "Could not allocate buffer!")
end
end

ruby_version_is "3.3" do
it "raises TypeError with non-Integer flags" do
-> { IO::Buffer.new(10, 0.0) }.should raise_error(TypeError, "not an Integer")
end
end
end
end
160 changes: 160 additions & 0 deletions core/io/buffer/map_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
require_relative '../../../spec_helper'

describe "IO::Buffer.map" do
after :each do
@buffer&.free
@buffer = nil
@file&.close
@file = nil
end

def open_fixture
File.open("#{__dir__}/../fixtures/read_text.txt", "r+")
end

it "creates a new buffer mapped from a file" do
@file = open_fixture
@buffer = IO::Buffer.map(@file)

@buffer.size.should == 9
@buffer.get_string.force_encoding(Encoding::UTF_8).should == "abcâdef\n"
end

it "allows to close the file after creating buffer, retaining mapping" do
File.open("#{__dir__}/../fixtures/read_text.txt", "r+") do |file|
@buffer = IO::Buffer.map(file)
end
@buffer.get_string.force_encoding(Encoding::UTF_8).should == "abcâdef\n"
end

ruby_version_is ""..."3.3" do
it "creates a buffer with default state and expected flags" do
@file = open_fixture
@buffer = IO::Buffer.map(@file)

@buffer.should_not.internal?
@buffer.should.mapped?
@buffer.should.external?

@buffer.should_not.empty?
@buffer.should_not.null?

@buffer.should.shared?
@buffer.should_not.readonly?

@buffer.should_not.locked?
@buffer.should.valid?
end
end

ruby_version_is "3.3" do
it "creates a buffer with default state and expected flags" do
@file = open_fixture
@buffer = IO::Buffer.map(@file)

@buffer.should_not.internal?
@buffer.should.mapped?
@buffer.should.external?

@buffer.should_not.empty?
@buffer.should_not.null?

@buffer.should.shared?
@buffer.should_not.private?
@buffer.should_not.readonly?

@buffer.should_not.locked?
@buffer.should.valid?
end
end

context "with an empty file" do
it "raises Errno::EINVAL" do
@file = File.open("#{__dir__}/../fixtures/empty.txt", "r+")
-> { IO::Buffer.map(@file) }.should raise_error(Errno::EINVAL)
end
end

context "with size argument" do
it "limits the buffer to the specified size in bytes, starting from the start of the file" do
@file = open_fixture
@buffer = IO::Buffer.map(@file, 4)

@buffer.size.should == 4
@buffer.get_string.force_encoding(Encoding::UTF_8).should == "abc\xC3"
end

it "maps the whole file if size is nil" do
@file = open_fixture
@buffer = IO::Buffer.map(@file, nil)

@buffer.size.should == 9
end

it "raises Errno::EINVAL if size is 0" do
@file = open_fixture
-> { IO::Buffer.map(@file, 0) }.should raise_error(Errno::EINVAL)
end

it "raises TypeError if size is not an Integer or nil" do
@file = open_fixture
-> { IO::Buffer.map(@file, "10") }.should raise_error(TypeError, "not an Integer")
-> { IO::Buffer.map(@file, 10.0) }.should raise_error(TypeError, "not an Integer")
end

it "raises ArgumentError if size is negative" do
@file = open_fixture
-> { IO::Buffer.map(@file, -1) }.should raise_error(ArgumentError, "Size can't be negative!")
end
end

context "with size and offset arguments" do
context "if offset is a multiple of page size" do
it "maps the specified length starting from the offset" do
@file = File.open("README.md", "r+")
@buffer = IO::Buffer.map(@file, 14, 65536)

@buffer.size.should == 14
# @buffer.get_string(0, 14).force_encoding(Encoding::UTF_8).should == "# Ruby Spec"
end

# A second mapping just doesn't work?
it "maps the rest of the file if size is nil" do
@file = File.open("#{__dir__}/fixtures/big_file.txt", "r+")
@buffer = IO::Buffer.map(@file, nil, IO::Buffer::PAGE_SIZE)

@buffer.size.should == 5895 # BUG: why is this the total size?
@buffer.get_string(0, 1).force_encoding(Encoding::UTF_8).should == "-"
end
end

it "raises Errno::EINVAL if offset is not a multiple of page size" do
@file = open_fixture
-> { IO::Buffer.map(@file, 4, IO::Buffer::PAGE_SIZE / 2) }.should raise_error(Errno::EINVAL)
end

it "maps the file from the start if offset is 0" do
@file = open_fixture
@buffer = IO::Buffer.map(@file, 4, 0)

@buffer.size.should == 4
@buffer.get_string.force_encoding(Encoding::UTF_8).should == "abc\xC3"
end

it "raises TypeError if offset is not convertible to Integer" do
@file = open_fixture
-> { IO::Buffer.map(@file, 4, "4096") }.should raise_error(TypeError, "no implicit conversion of String into Integer")
-> { IO::Buffer.map(@file, 4, nil) }.should raise_error(TypeError, "no implicit conversion from nil to integer")
end

it "raises Errno::EINVAL if offset is not an allowed value" do
@file = open_fixture
-> { IO::Buffer.map(@file, 4, 3) }.should raise_error(Errno::EINVAL)
end

it "raises Errno::EINVAL if offset is negative" do
@file = open_fixture
-> { IO::Buffer.map(@file, 4, -1) }.should raise_error(Errno::EINVAL)
end
end
end
Loading