filter_io 0.1.6 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -1
- data/Gemfile +2 -6
- data/README.markdown +37 -31
- data/Rakefile +4 -52
- data/filter_io.gemspec +26 -0
- data/lib/filter_io/version.rb +3 -0
- data/lib/filter_io.rb +108 -104
- data/spec/filter_io_spec.rb +801 -0
- data/spec/spec_helper.rb +6 -0
- metadata +114 -56
- data/Gemfile.lock +0 -24
- data/test/filter_io_test.rb +0 -777
- data/test/test_helper.rb +0 -6
data/test/filter_io_test.rb
DELETED
@@ -1,777 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
|
3
|
-
require 'test_helper'
|
4
|
-
require 'stringio'
|
5
|
-
require 'tempfile'
|
6
|
-
require 'zlib'
|
7
|
-
|
8
|
-
class FilterIOTest < ActiveSupport::TestCase
|
9
|
-
|
10
|
-
def assert_equal_reference_io(input)
|
11
|
-
|
12
|
-
expected_io = StringIO.new(input)
|
13
|
-
actual_io = FilterIO.new(StringIO.new(input))
|
14
|
-
|
15
|
-
results = [expected_io, actual_io].map do |io|
|
16
|
-
results = []
|
17
|
-
errors = []
|
18
|
-
positions = []
|
19
|
-
|
20
|
-
# call the block repeatedly until we get to EOF
|
21
|
-
# and once more at the end to check what happens at EOF
|
22
|
-
one_more_time = [true]
|
23
|
-
while !io.eof? || one_more_time.pop
|
24
|
-
begin
|
25
|
-
results << yield(io)
|
26
|
-
errors << nil
|
27
|
-
rescue Exception => e
|
28
|
-
results << nil
|
29
|
-
errors << [e.class, e.message]
|
30
|
-
end
|
31
|
-
positions << io.pos
|
32
|
-
raise 'Too many iterations' if results.size > 100
|
33
|
-
end
|
34
|
-
|
35
|
-
[results, errors, positions]
|
36
|
-
end
|
37
|
-
|
38
|
-
# compare the filtered output against the reference
|
39
|
-
results[0].zip(results[1]).each do |expected, actual|
|
40
|
-
assert_equal expected, actual
|
41
|
-
end
|
42
|
-
|
43
|
-
end
|
44
|
-
|
45
|
-
test "empty source" do
|
46
|
-
io = FilterIO.new(StringIO.new(''))
|
47
|
-
assert_true io.bof?
|
48
|
-
io = FilterIO.new(StringIO.new(''))
|
49
|
-
assert_true io.eof?
|
50
|
-
io = FilterIO.new(StringIO.new(''))
|
51
|
-
assert_raise EOFError do
|
52
|
-
io.readchar
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
test "simple eof" do
|
57
|
-
io = FilterIO.new(StringIO.new('x'))
|
58
|
-
assert_false io.eof?
|
59
|
-
assert_equal 'x', io.readchar.chr
|
60
|
-
assert_true io.eof?
|
61
|
-
assert_equal '', io.read
|
62
|
-
assert_equal nil, io.read(8)
|
63
|
-
end
|
64
|
-
|
65
|
-
test "simple bof" do
|
66
|
-
io = FilterIO.new(StringIO.new('x'))
|
67
|
-
assert_true io.bof?
|
68
|
-
assert_equal 'x', io.readchar.chr
|
69
|
-
assert_false io.bof?
|
70
|
-
end
|
71
|
-
|
72
|
-
test "unicode readchar" do
|
73
|
-
assert_equal_reference_io('Résume') { |io| io.readchar }
|
74
|
-
end
|
75
|
-
|
76
|
-
test "unicode read" do
|
77
|
-
(1..3).each do |read_size|
|
78
|
-
assert_equal_reference_io('Résume') { |io| io.read read_size }
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
test "unicode read all" do
|
83
|
-
assert_equal_reference_io('Résume') { |io| io.read }
|
84
|
-
end
|
85
|
-
|
86
|
-
test "unicode gets" do
|
87
|
-
assert_equal_reference_io("über\nrésumé") { |io| io.gets }
|
88
|
-
end
|
89
|
-
|
90
|
-
test "unicode in block" do
|
91
|
-
input = 'Résumé Test'
|
92
|
-
expected = 'résumé test'
|
93
|
-
[2, nil].each do |block_size|
|
94
|
-
io = FilterIO.new(StringIO.new(input), :block_size => block_size) { |data| data.downcase }
|
95
|
-
actual = io.read
|
96
|
-
assert_equal expected, actual
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
test "should not buffer forever on bad encoding" do
|
101
|
-
input = "123\xc3\xc34567890"
|
102
|
-
block_count = 0
|
103
|
-
io = FilterIO.new(StringIO.new(input), :block_size => 2) do |data|
|
104
|
-
block_count += 1
|
105
|
-
assert_operator data.size, :<=, 6
|
106
|
-
data
|
107
|
-
end
|
108
|
-
actual = io.read
|
109
|
-
if input.respond_to? :force_encoding
|
110
|
-
input.force_encoding 'ASCII-8BIT'
|
111
|
-
actual.force_encoding 'ASCII-8BIT'
|
112
|
-
end
|
113
|
-
assert_equal input, actual
|
114
|
-
assert_operator block_count, :>=, 3
|
115
|
-
end
|
116
|
-
|
117
|
-
if IO.method_defined? :external_encoding
|
118
|
-
|
119
|
-
def with_iso8859_1_test_file(internal_encoding)
|
120
|
-
Tempfile.open 'filter_io' do |tempfile|
|
121
|
-
File.open(tempfile.path, 'wb') do |io|
|
122
|
-
io.write "\xFCber\nR\xE9sum\xE9"
|
123
|
-
end
|
124
|
-
File.open(tempfile.path, :external_encoding => 'ISO-8859-1', :internal_encoding => internal_encoding) do |io|
|
125
|
-
yield io
|
126
|
-
end
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
test "ISO-8859-1 sanity check to UTF-8" do
|
131
|
-
with_iso8859_1_test_file 'UTF-8' do |io_raw|
|
132
|
-
assert_equal 'ü', io_raw.readchar
|
133
|
-
assert_equal "ber\n", io_raw.gets
|
134
|
-
str = io_raw.gets
|
135
|
-
assert_equal 'résumé', str.downcase
|
136
|
-
assert_equal 'UTF-8', str.encoding.name
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
test "ISO-8859-1 sanity check raw" do
|
141
|
-
with_iso8859_1_test_file nil do |io_raw|
|
142
|
-
assert_equal 'ü'.encode('ISO-8859-1'), io_raw.readchar
|
143
|
-
assert_equal "ber\n", io_raw.gets
|
144
|
-
str = io_raw.gets
|
145
|
-
assert_equal 'résumé'.encode('ISO-8859-1'), str.downcase
|
146
|
-
assert_equal 'ISO-8859-1', str.encoding.name
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
test "iso-8859-1 readchar to UTF-8" do
|
151
|
-
with_iso8859_1_test_file 'UTF-8' do |io_raw|
|
152
|
-
io = FilterIO.new(io_raw)
|
153
|
-
"über\n".chars.each do |expected|
|
154
|
-
actual = io.readchar
|
155
|
-
assert_equal expected, actual
|
156
|
-
assert_equal 'UTF-8', actual.encoding.name
|
157
|
-
end
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
test "iso-8859-1 readchar raw" do
|
162
|
-
with_iso8859_1_test_file nil do |io_raw|
|
163
|
-
io = FilterIO.new(io_raw)
|
164
|
-
"über\n".encode('ISO-8859-1').chars.each do |expected|
|
165
|
-
actual = io.readchar
|
166
|
-
assert_equal expected, actual
|
167
|
-
assert_equal 'ISO-8859-1', actual.encoding.name
|
168
|
-
end
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
test "iso-8859-1 read to UTF-8" do
|
173
|
-
with_iso8859_1_test_file 'UTF-8' do |io_raw|
|
174
|
-
io = FilterIO.new(io_raw)
|
175
|
-
assert_equal 'ü'.force_encoding('ASCII-8BIT'), io.read(2)
|
176
|
-
assert_equal 'ASCII-8BIT', io.read(2).encoding.name
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
test "iso-8859-1 read raw" do
|
181
|
-
with_iso8859_1_test_file nil do |io_raw|
|
182
|
-
io = FilterIO.new(io_raw)
|
183
|
-
assert_equal 'ü'.encode('ISO-8859-1').force_encoding('ASCII-8BIT'), io.read(1)
|
184
|
-
assert_equal 'ASCII-8BIT', io.read(2).encoding.name
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
|
-
test "iso-8859-1 lines to UTF-8" do
|
189
|
-
with_iso8859_1_test_file 'UTF-8' do |io_raw|
|
190
|
-
io = FilterIO.new(io_raw)
|
191
|
-
expected = ["über\n", 'Résumé']
|
192
|
-
actual = io.lines.to_a
|
193
|
-
assert_equal expected, actual
|
194
|
-
assert_equal 'UTF-8', actual[0].encoding.name
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
test "iso-8859-1 lines raw" do
|
199
|
-
with_iso8859_1_test_file nil do |io_raw|
|
200
|
-
io = FilterIO.new(io_raw)
|
201
|
-
expected = ["über\n", 'Résumé'].map { |str| str.encode('ISO-8859-1') }
|
202
|
-
actual = io.lines.to_a
|
203
|
-
assert_equal expected, actual
|
204
|
-
assert_equal 'ISO-8859-1', actual[0].encoding.name
|
205
|
-
end
|
206
|
-
end
|
207
|
-
|
208
|
-
test "iso-8859-1 block to UTF-8" do
|
209
|
-
[1, 2, nil].each do |block_size|
|
210
|
-
expected = "über\nrésumé"
|
211
|
-
with_iso8859_1_test_file 'UTF-8' do |io_raw|
|
212
|
-
io = FilterIO.new(io_raw, :block_size => block_size) do |data, state|
|
213
|
-
assert_equal 'ü', data[0] if state.bof?
|
214
|
-
assert_equal 'UTF-8', data.encoding.name
|
215
|
-
data.downcase
|
216
|
-
end
|
217
|
-
assert_equal 'ü', io.readchar
|
218
|
-
assert_equal 'UTF-8', io.gets.encoding.name
|
219
|
-
assert_equal 'rés'.force_encoding('ASCII-8BIT'), io.read(4)
|
220
|
-
str = io.gets
|
221
|
-
assert_equal 'umé', str
|
222
|
-
assert_equal 'UTF-8', str.encoding.name
|
223
|
-
end
|
224
|
-
end
|
225
|
-
end
|
226
|
-
|
227
|
-
test "iso-8859-1 block raw" do
|
228
|
-
[1, 2, nil].each do |block_size|
|
229
|
-
expected = "über\nrésumé".encode('ISO-8859-1')
|
230
|
-
with_iso8859_1_test_file 'ISO-8859-1' do |io_raw|
|
231
|
-
io = FilterIO.new(io_raw, :block_size => block_size) do |data, state|
|
232
|
-
assert_equal 'ü'.encode('ISO-8859-1'), data[0] if state.bof?
|
233
|
-
assert_equal 'ISO-8859-1', data.encoding.name
|
234
|
-
data.downcase
|
235
|
-
end
|
236
|
-
assert_equal 'ü'.encode('ISO-8859-1'), io.readchar
|
237
|
-
assert_equal 'ISO-8859-1', io.gets.encoding.name
|
238
|
-
assert_equal 'rés'.encode('ISO-8859-1').force_encoding('ASCII-8BIT'), io.read(3)
|
239
|
-
str = io.gets
|
240
|
-
assert_equal 'umé'.encode('ISO-8859-1'), str
|
241
|
-
assert_equal 'ISO-8859-1', str.encoding.name
|
242
|
-
end
|
243
|
-
end
|
244
|
-
end
|
245
|
-
|
246
|
-
test "block returning mix of UTF-8 and ASCII-8BIT" do
|
247
|
-
input = "X\xE2\x80\x94Y\xe2\x80\x99"
|
248
|
-
input.force_encoding 'ASCII-8BIT'
|
249
|
-
io = FilterIO.new(StringIO.new(input), :block_size => 4) do |data, state|
|
250
|
-
data.force_encoding data[0] == 'Y' ? 'UTF-8' : 'ASCII-8BIT'
|
251
|
-
data
|
252
|
-
end
|
253
|
-
assert_equal input, io.read
|
254
|
-
end
|
255
|
-
|
256
|
-
end
|
257
|
-
|
258
|
-
test "read" do
|
259
|
-
input = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit'
|
260
|
-
io_reference = StringIO.new(input)
|
261
|
-
io = FilterIO.new(StringIO.new(input))
|
262
|
-
[10,5,4,8,7,nil,nil].each do |read_len|
|
263
|
-
assert_equal io_reference.read(read_len), io.read(read_len)
|
264
|
-
assert_equal io_reference.pos, io.pos
|
265
|
-
if read_len
|
266
|
-
assert_equal io_reference.readchar, io.readchar
|
267
|
-
else
|
268
|
-
assert_raise(EOFError) { io_reference.readchar }
|
269
|
-
assert_raise(EOFError) { io.readchar }
|
270
|
-
end
|
271
|
-
assert_equal io_reference.pos, io.pos
|
272
|
-
assert_equal io_reference.eof?, io.eof?
|
273
|
-
end
|
274
|
-
assert_equal io_reference.read, io.read
|
275
|
-
assert_equal io_reference.read(4), io.read(4)
|
276
|
-
assert_true io_reference.eof?
|
277
|
-
assert_true io.eof?
|
278
|
-
end
|
279
|
-
|
280
|
-
test "read zero before eof" do
|
281
|
-
io = FilterIO.new(StringIO.new('foo'))
|
282
|
-
assert_equal '', io.read(0)
|
283
|
-
assert_equal 0, io.pos
|
284
|
-
assert_false io.eof?
|
285
|
-
end
|
286
|
-
|
287
|
-
test "read zero at eof" do
|
288
|
-
io = FilterIO.new(StringIO.new(''))
|
289
|
-
assert_equal '', io.read(0)
|
290
|
-
assert_equal 0, io.pos
|
291
|
-
assert_true io.eof?
|
292
|
-
end
|
293
|
-
|
294
|
-
test "read negative" do
|
295
|
-
io = FilterIO.new(StringIO.new('foo'))
|
296
|
-
assert_equal 'fo', io.read(2)
|
297
|
-
assert_raise ArgumentError do
|
298
|
-
io.read(-1)
|
299
|
-
end
|
300
|
-
assert_equal 2, io.pos
|
301
|
-
end
|
302
|
-
|
303
|
-
test "simple block" do
|
304
|
-
input = 'foo bar'
|
305
|
-
expected = 'FOO BAR'
|
306
|
-
io = FilterIO.new(StringIO.new(input)) do |data|
|
307
|
-
data.upcase
|
308
|
-
end
|
309
|
-
assert_equal expected, io.read
|
310
|
-
end
|
311
|
-
|
312
|
-
test "block bof and eof" do
|
313
|
-
input = "Test String"
|
314
|
-
expected = ">>>*Test** Str**ing*<<<"
|
315
|
-
io = FilterIO.new(StringIO.new(input), :block_size => 4) do |data, state|
|
316
|
-
data = "*#{data}*"
|
317
|
-
data = ">>>#{data}" if state.bof?
|
318
|
-
data = "#{data}<<<" if state.eof?
|
319
|
-
data
|
320
|
-
end
|
321
|
-
assert_equal expected, io.read
|
322
|
-
end
|
323
|
-
|
324
|
-
test "mutate before NeedMoreData shouldn't affect next block call" do
|
325
|
-
input = "foobar"
|
326
|
-
expected = [
|
327
|
-
['fo', true],
|
328
|
-
['foob', true],
|
329
|
-
['ar', false],
|
330
|
-
]
|
331
|
-
actual = []
|
332
|
-
io = FilterIO.new(StringIO.new(input), :block_size => 2) do |data, state|
|
333
|
-
actual << [data.dup, state.bof?]
|
334
|
-
data.upcase!
|
335
|
-
raise FilterIO::NeedMoreData if data == 'FO'
|
336
|
-
data
|
337
|
-
end
|
338
|
-
assert_equal input.upcase, io.read
|
339
|
-
assert_equal expected, actual
|
340
|
-
end
|
341
|
-
|
342
|
-
test "Symbol#to_proc" do
|
343
|
-
input = 'foo bar'
|
344
|
-
expected = 'FOO BAR'
|
345
|
-
io = FilterIO.new StringIO.new(input), &:upcase
|
346
|
-
assert_equal expected, io.read
|
347
|
-
end
|
348
|
-
|
349
|
-
test "block size for read(nil)" do
|
350
|
-
[1,4,7,9,13,30].each do |block_size|
|
351
|
-
input = ('A'..'Z').to_a.join
|
352
|
-
expected = input.chars.enum_for(:each_slice, block_size).to_a.map(&:join).map { |x| "[#{x}]" }.join
|
353
|
-
io = FilterIO.new(StringIO.new(input), :block_size => block_size) do |data|
|
354
|
-
"[#{data}]"
|
355
|
-
end
|
356
|
-
assert_equal expected, io.read
|
357
|
-
end
|
358
|
-
end
|
359
|
-
|
360
|
-
test "block size for gets/readline" do
|
361
|
-
[1,4,7,9,13,30].each do |block_size|
|
362
|
-
input = "ABCDEFG\nHJIKLMNOP\n"
|
363
|
-
expected = input.chars.enum_for(:each_slice, block_size).to_a.map(&:join).map { |x| "[#{x}]" }.join.lines.to_a
|
364
|
-
io = FilterIO.new(StringIO.new(input), :block_size => block_size) do |data|
|
365
|
-
"[#{data}]"
|
366
|
-
end
|
367
|
-
actual = io.readlines
|
368
|
-
assert_equal expected, actual
|
369
|
-
end
|
370
|
-
end
|
371
|
-
|
372
|
-
test "block size different to read size" do
|
373
|
-
(1..5).each do |block_size|
|
374
|
-
input_str = ('A'..'Z').to_a.join
|
375
|
-
expected_str = input_str.chars.enum_for(:each_slice, block_size).map { |x| "[#{x.join}]" }.join
|
376
|
-
(1..5).each do |read_size|
|
377
|
-
|
378
|
-
expected = StringIO.new(expected_str)
|
379
|
-
actual = FilterIO.new(StringIO.new(input_str), :block_size => block_size) do |data|
|
380
|
-
"[#{data}]"
|
381
|
-
end
|
382
|
-
|
383
|
-
until expected.eof?
|
384
|
-
assert_equal expected.read(read_size), actual.read(read_size)
|
385
|
-
assert_equal expected.pos, actual.pos
|
386
|
-
end
|
387
|
-
assert_equal expected.eof?, actual.eof?
|
388
|
-
|
389
|
-
end
|
390
|
-
end
|
391
|
-
end
|
392
|
-
|
393
|
-
test "rewind pass through" do
|
394
|
-
io = FilterIO.new(StringIO.new('foo bar baz'))
|
395
|
-
assert_equal 'foo b', io.read(5)
|
396
|
-
assert_equal 'ar b', io.read(4)
|
397
|
-
io.rewind
|
398
|
-
assert_equal 'foo', io.read(3)
|
399
|
-
assert_equal ' ', io.readchar.chr
|
400
|
-
io.rewind
|
401
|
-
assert_equal 'f', io.readchar.chr
|
402
|
-
assert_equal 'oo', io.read(2)
|
403
|
-
end
|
404
|
-
|
405
|
-
test "rewind resets buffer" do
|
406
|
-
str = 'foobar'
|
407
|
-
io = FilterIO.new(StringIO.new(str))
|
408
|
-
assert_equal 'foo', io.read(3)
|
409
|
-
str.replace 'FooBar'
|
410
|
-
assert_equal 'Bar', io.read(3)
|
411
|
-
io.rewind
|
412
|
-
assert_equal 'Foo', io.read(3)
|
413
|
-
end
|
414
|
-
|
415
|
-
test "rewind with block" do
|
416
|
-
input = 'abcdefghij'
|
417
|
-
expected = input[1..-1]
|
418
|
-
io = FilterIO.new(StringIO.new(input), :block_size => 4) do |data, state|
|
419
|
-
data = data[1..-1] if state.bof?
|
420
|
-
data
|
421
|
-
end
|
422
|
-
assert_equal 'bc', io.read(2)
|
423
|
-
assert_equal 'defg', io.read(4)
|
424
|
-
io.rewind
|
425
|
-
assert_equal 'bc', io.read(2)
|
426
|
-
assert_equal 'defg', io.read(4)
|
427
|
-
end
|
428
|
-
|
429
|
-
test "ungetc" do
|
430
|
-
input = 'foobar'
|
431
|
-
io = FilterIO.new(StringIO.new(input))
|
432
|
-
assert_equal 'foo', io.read(3)
|
433
|
-
io.ungetc 'x'
|
434
|
-
io.ungetc 'y'[0].ord
|
435
|
-
assert_equal 'yxb', io.read(3)
|
436
|
-
(1..5).each do |i|
|
437
|
-
io.ungetc i.to_s
|
438
|
-
end
|
439
|
-
assert_equal '54321ar', io.read
|
440
|
-
assert_equal 'foobar', input
|
441
|
-
end
|
442
|
-
|
443
|
-
test "need more data" do
|
444
|
-
input = '1ab123456cde78f9ghij0'
|
445
|
-
expected = input.gsub(/\d+/, '[\0]')
|
446
|
-
(1..5).each do |block_size|
|
447
|
-
expected_size = 0
|
448
|
-
io = FilterIO.new(StringIO.new(input), :block_size => block_size) do |data, state|
|
449
|
-
expected_size += block_size
|
450
|
-
raise FilterIO::NeedMoreData if data =~ /\d\z/ && !state.eof?
|
451
|
-
assert_equal expected_size, data.size unless state.eof?
|
452
|
-
expected_size = 0
|
453
|
-
data.gsub(/\d+/, '[\0]')
|
454
|
-
end
|
455
|
-
assert_equal expected, io.read
|
456
|
-
end
|
457
|
-
end
|
458
|
-
|
459
|
-
test "line ending normalisation" do
|
460
|
-
input = "This\r\nis\r\ra\n\ntest\n\r\n\nstring\r\r\n.\n"
|
461
|
-
expected = "This\nis\n\na\n\ntest\n\n\nstring\n\n.\n"
|
462
|
-
(1..5).each do |block_size|
|
463
|
-
io = FilterIO.new(StringIO.new(input), :block_size => block_size) do |data, state|
|
464
|
-
raise FilterIO::NeedMoreData if data =~ /[\r\n]\z/ && !state.eof?
|
465
|
-
data.gsub(/\r\n|\r|\n/, "\n")
|
466
|
-
end
|
467
|
-
assert_equal expected, io.read
|
468
|
-
end
|
469
|
-
end
|
470
|
-
|
471
|
-
test "dropping characters" do
|
472
|
-
input = "ab1cde23f1g4hijklmno567pqr8stu9vw0xyz"
|
473
|
-
expected = input.gsub(/\d+/, '')
|
474
|
-
(1..5).each do |block_size|
|
475
|
-
io = FilterIO.new(StringIO.new(input), :block_size => block_size) do |data|
|
476
|
-
data.gsub(/\d+/, '')
|
477
|
-
end
|
478
|
-
assert_equal 0, io.pos
|
479
|
-
assert_equal expected, io.read
|
480
|
-
assert_equal expected.size, io.pos
|
481
|
-
end
|
482
|
-
end
|
483
|
-
|
484
|
-
test "getc" do
|
485
|
-
assert_equal_reference_io('foo') { |io| io.getc }
|
486
|
-
end
|
487
|
-
|
488
|
-
test "gets default" do
|
489
|
-
[
|
490
|
-
"",
|
491
|
-
"x",
|
492
|
-
"foo bar",
|
493
|
-
"foo\nbar",
|
494
|
-
"foo\nbar\nbaz\n"
|
495
|
-
].each do |input|
|
496
|
-
assert_equal_reference_io(input) { |io| io.gets }
|
497
|
-
end
|
498
|
-
end
|
499
|
-
|
500
|
-
test "gets all" do
|
501
|
-
[
|
502
|
-
"",
|
503
|
-
"x",
|
504
|
-
"foo bar",
|
505
|
-
"foo\nbar",
|
506
|
-
"foo\nbar\nbaz\n"
|
507
|
-
].each do |input|
|
508
|
-
assert_equal_reference_io(input) { |io| io.gets(nil) }
|
509
|
-
end
|
510
|
-
end
|
511
|
-
|
512
|
-
test "gets separator" do
|
513
|
-
[
|
514
|
-
"",
|
515
|
-
"x",
|
516
|
-
"foo\nbar\rbaz\n",
|
517
|
-
"abc\rdef\rghi\r",
|
518
|
-
"abcxyz",
|
519
|
-
].each do |input|
|
520
|
-
["\r", "x"].each do |sep_string|
|
521
|
-
assert_equal_reference_io(input) { |io| io.gets(sep_string) }
|
522
|
-
end
|
523
|
-
end
|
524
|
-
end
|
525
|
-
|
526
|
-
test "gets 2 char separator" do
|
527
|
-
["o", "oo"].each do |sep_string|
|
528
|
-
assert_equal_reference_io("foobarhelloworld") { |io| io.gets(sep_string) }
|
529
|
-
end
|
530
|
-
end
|
531
|
-
|
532
|
-
test "gets paragraph" do
|
533
|
-
{
|
534
|
-
"" => [],
|
535
|
-
"x" => ['x'],
|
536
|
-
"foo bar" => ["foo bar"],
|
537
|
-
"foo bar\n" => ["foo bar\n"],
|
538
|
-
"foo bar\n\n" => ["foo bar\n\n"],
|
539
|
-
"foo bar\n\n\n" => ["foo bar\n\n"],
|
540
|
-
"foo bar\nbaz" => ["foo bar\nbaz"],
|
541
|
-
"foo bar\n\nbaz" => ["foo bar\n\n", "baz"],
|
542
|
-
"foo bar\n\n\nbaz" => ["foo bar\n\n", "baz"],
|
543
|
-
"foo bar\n\nbaz\n" => ["foo bar\n\n", "baz\n"],
|
544
|
-
"foo bar\n\nbaz\n\n" => ["foo bar\n\n", "baz\n\n"],
|
545
|
-
"foo bar\n\nbaz\n\n\n" => ["foo bar\n\n", "baz\n\n"],
|
546
|
-
"\n\n\nfoo bar\n\nbaz\n\n\nabc\ndef" => ["foo bar\n\n", "baz\n\n", "abc\ndef"],
|
547
|
-
}.each do |input, expected|
|
548
|
-
io = FilterIO.new(StringIO.new(input))
|
549
|
-
actual = []
|
550
|
-
while para = io.gets('')
|
551
|
-
actual << para
|
552
|
-
end
|
553
|
-
assert_equal expected, actual
|
554
|
-
end
|
555
|
-
end
|
556
|
-
|
557
|
-
test "readline" do
|
558
|
-
[
|
559
|
-
"foo\nbar\n",
|
560
|
-
"foo\nbar\nbaz"
|
561
|
-
].each do |input|
|
562
|
-
assert_equal_reference_io(input) { |io| io.readline }
|
563
|
-
assert_equal_reference_io(input) { |io| io.readline("o") }
|
564
|
-
end
|
565
|
-
end
|
566
|
-
|
567
|
-
test "readlines" do
|
568
|
-
[
|
569
|
-
"foo\nbar\n",
|
570
|
-
"foo\nbar\nbaz"
|
571
|
-
].each do |input|
|
572
|
-
assert_equal_reference_io(input) { |io| io.readlines }
|
573
|
-
assert_equal_reference_io(input) { |io| io.readlines("o") }
|
574
|
-
end
|
575
|
-
end
|
576
|
-
|
577
|
-
test "lines with block" do
|
578
|
-
io = FilterIO.new(StringIO.new("foo\nbar\nbaz"))
|
579
|
-
expected = [ ["foo\n", "bar\n"], ["baz", nil] ]
|
580
|
-
actual = []
|
581
|
-
retval = io.lines do |line|
|
582
|
-
actual << [line, io.gets]
|
583
|
-
end
|
584
|
-
assert_equal io, retval
|
585
|
-
assert_equal expected, actual
|
586
|
-
end
|
587
|
-
|
588
|
-
test "lines enumerator" do
|
589
|
-
io = FilterIO.new(StringIO.new("foo\nbar\nbaz"))
|
590
|
-
e = io.lines
|
591
|
-
expected = [ ["foo\n", "bar\n"], ["baz", nil] ]
|
592
|
-
actual = e.map { |line| [line, io.gets] }
|
593
|
-
assert_equal expected, actual
|
594
|
-
end
|
595
|
-
|
596
|
-
test "seek set" do
|
597
|
-
|
598
|
-
io = FilterIO.new(StringIO.new("abcdef"))
|
599
|
-
|
600
|
-
# beginning
|
601
|
-
assert_equal 'a', io.readchar.chr
|
602
|
-
assert_equal 1, io.pos
|
603
|
-
io.seek 0, IO::SEEK_SET
|
604
|
-
assert_equal 'a', io.readchar.chr
|
605
|
-
assert_equal 1, io.pos
|
606
|
-
|
607
|
-
# same position
|
608
|
-
io.seek 1, IO::SEEK_SET
|
609
|
-
assert_equal 'b', io.readchar.chr
|
610
|
-
assert_equal 2, io.pos
|
611
|
-
|
612
|
-
# backwards fail
|
613
|
-
assert_raise Errno::EINVAL do
|
614
|
-
io.seek 1, IO::SEEK_SET
|
615
|
-
end
|
616
|
-
assert_equal 'c', io.readchar.chr
|
617
|
-
assert_equal 3, io.pos
|
618
|
-
|
619
|
-
end
|
620
|
-
|
621
|
-
test "seek current" do
|
622
|
-
|
623
|
-
io = FilterIO.new(StringIO.new("abcdef"))
|
624
|
-
|
625
|
-
# same pos
|
626
|
-
assert_equal 'ab', io.read(2)
|
627
|
-
assert_equal 2, io.pos
|
628
|
-
io.seek 0, IO::SEEK_CUR
|
629
|
-
assert_equal 2, io.pos
|
630
|
-
|
631
|
-
# backwards fail
|
632
|
-
assert_equal 'c', io.read(1)
|
633
|
-
assert_equal 3, io.pos
|
634
|
-
assert_raise Errno::EINVAL do
|
635
|
-
io.seek(-1, IO::SEEK_CUR)
|
636
|
-
end
|
637
|
-
assert_equal 3, io.pos
|
638
|
-
|
639
|
-
# forwards fail
|
640
|
-
assert_equal 3, io.pos
|
641
|
-
assert_raise Errno::EINVAL do
|
642
|
-
io.seek(2, IO::SEEK_CUR)
|
643
|
-
end
|
644
|
-
assert_equal 3, io.pos
|
645
|
-
|
646
|
-
# beginning
|
647
|
-
io.seek(-io.pos, IO::SEEK_CUR)
|
648
|
-
assert_equal 0, io.pos
|
649
|
-
|
650
|
-
end
|
651
|
-
|
652
|
-
test "seek end" do
|
653
|
-
io = FilterIO.new(StringIO.new("abcdef"))
|
654
|
-
assert_raise Errno::EINVAL do
|
655
|
-
io.seek(0, IO::SEEK_END)
|
656
|
-
end
|
657
|
-
assert_raise Errno::EINVAL do
|
658
|
-
io.seek(6, IO::SEEK_END)
|
659
|
-
end
|
660
|
-
assert_raise Errno::EINVAL do
|
661
|
-
io.seek(-6, IO::SEEK_END)
|
662
|
-
end
|
663
|
-
end
|
664
|
-
|
665
|
-
test "need more data at eof" do
|
666
|
-
input = "foo"
|
667
|
-
[2,3,6].each do |block_size|
|
668
|
-
[true, false].each do |always|
|
669
|
-
count = 0
|
670
|
-
io = FilterIO.new(StringIO.new(input), :block_size => block_size) do |data, state|
|
671
|
-
count += 1
|
672
|
-
raise FilterIO::NeedMoreData if state.eof? or always
|
673
|
-
data
|
674
|
-
end
|
675
|
-
assert_raise EOFError do
|
676
|
-
io.readline
|
677
|
-
end
|
678
|
-
expected_count = block_size < input.size ? 2 : 1
|
679
|
-
assert_equal expected_count, count
|
680
|
-
end
|
681
|
-
end
|
682
|
-
end
|
683
|
-
|
684
|
-
test "unget via block" do
|
685
|
-
# get consecutive unique characters from a feed
|
686
|
-
# this is similar to uniq(1) and STL's unique_copy
|
687
|
-
input = "122234435"
|
688
|
-
expected = "123435"
|
689
|
-
(1..5).each do |block_size|
|
690
|
-
io = FilterIO.new(StringIO.new(input), :block_size => block_size) do |data, state|
|
691
|
-
# grab all of the same character
|
692
|
-
data =~ /\A(.)\1*(?!\1)/ or raise 'No data'
|
693
|
-
# if there was nothing after it and we aren't at EOF...
|
694
|
-
# ...grab more data to make sure we're at the end
|
695
|
-
raise FilterIO::NeedMoreData if $'.empty? && !state.eof?
|
696
|
-
# return the matched character as data and re-buffer the rest
|
697
|
-
[$&[0], $']
|
698
|
-
end
|
699
|
-
assert_equal expected, io.read
|
700
|
-
end
|
701
|
-
end
|
702
|
-
|
703
|
-
test "get more data via unget" do
|
704
|
-
|
705
|
-
input = "foo\ntest\n\n12345\n678"
|
706
|
-
expected = input.gsub(/^.*$/) { |x| "#{$&.size} #{$&}" }
|
707
|
-
expected += "\n" unless expected =~ /\n\z/
|
708
|
-
|
709
|
-
block_count = 0
|
710
|
-
io = FilterIO.new StringIO.new(input), :block_size => 2 do |data, state|
|
711
|
-
block_count += 1
|
712
|
-
raise 'Too many retries' if block_count > 100
|
713
|
-
raise "Expected less data: #{data.inspect}" if data.size > 6
|
714
|
-
output = ''
|
715
|
-
while data =~ /(.*)\n/ || (state.eof? && data =~ /(.+)/)
|
716
|
-
output << "#{$1.size} #{$1}\n"
|
717
|
-
data = $'
|
718
|
-
end
|
719
|
-
[output, data]
|
720
|
-
end
|
721
|
-
actual = io.read
|
722
|
-
|
723
|
-
assert_equal expected, actual
|
724
|
-
assert_operator block_count, :>=, 10
|
725
|
-
|
726
|
-
end
|
727
|
-
|
728
|
-
test "close method" do
|
729
|
-
[2, 16].each do |block_size|
|
730
|
-
|
731
|
-
source_io = StringIO.new("foo\nbar\nbaz")
|
732
|
-
filtered_io = FilterIO.new(source_io, :block_size => block_size, &:upcase)
|
733
|
-
|
734
|
-
assert_equal "FOO\n", filtered_io.gets
|
735
|
-
|
736
|
-
# close the filtered stream
|
737
|
-
filtered_io.close
|
738
|
-
|
739
|
-
# both the filtered and source stream should be closed
|
740
|
-
assert_true source_io.closed?
|
741
|
-
assert_true filtered_io.closed?
|
742
|
-
|
743
|
-
# futher reads should raise an error
|
744
|
-
assert_raise IOError do
|
745
|
-
filtered_io.gets
|
746
|
-
end
|
747
|
-
|
748
|
-
# closing again should raise an error
|
749
|
-
assert_raise IOError do
|
750
|
-
filtered_io.close
|
751
|
-
end
|
752
|
-
|
753
|
-
end
|
754
|
-
end
|
755
|
-
|
756
|
-
test "should raise IO error if block returns nil" do
|
757
|
-
io = FilterIO.new(StringIO.new("foo")) { |data| nil }
|
758
|
-
assert_raise IOError do
|
759
|
-
io.read.to_a
|
760
|
-
end
|
761
|
-
end
|
762
|
-
|
763
|
-
test "should be able to read from GzipReader stream" do
|
764
|
-
input = "über résumé"
|
765
|
-
input.force_encoding 'ASCII-8BIT' if input.respond_to? :force_encoding
|
766
|
-
buffer = StringIO.new
|
767
|
-
out = Zlib::GzipWriter.new buffer
|
768
|
-
out.write input
|
769
|
-
out.finish
|
770
|
-
buffer.rewind
|
771
|
-
io = Zlib::GzipReader.new(buffer)
|
772
|
-
|
773
|
-
io = FilterIO.new(io)
|
774
|
-
assert_equal input, io.read
|
775
|
-
end
|
776
|
-
|
777
|
-
end
|