summaryrefslogtreecommitdiff
path: root/spec/ruby/library/stringio/reopen_spec.rb
blob: 6752cf9970f115dbe7ab73d9c4cd20cd009de038 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
require_relative '../../spec_helper'
require_relative 'fixtures/classes'

describe "StringIO#reopen when passed [Object, Integer]" do
  before :each do
    @io = StringIO.new("example")
  end

  it "reopens self with the passed Object in the passed mode" do
    @io.reopen("reopened", IO::RDONLY)
    @io.closed_read?.should be_false
    @io.closed_write?.should be_true
    @io.string.should == "reopened"

    @io.reopen("reopened, twice", IO::WRONLY)
    @io.closed_read?.should be_true
    @io.closed_write?.should be_false
    @io.string.should == "reopened, twice"

    @io.reopen("reopened, another time", IO::RDWR)
    @io.closed_read?.should be_false
    @io.closed_write?.should be_false
    @io.string.should == "reopened, another time"
  end

  ruby_version_is ""..."3.0" do
    # NOTE: WEIRD!
    it "does not taint self when the passed Object was tainted" do
      @io.reopen("reopened".taint, IO::RDONLY)
      @io.tainted?.should be_false

      @io.reopen("reopened".taint, IO::WRONLY)
      @io.tainted?.should be_false
    end
  end

  it "tries to convert the passed Object to a String using #to_str" do
    obj = mock("to_str")
    obj.should_receive(:to_str).and_return("to_str")
    @io.reopen(obj, IO::RDWR)
    @io.string.should == "to_str"
  end

  it "raises a TypeError when the passed Object can't be converted to a String" do
    -> { @io.reopen(Object.new, IO::RDWR) }.should raise_error(TypeError)
  end

  it "raises an Errno::EACCES when trying to reopen self with a frozen String in write-mode" do
    -> { @io.reopen("burn".freeze, IO::WRONLY) }.should raise_error(Errno::EACCES)
    -> { @io.reopen("burn".freeze, IO::WRONLY | IO::APPEND) }.should raise_error(Errno::EACCES)
  end

  it "raises a FrozenError when trying to reopen self with a frozen String in truncate-mode" do
    -> { @io.reopen("burn".freeze, IO::RDONLY | IO::TRUNC) }.should raise_error(FrozenError)
  end

  it "does not raise IOError when passed a frozen String in read-mode" do
    @io.reopen("burn".freeze, IO::RDONLY)
    @io.string.should == "burn"
  end
end

describe "StringIO#reopen when passed [Object, Object]" do
  before :each do
    @io = StringIO.new("example")
  end

  it "reopens self with the passed Object in the passed mode" do
    @io.reopen("reopened", "r")
    @io.closed_read?.should be_false
    @io.closed_write?.should be_true
    @io.string.should == "reopened"

    @io.reopen("reopened, twice", "r+")
    @io.closed_read?.should be_false
    @io.closed_write?.should be_false
    @io.string.should == "reopened, twice"

    @io.reopen("reopened, another", "w+")
    @io.closed_read?.should be_false
    @io.closed_write?.should be_false
    @io.string.should == ""

    @io.reopen("reopened, another time", "r+")
    @io.closed_read?.should be_false
    @io.closed_write?.should be_false
    @io.string.should == "reopened, another time"
  end

  it "truncates the passed String when opened in truncate mode" do
    @io.reopen(str = "reopened", "w")
    str.should == ""
  end

  ruby_version_is ""..."3.0" do
    # NOTE: WEIRD!
    it "does not taint self when the passed Object was tainted" do
      @io.reopen("reopened".taint, "r")
      @io.tainted?.should be_false

      @io.reopen("reopened".taint, "w")
      @io.tainted?.should be_false
    end
  end

  it "tries to convert the passed Object to a String using #to_str" do
    obj = mock("to_str")
    obj.should_receive(:to_str).and_return("to_str")
    @io.reopen(obj, "r")
    @io.string.should == "to_str"
  end

  it "raises a TypeError when the passed Object can't be converted to a String using #to_str" do
    -> { @io.reopen(Object.new, "r") }.should raise_error(TypeError)
  end

  it "resets self's position to 0" do
    @io.read(5)
    @io.reopen("reopened")
    @io.pos.should eql(0)
  end

  it "resets self's line number to 0" do
    @io.gets
    @io.reopen("reopened")
    @io.lineno.should eql(0)
  end

  it "tries to convert the passed mode Object to an Integer using #to_str" do
    obj = mock("to_str")
    obj.should_receive(:to_str).and_return("r")
    @io.reopen("reopened", obj)
    @io.closed_read?.should be_false
    @io.closed_write?.should be_true
    @io.string.should == "reopened"
  end

  it "raises an Errno::EACCES error when trying to reopen self with a frozen String in write-mode" do
    -> { @io.reopen("burn".freeze, 'w') }.should raise_error(Errno::EACCES)
    -> { @io.reopen("burn".freeze, 'w+') }.should raise_error(Errno::EACCES)
    -> { @io.reopen("burn".freeze, 'a') }.should raise_error(Errno::EACCES)
    -> { @io.reopen("burn".freeze, "r+") }.should raise_error(Errno::EACCES)
  end

  it "does not raise IOError if a frozen string is passed in read mode" do
    @io.reopen("burn".freeze, "r")
    @io.string.should == "burn"
  end
