Skip to content

Commit 9e6959a

Browse files
committed
Add specs for IO::Buffer#free, #resize and #transfer
1 parent 848f44d commit 9e6959a

File tree

5 files changed

+384
-1
lines changed

5 files changed

+384
-1
lines changed

core/io/buffer/free_spec.rb

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
require_relative '../../../spec_helper'
2+
3+
describe "IO::Buffer#free" do
4+
context "with a buffer created with .new" do
5+
it "frees internal memory and nullifies the buffer" do
6+
buffer = IO::Buffer.new(4)
7+
buffer.free
8+
buffer.null?.should be_true
9+
end
10+
11+
it "frees mapped memory and nullifies the buffer" do
12+
buffer = IO::Buffer.new(4, IO::Buffer::MAPPED)
13+
buffer.free
14+
buffer.null?.should be_true
15+
end
16+
end
17+
18+
context "with a file-backed buffer created with .map" do
19+
it "frees mapped memory and nullifies the buffer" do
20+
File.open(__FILE__, "r") do |file|
21+
buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY)
22+
buffer.free
23+
buffer.null?.should be_true
24+
end
25+
end
26+
end
27+
28+
context "with a String-backed buffer created with .for" do
29+
context "without a block" do
30+
it "disassociates the buffer from the string and nullifies the buffer" do
31+
string = +"test"
32+
buffer = IO::Buffer.for(string)
33+
# Read-only buffer, can't modify the string.
34+
buffer.free
35+
buffer.null?.should be_true
36+
end
37+
end
38+
39+
context "with a block" do
40+
it "disassociates the buffer from the string and nullifies the buffer" do
41+
string = +"test"
42+
IO::Buffer.for(string) do |buffer|
43+
buffer.set_string("meat")
44+
buffer.free
45+
buffer.null?.should be_true
46+
end
47+
string.should == "meat"
48+
end
49+
end
50+
end
51+
52+
ruby_version_is "3.3" do
53+
context "with a String-backed buffer created with .string" do
54+
it "disassociates the buffer from the string and nullifies the buffer" do
55+
string =
56+
IO::Buffer.string(4) do |buffer|
57+
buffer.set_string("meat")
58+
buffer.free
59+
buffer.null?.should be_true
60+
end
61+
string.should == "meat"
62+
end
63+
end
64+
end
65+
66+
it "can be called repeatedly without an error" do
67+
buffer = IO::Buffer.new(4)
68+
buffer.free
69+
buffer.null?.should be_true
70+
buffer.free
71+
buffer.null?.should be_true
72+
end
73+
74+
it "is disallowed while locked, raising IO::Buffer::LockedError" do
75+
buffer = IO::Buffer.new(4)
76+
buffer.locked do
77+
-> { buffer.free }.should raise_error(IO::Buffer::LockedError, "Buffer is locked!")
78+
end
79+
buffer.free
80+
buffer.null?.should be_true
81+
end
82+
83+
context "with a slice of a buffer" do
84+
it "nullifies the slice, not touching the buffer" do
85+
buffer = IO::Buffer.new(4)
86+
slice = buffer.slice(0, 2)
87+
88+
slice.free
89+
slice.null?.should be_true
90+
buffer.null?.should be_false
91+
92+
buffer.free
93+
end
94+
95+
it "nullifies buffer, invalidating the slice" do
96+
buffer = IO::Buffer.new(4)
97+
slice = buffer.slice(0, 2)
98+
99+
buffer.free
100+
slice.null?.should be_false
101+
slice.valid?.should be_false
102+
end
103+
end
104+
end

