Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
108 changes: 108 additions & 0 deletions core/io/buffer/free_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
require_relative '../../../spec_helper'

describe "IO::Buffer#free" do
context "with a buffer created with .new" do
it "frees internal memory and nullifies the buffer" do
buffer = IO::Buffer.new(4)
buffer.free
buffer.null?.should be_true
end

it "frees mapped memory and nullifies the buffer" do
buffer = IO::Buffer.new(4, IO::Buffer::MAPPED)
buffer.free
buffer.null?.should be_true
end
end

context "with a file-backed buffer created with .map" do
it "frees mapped memory and nullifies the buffer" do
File.open(__FILE__, "r") do |file|
buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY)
buffer.free
buffer.null?.should be_true
end
end
end

context "with a String-backed buffer created with .for" do
context "without a block" do
it "disassociates the buffer from the string and nullifies the buffer" do
string = +"test"
buffer = IO::Buffer.for(string)
# Read-only buffer, can't modify the string.
buffer.free
buffer.null?.should be_true
end
end

context "with a block" do
it "disassociates the buffer from the string and nullifies the buffer" do
string = +"test"
IO::Buffer.for(string) do |buffer|
buffer.set_string("meat")
buffer.free
buffer.null?.should be_true
end
string.should == "meat"
end
end
end

ruby_version_is "3.3" do
context "with a String-backed buffer created with .string" do
it "disassociates the buffer from the string and nullifies the buffer" do
string =
IO::Buffer.string(4) do |buffer|
buffer.set_string("meat")
buffer.free
buffer.null?.should be_true
end
string.should == "meat"
end
end
end

it "can be called repeatedly without an error" do
buffer = IO::Buffer.new(4)
buffer.free
buffer.null?.should be_true
buffer.free
buffer.null?.should be_true
end

it "is disallowed while locked, raising IO::Buffer::LockedError" do
buffer = IO::Buffer.new(4)
buffer.locked do
-> { buffer.free }.should raise_error(IO::Buffer::LockedError, "Buffer is locked!")
end
buffer.free
buffer.null?.should be_true
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor: seems it's duplicated in specs for #locked?.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and other dupes: I thought it would be better to over-spec than to select which file these interactions shoud be in 😅 I guess it would make sense to remove them from #locked, as it's really a property of individual methods (like this https://github.com/ruby/ruby/blob/master/io_buffer.c#L1485)?


context "with a slice of a buffer" do
it "nullifies the slice, not touching the buffer" do
buffer = IO::Buffer.new(4)
slice = buffer.slice(0, 2)

buffer.set_string("test")
slice.set_string("ea")
slice.free
slice.null?.should be_true
buffer.null?.should be_false
buffer.get_string.should == "east"

buffer.free
end

it "nullifies buffer, invalidating the slice" do
buffer = IO::Buffer.new(4)
slice = buffer.slice(0, 2)
buffer.free

slice.null?.should be_false
slice.valid?.should be_false
-> { slice.get_string }.should raise_error(IO::Buffer::InvalidatedError, "Buffer has been invalidated!")
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor: seems is duplicated in specs for #valid?

end
end
14 changes: 5 additions & 9 deletions core/io/buffer/locked_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,13 @@
end
end

it "disallows freeing while locked, raising IO::Buffer::LockedError" do
it "disallows operations changing buffer itself, raising IO::Buffer::LockedError" do
@buffer = IO::Buffer.new(4)
@buffer.locked do
-> { @buffer.free }.should raise_error(IO::Buffer::LockedError, "Buffer is locked!")
end
end

it "disallows resizing while locked, raising IO::Buffer::LockedError" do
@buffer = IO::Buffer.new(4)
@buffer.locked do
-> { @buffer.resize(8) }.should raise_error(IO::Buffer::LockedError, "Cannot resize locked buffer!")
# See specs of individual methods for error messages.
-> { @buffer.free }.should raise_error(IO::Buffer::LockedError)
-> { @buffer.resize(8) }.should raise_error(IO::Buffer::LockedError)
-> { @buffer.transfer }.should raise_error(IO::Buffer::LockedError)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: it makes sense to specify exception messages everywhere

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor: I suppose other operations changing a buffer should be mentioned here as well (e.g. set_string or write).

end
end

Expand Down
155 changes: 155 additions & 0 deletions core/io/buffer/resize_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
require_relative '../../../spec_helper'

describe "IO::Buffer#resize" do
after :each do
@buffer&.free
@buffer = nil
end

context "with a buffer created with .new" do
it "resizes internal buffer, preserving type" do
@buffer = IO::Buffer.new(4)
@buffer.resize(IO::Buffer::PAGE_SIZE)
@buffer.size.should == IO::Buffer::PAGE_SIZE
@buffer.internal?.should be_true
@buffer.mapped?.should be_false
end

platform_is :linux do
it "resizes mapped buffer, preserving type" do
@buffer = IO::Buffer.new(IO::Buffer::PAGE_SIZE, IO::Buffer::MAPPED)
@buffer.resize(4)
@buffer.size.should == 4
@buffer.internal?.should be_false
@buffer.mapped?.should be_true
end
end

platform_is_not :linux do
it "resizes mapped buffer, changing type to internal" do
@buffer = IO::Buffer.new(IO::Buffer::PAGE_SIZE, IO::Buffer::MAPPED)
@buffer.resize(4)
@buffer.size.should == 4
@buffer.internal?.should be_true
@buffer.mapped?.should be_false
end
end
end

context "with a file-backed buffer created with .map" do
it "disallows resizing shared buffer, raising IO::Buffer::AccessError" do
File.open(__FILE__, "r+") do |file|
@buffer = IO::Buffer.map(file)
-> { @buffer.resize(10) }.should raise_error(IO::Buffer::AccessError, "Cannot resize external buffer!")
end
end

ruby_version_is "3.3" do
it "resizes private buffer, discarding excess contents" do
File.open(__FILE__, "r") do |file|
@buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::PRIVATE)
@buffer.resize(10)
@buffer.size.should == 10
@buffer.get_string.should == "require_re"
@buffer.resize(12)
@buffer.size.should == 12
@buffer.get_string.should == "require_re\0\0"
end
end
end
end

