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.
@@ -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