core/io/buffer/resize_spec.rb

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
require_relative '../../../spec_helper'
2+
3+
describe "IO::Buffer#resize" do
4+
after :each do
5+
@buffer&.free
6+
@buffer = nil
7+
end
8+
9+
context "with a buffer created with .new" do
10+
it "resizes internal buffer, preserving type" do
11+
@buffer = IO::Buffer.new(4)
12+
@buffer.resize(IO::Buffer::PAGE_SIZE)
13+
@buffer.size.should == IO::Buffer::PAGE_SIZE
14+
@buffer.internal?.should be_true
15+
@buffer.mapped?.should be_false
16+
end
17+
18+
platform_is :linux do
19+
it "resizes mapped buffer, preserving type" do
20+
@buffer = IO::Buffer.new(IO::Buffer::PAGE_SIZE, IO::Buffer::MAPPED)
21+
@buffer.resize(4)
22+
@buffer.size.should == 4
23+
@buffer.internal?.should be_false
24+
@buffer.mapped?.should be_true
25+
end
26+
end
27+
28+
platform_is_not :linux do
29+
it "resizes mapped buffer, changing type to internal" do
30+
@buffer = IO::Buffer.new(IO::Buffer::PAGE_SIZE, IO::Buffer::MAPPED)
31+
@buffer.resize(4)
32+
@buffer.size.should == 4
33+
@buffer.internal?.should be_true
34+
@buffer.mapped?.should be_false
35+
end
36+
end
37+
end
38+
39+
context "with a file-backed buffer created with .map" do
40+
it "disallows resizing shared buffer, raising IO::Buffer::AccessError" do
41+
File.open(__FILE__, "r+") do |file|
42+
@buffer = IO::Buffer.map(file)
43+
-> { @buffer.resize(10) }.should raise_error(IO::Buffer::AccessError, "Cannot resize external buffer!")
44+
end
45+
end
46+
47+
ruby_version_is "3.3" do
48+
it "resizes private buffer, discarding excess contents" do
49+
File.open(__FILE__, "r") do |file|
50+
@buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::PRIVATE)
51+
@buffer.resize(10)
52+
@buffer.size.should == 10
53+
@buffer.get_string.should == "require_re"
54+
@buffer.resize(12)
55+
@buffer.size.should == 12
56+
@buffer.get_string.should == "require_re\0\0"
57+
end
58+
end
59+
end
60+
end
61+
62+
context "with a String-backed buffer created with .for" do
63+
context "without a block" do
64+
it "disallows resizing, raising IO::Buffer::AccessError" do
65+
@buffer = IO::Buffer.for(+"test")
66+
-> { @buffer.resize(10) }.should raise_error(IO::Buffer::AccessError, "Cannot resize external buffer!")
67+
end
68+
end
69+
70+
context "with a block" do
71+
it "disallows resizing, raising IO::Buffer::AccessError" do
72+
IO::Buffer.for(+'test') do |buffer|
73+
-> { buffer.resize(10) }.should raise_error(IO::Buffer::AccessError, "Cannot resize external buffer!")
74+
end
75+
end
76+
end
77+
end
78+
79+
ruby_version_is "3.3" do
80+
context "with a String-backed buffer created with .string" do
81+
it "disallows resizing, raising IO::Buffer::AccessError" do
82+
IO::Buffer.string(4) do |buffer|
83+
-> { buffer.resize(10) }.should raise_error(IO::Buffer::AccessError, "Cannot resize external buffer!")
84+
end
85+
end
86+
end
87+
end
88+
89+
context "with a null buffer" do
90+
it "allows resizing a 0-sized buffer, creating a regular buffer according to new size" do
91+
@buffer = IO::Buffer.new(0)
92+
@buffer.resize(IO::Buffer::PAGE_SIZE)
93+
@buffer.size.should == IO::Buffer::PAGE_SIZE
94+
@buffer.internal?.should be_false
95+
@buffer.mapped?.should be_true
96+
end
97+
98+
it "allows resizing after a free, creating a regular buffer according to new size" do
99+
@buffer = IO::Buffer.for("test")
100+
@buffer.free
101+
@buffer.resize(10)
102+
@buffer.size.should == 10
103+
@buffer.internal?.should be_true
104+
@buffer.mapped?.should be_false
105+
end
106+
end
107+
108+
it "allows resizing to 0, freeing memory" do
109+
@buffer = IO::Buffer.new(4)
110+
@buffer.resize(0)
111+
@buffer.null?.should be_true
112+
end
113+
114+
it "can be called repeatedly" do
115+
@buffer = IO::Buffer.new(4)
116+
@buffer.resize(10)
117+
@buffer.resize(27)
118+
@buffer.resize(1)
119+
@buffer.size.should == 1
120+
end
121+
122+
it "always clears extra memory" do
123+
@buffer = IO::Buffer.new(4)
124+
@buffer.set_string("test")
125+
# This should not cause a re-allocation, just a technical resizing,
126+
# even with very aggressive memory allocation.
127+
@buffer.resize(2)
128+
@buffer.resize(4)
129+
@buffer.get_string.should == "te\0\0"
130+
end
131+
132+
it "is disallowed while locked, raising IO::Buffer::LockedError" do
133+
@buffer = IO::Buffer.new(4)
134+
@buffer.locked do
135+
-> { @buffer.resize(10) }.should raise_error(IO::Buffer::LockedError, "Cannot resize locked buffer!")
136+
end
137+
end
138+
139+
it "raises ArgumentError if size is negative" do
140+
@buffer = IO::Buffer.new(4)
141+
-> { @buffer.resize(-1) }.should raise_error(ArgumentError, "Size can't be negative!")
142+
end
143+
144+
it "raises TypeError if size is not an Integer" do
145+
@buffer = IO::Buffer.new(4)
146+
-> { @buffer.resize(nil) }.should raise_error(TypeError, "not an Integer")
147+
-> { @buffer.resize(10.0) }.should raise_error(TypeError, "not an Integer")
148+
end
149+
150+
context "with a slice of a buffer" do
151+
# Current behavior of slice resizing seems unintended (it's undocumented, too).
152+
# It either creates a completely new buffer, or breaks the slice on size 0.
153+
it "needs to be reviewed for spec completeness"
154+
end
155+
end