context "with a String-backed buffer created with .for" do
context "without a block" do
it "disallows resizing, raising IO::Buffer::AccessError" do
@buffer = IO::Buffer.for(+"test")
-> { @buffer.resize(10) }.should raise_error(IO::Buffer::AccessError, "Cannot resize external buffer!")
end
end

context "with a block" do
it "disallows resizing, raising IO::Buffer::AccessError" do
IO::Buffer.for(+'test') do |buffer|
-> { buffer.resize(10) }.should raise_error(IO::Buffer::AccessError, "Cannot resize external buffer!")
end
end
end
end

ruby_version_is "3.3" do
context "with a String-backed buffer created with .string" do
it "disallows resizing, raising IO::Buffer::AccessError" do
IO::Buffer.string(4) do |buffer|
-> { buffer.resize(10) }.should raise_error(IO::Buffer::AccessError, "Cannot resize external buffer!")
end
end
end
end

context "with a null buffer" do
it "allows resizing a 0-sized buffer, creating a regular buffer according to new size" do
@buffer = IO::Buffer.new(0)
@buffer.resize(IO::Buffer::PAGE_SIZE)
@buffer.size.should == IO::Buffer::PAGE_SIZE
@buffer.internal?.should be_false
@buffer.mapped?.should be_true
end

it "allows resizing after a free, creating a regular buffer according to new size" do
@buffer = IO::Buffer.for("test")
@buffer.free
@buffer.resize(10)
@buffer.size.should == 10
@buffer.internal?.should be_true
@buffer.mapped?.should be_false
end
end

it "allows resizing to 0, freeing memory" do
@buffer = IO::Buffer.new(4)
@buffer.resize(0)
@buffer.null?.should be_true
end

it "can be called repeatedly" do
@buffer = IO::Buffer.new(4)
@buffer.resize(10)
@buffer.resize(27)
@buffer.resize(1)
@buffer.size.should == 1
end

it "always clears extra memory" do
@buffer = IO::Buffer.new(4)
@buffer.set_string("test")
# This should not cause a re-allocation, just a technical resizing,
# even with very aggressive memory allocation.
@buffer.resize(2)
@buffer.resize(4)
@buffer.get_string.should == "te\0\0"
end

it "is disallowed while locked, raising IO::Buffer::LockedError" do
@buffer = IO::Buffer.new(4)
@buffer.locked do
-> { @buffer.resize(10) }.should raise_error(IO::Buffer::LockedError, "Cannot resize locked buffer!")
end
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor: seems a duplicate


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

it "raises TypeError if size is not an Integer" do
@buffer = IO::Buffer.new(4)
-> { @buffer.resize(nil) }.should raise_error(TypeError, "not an Integer")
-> { @buffer.resize(10.0) }.should raise_error(TypeError, "not an Integer")
end

context "with a slice of a buffer" do
# Current behavior of slice resizing seems unintended (it's undocumented, too).
# It either creates a completely new buffer, or breaks the slice on size 0.
it "needs to be reviewed for spec completeness"
end
end
6 changes: 6 additions & 0 deletions core/io/buffer/shared/null_and_empty.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,10 @@
@buffer.resize(0)
@buffer.send(@method).should be_true
end

it "is true for a buffer whose memory was transferred" do
buffer = IO::Buffer.new(1)
@buffer = buffer.transfer
buffer.send(@method).should be_true
end
end
Loading