Skip to content

Commit 2173a93

Browse files
committed
[GR-18163] Fix StringIO#{gets, readline} when it is called with both separator and limit
PullRequest: truffleruby/4542
2 parents a89b13f + 2c508f4 commit 2173a93

File tree

10 files changed

+392
-354
lines changed

10 files changed

+392
-354
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Compatibility:
2727
* Fix `Struct#initialize` when mixed positional and keyword arguments (#3855, @andrykonchin).
2828
* Fix `Integer.sqrt` for large values (#3872, @tompng).
2929
* Fix `Data#inspect` when data contains a recursive attribute (#3847, @andrykonchin).
30+
* Fix `StringIO#{gets,readline}` when it is called with both separator and limit to truncate the separator if the limit is exceeded (#3856, @andrykonchin).
3031

3132
Performance:
3233

lib/truffle/stringio.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -779,7 +779,7 @@ def yaml_initialize(type, val)
779779
end
780780
else
781781
if stop = Primitive.find_string(string, sep, pos)
782-
if limit && stop - pos >= limit
782+
if limit && stop - pos + sep.bytesize >= limit
783783
stop = pos + limit
784784
else
785785
stop += sep.bytesize

spec/ruby/library/stringio/each_line_spec.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,7 @@
2121
describe "StringIO#each_line when passed limit" do
2222
it_behaves_like :stringio_each_limit, :each_line
2323
end
24+
25+
describe "StringIO#each when passed separator and limit" do
26+
it_behaves_like :stringio_each_separator_and_limit, :each_line
27+
end

spec/ruby/library/stringio/each_spec.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,7 @@
2525
describe "StringIO#each when passed limit" do
2626
it_behaves_like :stringio_each_limit, :each
2727
end
28+
29+
describe "StringIO#each when passed separator and limit" do
30+
it_behaves_like :stringio_each_separator_and_limit, :each
31+
end
Lines changed: 37 additions & 226 deletions
Original file line numberDiff line numberDiff line change
@@ -1,250 +1,61 @@
11
require_relative '../../spec_helper'
22
require "stringio"
3+
require_relative "shared/gets"
34

4-
describe "StringIO#gets when passed [separator]" do
5-
before :each do
6-
@io = StringIO.new("this>is>an>example")
7-
end
8-
9-
it "returns the data read till the next occurrence of the passed separator" do
10-
@io.gets(">").should == "this>"
11-
@io.gets(">").should == "is>"
12-
@io.gets(">").should == "an>"
13-
@io.gets(">").should == "example"
14-
end
15-
16-
it "sets $_ to the read content" do
17-
@io.gets(">")
18-
$_.should == "this>"
19-
@io.gets(">")
20-
$_.should == "is>"
21-
@io.gets(">")
22-
$_.should == "an>"
23-
@io.gets(">")
24-
$_.should == "example"
25-
@io.gets(">")
26-
$_.should be_nil
27-
end
28-
29-
it "accepts string as separator" do
30-
@io.gets("is>")
31-
$_.should == "this>"
32-
@io.gets("an>")
33-
$_.should == "is>an>"
34-
@io.gets("example")
35-
$_.should == "example"
36-
@io.gets("ple")
37-
$_.should be_nil
38-
end
39-
40-
it "updates self's lineno by one" do
41-
@io.gets(">")
42-
@io.lineno.should eql(1)
43-
44-
@io.gets(">")
45-
@io.lineno.should eql(2)
46-
47-
@io.gets(">")
48-
@io.lineno.should eql(3)
49-
end
50-
51-
it "returns the next paragraph when the passed separator is an empty String" do
52-
io = StringIO.new("this is\n\nan example")
53-
io.gets("").should == "this is\n\n"
54-
io.gets("").should == "an example"
55-
end
56-
57-
it "returns the remaining content starting at the current position when passed nil" do
58-
io = StringIO.new("this is\n\nan example")
59-
io.pos = 5
60-
io.gets(nil).should == "is\n\nan example"
61-
end
5+
describe "StringIO#gets" do
6+
describe "when passed [separator]" do
7+
it_behaves_like :stringio_gets_separator, :gets
628

63-
it "tries to convert the passed separator to a String using #to_str" do
64-
obj = mock('to_str')
65-
obj.should_receive(:to_str).and_return(">")
66-
@io.gets(obj).should == "this>"
67-
end
68-
end
69-
70-
describe "StringIO#gets when passed no argument" do
71-
before :each do
72-
@io = StringIO.new("this is\nan example\nfor StringIO#gets")
73-
end
9+
it "returns nil if self is at the end" do
10+
@io = StringIO.new("this>is>an>example")
7411

75-
it "returns the data read till the next occurrence of $/ or till eof" do
76-
@io.gets.should == "this is\n"
77-
78-
begin
79-
old_sep = $/
80-
suppress_warning {$/ = " "}
81-
@io.gets.should == "an "
82-
@io.gets.should == "example\nfor "
83-
@io.gets.should == "StringIO#gets"
84-
ensure
85-
suppress_warning {$/ = old_sep}
12+
@io.pos = 36
13+
@io.gets(">").should be_nil
14+
@io.gets(">").should be_nil
8615
end
8716
end
8817

89-
it "sets $_ to the read content" do
90-
@io.gets
91-
$_.should == "this is\n"
92-
@io.gets
93-
$_.should == "an example\n"
94-
@io.gets
95-
$_.should == "for StringIO#gets"
96-
@io.gets
97-
$_.should be_nil
98-
end
99-
100-
it "updates self's position" do
101-
@io.gets
102-
@io.pos.should eql(8)
103-
104-
@io.gets
105-
@io.pos.should eql(19)
106-
107-
@io.gets
108-
@io.pos.should eql(36)
109-
end
110-
111-
it "updates self's lineno" do
112-
@io.gets
113-
@io.lineno.should eql(1)
114-
115-
@io.gets
116-
@io.lineno.should eql(2)
117-
118-
@io.gets
119-
@io.lineno.should eql(3)
120-
end
121-
122-
it "returns nil if self is at the end" do
123-
@io.pos = 36
124-
@io.gets.should be_nil
125-
@io.gets.should be_nil
126-
end
127-
end
128-
129-
describe "StringIO#gets when passed [limit]" do
130-
before :each do
131-
@io = StringIO.new("this>is>an>example")
132-
end
133-
134-
it "returns the data read until the limit is met" do
135-
@io.gets(4).should == "this"
136-
@io.gets(3).should == ">is"
137-
@io.gets(5).should == ">an>e"
138-
@io.gets(6).should == "xample"
139-
end
140-
141-
it "sets $_ to the read content" do
142-
@io.gets(4)
143-
$_.should == "this"
144-
@io.gets(3)
145-
$_.should == ">is"
146-
@io.gets(5)
147-
$_.should == ">an>e"
148-
@io.gets(6)
149-
$_.should == "xample"
150-
@io.gets(3)
151-
$_.should be_nil
152-
end
153-
154-
it "updates self's lineno by one" do
155-
@io.gets(3)
156-
@io.lineno.should eql(1)
157-
158-
@io.gets(3)
159-
@io.lineno.should eql(2)
160-
161-
@io.gets(3)
162-
@io.lineno.should eql(3)
163-
end
164-
165-
it "tries to convert the passed limit to an Integer using #to_int" do
166-
obj = mock('to_int')
167-
obj.should_receive(:to_int).and_return(4)
168-
@io.gets(obj).should == "this"
169-
end
170-
171-
it "returns a blank string when passed a limit of 0" do
172-
@io.gets(0).should == ""
173-
end
174-
175-
it "ignores it when passed a negative limit" do
176-
@io.gets(-4).should == "this>is>an>example"
177-
end
178-
end
18+
describe "when passed [limit]" do
19+
it_behaves_like :stringio_gets_limit, :gets
17920

180-
describe "StringIO#gets when passed [separator] and [limit]" do
181-
before :each do
182-
@io = StringIO.new("this>is>an>example")
183-
end
184-
185-
it "returns the data read until the limit is consumed or the separator is met" do
186-
@io.gets('>', 8).should == "this>"
187-
@io.gets('>', 2).should == "is"
188-
@io.gets('>', 10).should == ">"
189-
@io.gets('>', 6).should == "an>"
190-
@io.gets('>', 5).should == "examp"
191-
end
21+
it "returns nil if self is at the end" do
22+
@io = StringIO.new("this>is>an>example")
19223

193-
it "sets $_ to the read content" do
194-
@io.gets('>', 8)
195-
$_.should == "this>"
196-
@io.gets('>', 2)
197-
$_.should == "is"
198-
@io.gets('>', 10)
199-
$_.should == ">"
200-
@io.gets('>', 6)
201-
$_.should == "an>"
202-
@io.gets('>', 5)
203-
$_.should == "examp"
24+
@io.pos = 36
25+
@io.gets(3).should be_nil
26+
@io.gets(3).should be_nil
27+
end
20428
end
20529

206-
it "updates self's lineno by one" do
207-
@io.gets('>', 3)
208-
@io.lineno.should eql(1)
30+
describe "when passed [separator] and [limit]" do
31+
it_behaves_like :stringio_gets_separator_and_limit, :gets
20932

210-
@io.gets('>', 3)
211-
@io.lineno.should eql(2)
33+
it "returns nil if self is at the end" do
34+
@io = StringIO.new("this>is>an>example")
21235

213-
@io.gets('>', 3)
214-
@io.lineno.should eql(3)
36+
@io.pos = 36
37+
@io.gets(">", 3).should be_nil
38+
@io.gets(">", 3).should be_nil
39+
end
21540
end
21641

217-
it "tries to convert the passed separator to a String using #to_str" do
218-
obj = mock('to_str')
219-
obj.should_receive(:to_str).and_return('>')
220-
@io.gets(obj, 5).should == "this>"
221-
end
42+
describe "when passed no argument" do
43+
it_behaves_like :stringio_gets_no_argument, :gets
22244

223-
it "does not raise TypeError if passed separator is nil" do
224-
@io.gets(nil, 5).should == "this>"
225-
end
45+
it "returns nil if self is at the end" do
46+
@io = StringIO.new("this>is>an>example")
22647

227-
it "tries to convert the passed limit to an Integer using #to_int" do
228-
obj = mock('to_int')
229-
obj.should_receive(:to_int).and_return(5)
230-
@io.gets('>', obj).should == "this>"
48+
@io.pos = 36
49+
@io.gets.should be_nil
50+
@io.gets.should be_nil
51+
end
23152
end
232-
end
233-
234-
describe "StringIO#gets when in write-only mode" do
235-
it "raises an IOError" do
236-
io = StringIO.new(+"xyz", "w")
237-
-> { io.gets }.should raise_error(IOError)
23853

239-
io = StringIO.new("xyz")
240-
io.close_read
241-
-> { io.gets }.should raise_error(IOError)
54+
describe "when passed [chomp]" do
55+
it_behaves_like :stringio_gets_chomp, :gets
24256
end
243-
end
24457

245-
describe "StringIO#gets when passed [chomp]" do
246-
it "returns the data read without a trailing newline character" do
247-
io = StringIO.new("this>is>an>example\n")
248-
io.gets(chomp: true).should == "this>is>an>example"
58+
describe "when in write-only mode" do
59+
it_behaves_like :stringio_gets_write_only, :gets
24960
end
25061
end

0 commit comments

Comments
 (0)