Initial import
[ruby-io-mixins.git] / test / io_mixins_test.rb
1 #!/usr/bin/ruby
2 # $Id$
3 # -*- ruby -*- vim:set ft=ruby et sw=2 sts=2:
4
5 require 'io/mixins'
6 require 'stringio'
7 require 'forwardable'
8 require 'test/unit'
9
10 class SimpleIO
11   include IO::Readable
12   include IO::Writable
13   include IO::Seekable
14   extend  IO::Openable
15   extend  Forwardable
16   def initialize(*args)
17     @string = StringIO.new(*args)
18   end
19   def sysread(length)
20     @string.read(length) or raise EOFError, "end of file reached", caller
21   end
22   def syswrite(chunk)
23     @string.write(chunk)
24   end
25   def sysseek(offset, whence = IO::SEEK_SET)
26     @string.seek(offset,whence)
27     @string.tell
28   end
29   DELEGATORS = [:close_read,:close_write,:closed_read?,:closed_write?,:reopen]
30   def_delegators :@string, *DELEGATORS
31   # def close_read()   @string.close_read    end
32   # def close_write()  @string.close_write   end
33   # def closed_read?() @string.closed_read?  end
34   # def closed_write?()@string.closed_write? end
35   # def close()        @string.close         end
36 end
37
38 require File.join(File.dirname(__FILE__),'rot13_filter')
39 require File.join(File.dirname(__FILE__),'io_loop')
40 require File.join(File.dirname(__FILE__),'read_joiner')
41
42 class PairTester
43   def initialize(test,initial,*args)
44     @test = test
45     @string1 = initial.dup
46     @string2 = initial.dup
47     @s1 = StringIO.new(@string1,*args)
48     @s2 = SimpleIO.new(@string2,*args)
49   end
50
51   def perform(*args)
52     @s1.__send__(*args)
53     @s2.__send__(*args)
54   end
55
56   def assert_equal
57     @test.assert_equal @string1, @string2
58   end
59
60   def method_missing(*args)
61     value1 = @s1.__send__(*args) rescue $!.class
62     value2 = @s2.__send__(*args) rescue $!.class
63     value2 = value1 if value1 == @s1 && value2 == @s2
64     @test.assert_equal value1, value2
65     value1
66   end
67
68 end
69
70 class IOMixinsTest < Test::Unit::TestCase
71
72   def test_minimal
73     SimpleIO.open(":)\n") do |io|
74       assert_nothing_raised { io.gets }
75       assert_raise(NotImplementedError) { io.fcntl }
76     end
77   end
78
79   def test_enough_methods
80     junk = Object.instance_methods | Enumerable.instance_methods
81     good = IO.instance_methods(false) & StringIO.instance_methods(false)
82     mine = SimpleIO.instance_methods
83     assert_equal [], good - junk - mine
84   end
85
86   def test_io_loop
87     string = "foo\nbar\n"
88     io = IOLoop.open(string)
89     assert_equal ?f, io.getc
90     io.ungetc ?g
91     assert_equal ?g, io.getc
92     io.putc "lqqqqq" # Should use first character
93     assert_equal "o\n", io.gets
94     io.puts "baz\n"
95     assert_equal "flo\n", io.readline
96     assert_equal "baz\n", io.readline
97     io.close
98     io.instance_variable_set("@closed_read",false)
99     assert_equal true, io.closed_read?
100     assert_raise(IOError) { io.read }
101     io = IOLoop.open("foo")
102     assert_raise(IOError) { io.read }
103   end
104
105   def empty_file
106     File.open(RUBY_PLATFORM =~ /win32/ ? "NUL:" : "/dev/null")
107   end
108
109   def empty_string
110     StringIO.open("")
111   end
112
113   def empty_simple
114     SimpleIO.new("")
115   end
116
117   def assert_identical_response(object1,object2,method,*args,&block)
118     value1 = object1.send(method,*args,&block) rescue $!.class
119     value2 = object2.send(method,*args,&block) rescue $!.class
120     assert_equal value1, value2, "#{object1.class} gave #{value1.inspect} while #{object2.class} gave #{value2.inspect}"
121   end
122
123   def initialize_both(*args)
124     @both = PairTester.new(self,*args)
125   end
126
127   def test_simple_io_empty_read
128     [lambda {empty_file},lambda {empty_string}].each do |l|
129       assert_identical_response l.call,empty_simple,:read
130       assert_identical_response l.call,empty_simple,:read,0
131       assert_identical_response l.call,empty_simple,:read,2
132     end
133   end
134
135   def test_simple_io_read
136     initialize_both("Hello\nworld!")
137     @both.eof
138     @both.tty?
139     @both.lineno
140     @both.read
141     @both.eof?
142     @both.seek(2)
143     @both.pos
144     @both.rewind
145     @both.tell
146     @both.gets
147     @both.lineno
148     @both.read(4, "buffer")
149     @both.tell
150     @both.read(0)
151     @both.read(6)
152     @both.read
153     @both.eof
154     @both.read(0)
155     @both.rewind
156     @both.lineno
157     @both.read(8)
158     @both.read(0)
159     @both.read(1)
160     @both.readchar
161     @both.close_read
162     @both.close
163     @both.close_write
164     @both.closed_read?
165     @both.closed_write?
166     @both.sync
167     @both.closed?
168   end
169
170   def test_simple_io_gets
171     # io = SimpleIO.open(StringIO.new(<<-EOF))
172     string = <<-EOF
173
174
175 Hello world!
176
177
178 Chunky
179 bacon!
180
181
182
183 Goodbye world!
184     EOF
185     string.chomp!
186     initialize_both(string)
187     @both.gets ""
188     @both.readline ""
189     @both.map {|x|x}
190     @both.close
191   end
192
193   def test_simple_io_write
194     initialize_both("x","w")
195     @both.eof
196     @both.putc(42)
197     @both.eof # => IOError
198     @both.print("Hello ")
199     @both.binmode
200     @both.puts("world")
201     @both << ":-"
202     @both << ?)
203     @both.fsync
204     @both.printf("%4d",17)
205     @both.putc(1..3) # => TypeError
206     @both.putc(:bad) # => TypeError
207     @both.pos
208     @both.flush
209     @both.pos = 6
210     @both.putc(?_)
211     @both.close
212     @both.assert_equal
213   end
214
215   def test_simple_io_seek
216     initialize_both("Look ma!","r+")
217     @both.pos
218     @both.getc
219     @both.tell
220     @both.seek(3,IO::SEEK_SET)
221     @both.read(2)
222     @both.ungetc(?z)
223     @both.tell
224     @both.seek(-2,IO::SEEK_CUR)
225     @both.getc
226     @both.pos
227     @both.seek(-3,IO::SEEK_END)
228     @both.tell
229     @both.getc
230   end
231
232   def test_missing_methods
233     readable = Class.new do
234       include IO::Readable
235     end
236     writable = Class.new do
237       include IO::Writable
238       def close_write
239         if @closed_write
240           raise IOError
241         else
242           @closed_write = true
243         end
244       end
245       def closed_write?
246         !!@closed_write
247       end
248     end
249     streamable = Class.new(writable) do
250       include IO::Readable
251       def close_read
252         if @closed_read
253           raise IOError
254         else
255           @closed_read = true
256         end
257       end
258       def closed_read?
259         !!@closed_read
260       end
261     end
262     assert_raise(NotImplementedError) {readable.new.close}
263     assert_raise(NotImplementedError) {readable.new.closed?}
264     w = writable.new
265     assert_equal false, w.closed_write?
266     assert_nothing_thrown {w.close}
267     assert_raise(IOError) {w.close}
268     assert_equal true, w.closed?
269     s = streamable.new
270     assert_equal false, s.closed?
271     assert_nothing_thrown {s.close_read}
272     assert_nothing_thrown {s.close}
273     assert_raise(IOError) {s.close_write}
274     assert_raise(IOError) {s.close}
275     assert_equal true, s.closed?
276   end
277
278   def test_open_readers
279     assert_raise(EOFError) { SimpleIO.open("") {|io|io.sysread(1)}}
280     assert_equal "READ", SimpleIO.read("README",4)
281     assert_equal "x" * 1025, SimpleIO.read("x"*1025)
282     assert_equal ["foo\n","bar"], SimpleIO.readlines("foo\nbar")
283     assert_raise(LocalJumpError) { SimpleIO.foreach("foo\nbar") }
284   end
285
286   def test_rot13
287     string = "Puhaxl onpba!"
288     stringio = StringIO.open(string,"r")
289     filtered = Rot13Filter.open(stringio)
290     assert_equal "Chunky bacon!", filtered.read
291     stringio = StringIO.open(string,"r+")
292     filtered = Rot13Filter.open(stringio)
293     filtered.write("Rotten")
294     assert_equal "Ebggra onpba!", string
295   end
296
297   def test_joiner
298     joiner = ReadJoiner.new(StringIO.new("fo"),StringIO.new("o\nbar\n"))
299     assert_equal "foo\n", joiner.readline
300     assert_nil   joiner.close
301     assert_equal true, joiner.closed?
302   end
303
304 end