filter_io 0.1.6 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|