core/io/buffer/shared/null_and_empty.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,10 @@
5050
@buffer.resize(0)
5151
@buffer.send(@method).should be_true
5252
end
53+
54+
it "is true for a buffer whose memory was transferred" do
55+
buffer = IO::Buffer.new(1)
56+
@buffer = buffer.transfer
57+
buffer.send(@method).should be_true
58+
end
5359
end

core/io/buffer/transfer_spec.rb

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
require_relative '../../../spec_helper'
2+
3+
describe "IO::Buffer#transfer" do
4+
after :each do
5+
@buffer&.free
6+
@buffer = nil
7+
end
8+
9+
context "with a buffer created with .new" do
10+
it "transfers internal memory to a new buffer, nullifying the original" do
11+
buffer = IO::Buffer.new(4)
12+
info = buffer.to_s
13+
@buffer = buffer.transfer
14+
@buffer.to_s.should == info
15+
buffer.null?.should be_true
16+
end
17+
18+
it "transfers mapped memory to a new buffer, nullifying the original" do
19+
buffer = IO::Buffer.new(4, IO::Buffer::MAPPED)
20+
info = buffer.to_s
21+
@buffer = buffer.transfer
22+
@buffer.to_s.should == info
23+
buffer.null?.should be_true
24+
end
25+
end
26+
27+
context "with a file-backed buffer created with .map" do
28+
it "transfers mapped memory to a new buffer, nullifying the original" do
29+
File.open(__FILE__, "r") do |file|
30+
buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY)
31+
info = buffer.to_s
32+
@buffer = buffer.transfer
33+
@buffer.to_s.should == info
34+
buffer.null?.should be_true
35+
end
36+
end
37+
end
38+
39+
context "with a String-backed buffer created with .for" do
40+
context "without a block" do
41+
it "transfers memory to a new buffer, nullifying the original" do
42+
buffer = IO::Buffer.for("test")
43+
info = buffer.to_s
44+
@buffer = buffer.transfer
45+
@buffer.to_s.should == info
46+
buffer.null?.should be_true
47+
end
48+
end
49+
50+
context "with a block" do
51+
it "transfers memory to a new buffer, breaking the transaction by nullifying the original" do
52+
IO::Buffer.for(+"test") do |buffer|
53+
info = buffer.to_s
54+
@buffer = buffer.transfer
55+
@buffer.to_s.should == info
56+
buffer.null?.should be_true
57+
end
58+
@buffer.null?.should be_false
59+
end
60+
end
61+
end
62+
63+
ruby_version_is "3.3" do
64+
context "with a String-backed buffer created with .string" do
65+
it "transfers memory to a new buffer, breaking the transaction by nullifying the original" do
66+
IO::Buffer.string(4) do |buffer|
67+
info = buffer.to_s
68+
@buffer = buffer.transfer
69+
@buffer.to_s.should == info
70+
buffer.null?.should be_true
71+
end
72+
@buffer.null?.should be_false
73+
end
74+
end
75+
end
76+
77+
it "allows multiple transfers" do
78+
buffer_1 = IO::Buffer.new(4)
79+
buffer_2 = buffer_1.transfer
80+
@buffer = buffer_2.transfer
81+
buffer_1.null?.should be_true
82+
buffer_2.null?.should be_true
83+
@buffer.null?.should be_false
84+
end
85+
86+
it "is disallowed while locked, raising IO::Buffer::LockedError" do
87+
@buffer = IO::Buffer.new(4)
88+
@buffer.locked do
89+
-> { @buffer.transfer }.should raise_error(IO::Buffer::LockedError, "Cannot transfer ownership of locked buffer!")
90+
end
91+
end
92+
93+
context "with a slice of a buffer" do
94+
it "transfers source to a new slice, not touching the buffer" do
95+
@buffer = IO::Buffer.new(4)
96+
slice = @buffer.slice(0, 2)
97+
@buffer.set_string("test")
98+
99+
new_slice = slice.transfer
100+
slice.null?.should be_true
101+
new_slice.null?.should be_false
102+
@buffer.null?.should be_false
103+
104+
new_slice.set_string("ea")
105+
@buffer.get_string.should == "east"
106+
end
107+
108+
it "nullifies buffer, invalidating the slice" do
109+
buffer = IO::Buffer.new(4)
110+
slice = buffer.slice(0, 2)
111+
@buffer = buffer.transfer
112+
113+
slice.null?.should be_false
114+
slice.valid?.should be_false
115+
-> { slice.get_string }.should raise_error(IO::Buffer::InvalidatedError, "Buffer has been invalidated!")
116+
end
117+
end
118+
end

core/io/buffer/valid_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969

7070
platform_is_not :linux do
7171
# This test does not cause a copy-resize on Linux.
72-
# It MAY cause the buffer to move, but there is no guarantee.
72+
# `#resize` MAY cause the buffer to move, but there is no guarantee.
7373
it "is false when buffer is copied on resize" do
7474
@buffer = IO::Buffer.new(4, IO::Buffer::MAPPED)
7575
slice = @buffer.slice(0, 2)

0 commit comments

Comments
 (0)