end

describe "StringIO#reopen when passed [String]" do
  before :each do
    @io = StringIO.new("example")
  end

  it "reopens self with the passed String in read-write mode" do
    @io.close

    @io.reopen("reopened")

    @io.closed_write?.should be_false
    @io.closed_read?.should be_false

    @io.string.should == "reopened"
  end

  ruby_version_is ""..."3.0" do
    # NOTE: WEIRD!
    it "does not taint self when the passed Object was tainted" do
      @io.reopen("reopened".taint)
      @io.tainted?.should be_false
    end
  end

  it "resets self's position to 0" do
    @io.read(5)
    @io.reopen("reopened")
    @io.pos.should eql(0)
  end

  it "resets self's line number to 0" do
    @io.gets
    @io.reopen("reopened")
    @io.lineno.should eql(0)
  end
end

describe "StringIO#reopen when passed [Object]" do
  before :each do
    @io = StringIO.new("example")
  end

  it "raises a TypeError when passed an Object that can't be converted to a StringIO" do
    -> { @io.reopen(Object.new) }.should raise_error(TypeError)
  end

  it "does not try to convert the passed Object to a String using #to_str" do
    obj = mock("not to_str")
    obj.should_not_receive(:to_str)
    -> { @io.reopen(obj) }.should raise_error(TypeError)
  end

  it "tries to convert the passed Object to a StringIO using #to_strio" do
    obj = mock("to_strio")
    obj.should_receive(:to_strio).and_return(StringIO.new("to_strio"))
    @io.reopen(obj)
    @io.string.should == "to_strio"
  end

  # NOTE: WEIRD!
  ruby_version_is ""..."2.7" do
    it "taints self when the passed Object was tainted" do
      @io.reopen(StringIO.new("reopened").taint)
      @io.tainted?.should be_true
    end
  end
end

describe "StringIO#reopen when passed no arguments" do
  before :each do
    @io = StringIO.new("example\nsecond line")
  end

  it "resets self's mode to read-write" do
    @io.close
    @io.reopen
    @io.closed_read?.should be_false
    @io.closed_write?.should be_false
  end

  it "resets self's position to 0" do
    @io.read(5)
    @io.reopen
    @io.pos.should eql(0)
  end

  it "resets self's line number to 0" do
    @io.gets
    @io.reopen
    @io.lineno.should eql(0)
  end
end

# NOTE: Some reopen specs disabled due to MRI bugs. See:
# http://rubyforge.org/tracker/index.php?func=detail&aid=13919&group_id=426&atid=1698
# for details.
describe "StringIO#reopen" do
  before :each do
    @io = StringIO.new('hello','a')
  end

  # TODO: find out if this is really a bug
  it "reopens a stream when given a String argument" do
    @io.reopen('goodbye').should == @io
    @io.string.should == 'goodbye'
    @io << 'x'
    @io.string.should == 'xoodbye'
  end

  it "reopens a stream in append mode when flagged as such" do
    @io.reopen('goodbye', 'a').should == @io
    @io.string.should == 'goodbye'
    @io << 'x'
    @io.string.should == 'goodbyex'
  end

  it "reopens and truncate when reopened in write mode" do
    @io.reopen('goodbye', 'wb').should == @io
    @io.string.should == ''
    @io << 'x'
    @io.string.should == 'x'
  end

  it "truncates the given string, not a copy" do
    str = 'goodbye'
    @io.reopen(str, 'w')
    @io.string.should == ''
    str.should == ''
  end

  ruby_version_is ""..."2.7" do
    it "taints self if the provided StringIO argument is tainted" do
      new_io = StringIO.new("tainted")
      new_io.taint
      @io.reopen(new_io)
      @io.should.tainted?
    end
  end

  it "does not truncate the content even when the StringIO argument is in the truncate mode" do
    orig_io = StringIO.new("Original StringIO", IO::RDWR|IO::TRUNC)
    orig_io.write("BLAH") # make sure the content is not empty

    @io.reopen(orig_io)
    @io.string.should == "BLAH"
  end

end