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