Skip to content

Commit c307df9

Browse files
committed
Add specs for IO::Buffer#free, #resize and #transfer
1 parent 2d387d9 commit c307df9

File tree

5 files changed

+367
-9
lines changed

5 files changed

+367
-9
lines changed

core/io/buffer/free_spec.rb

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
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+
buffer.set_string("test")
89+
slice.set_string("ea")
90+
slice.free
91+
slice.null?.should be_true
92+
buffer.null?.should be_false
93+
buffer.get_string.should == "east"
94+
95+
buffer.free
96+
end
97+
98+
it "nullifies buffer, invalidating the slice" do
99+
buffer = IO::Buffer.new(4)
100+
slice = buffer.slice(0, 2)
101+
buffer.free
102+
103+
slice.null?.should be_false
104+
slice.valid?.should be_false
105+
-> { slice.get_string }.should raise_error(IO::Buffer::InvalidatedError, "Buffer has been invalidated!")
106+
end
107+
end
108+
end

core/io/buffer/locked_spec.rb

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,13 @@
1313
end
1414
end
1515

16-
it "disallows freeing while locked, raising IO::Buffer::LockedError" do
16+
it "disallows operations changing buffer itself, raising IO::Buffer::LockedError" do
1717
@buffer = IO::Buffer.new(4)
1818
@buffer.locked do
19-
-> { @buffer.free }.should raise_error(IO::Buffer::LockedError, "Buffer is locked!")
20-
end
21-
end
22-
23-
it "disallows resizing while locked, raising IO::Buffer::LockedError" do
24-
@buffer = IO::Buffer.new(4)
25-
@buffer.locked do
26-
-> { @buffer.resize(8) }.should raise_error(IO::Buffer::LockedError, "Cannot resize locked buffer!")
19+
# See specs of individual methods for error messages.
20+
-> { @buffer.free }.should raise_error(IO::Buffer::LockedError)
21+
-> { @buffer.resize(8) }.should raise_error(IO::Buffer::LockedError)
22+
-> { @buffer.transfer }.should raise_error(IO::Buffer::LockedError)
2723
end
2824
end
2925

core/io/buffer/resize_spec.rb

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

0 commit comments

Comments
 (0)