Fix for JRUBY-2882. Handle error messages related to constructors better
[jruby.git] / test / test_io.rb
blobfed027a74635fef411667e18dd12849205cda731
1 require 'test/unit'
2 require 'rbconfig'
4 class TestIO < Test::Unit::TestCase
5   WINDOWS = Config::CONFIG['host_os'] =~ /Windows|mswin/
6   def setup
7     @to_close = []
8     @file = "TestIO_tmp"
9     @file2 = "Test2IO_tmp"
10     @file3 = "Test3IO_tmp"
11     if (WINDOWS)
12       @devnull = 'NUL:'
13     else
14       @devnull = '/dev/null'
15     end
16   end
18   def teardown
19     @to_close.each {|io| io.close rescue nil }
20     File.unlink @file rescue nil
21     File.unlink @file2 rescue nil
22     File.unlink @file3 rescue nil
23   end
25   def test_erroneous_io_usage
26     assert_raises(ArgumentError) { IO.new }
27     # commented out until JRUBY-1048 is completed
28     #assert_raises(StandardError) { IO.new(123) }
29     assert_raises(TypeError) { IO.new "FROGGER" }
30     assert_raises(TypeError) { IO.foreach 3 }
31   end
33   def test_gets_delimiting
34     f = File.new(@file3, "w")
35     f.print("A\n\n\nB\n")
36     f.close
37     f = File.new(@file3, "r")
38     f.gets("\n\n")
39     b = f.gets("\n\n")
40     f.gets("\n\n")
41     f.close
42     assert(b == "\nB\n", "gets of non-paragraph \"\\n\\n\" failed")
43   end
45   def test_two_ios_with_same_filenos
46     # Two ios with same fileno, but different objects.
47     f = File.new(@file, "w")
48     @to_close << f
49     f.puts("heh")
50     g = IO.new(f.fileno)
51     assert_equal(f.fileno, g.fileno)
52     assert_raises(IOError) { g.gets }
53     g.close
54     assert_raises(IOError) { g.puts }
56     f = File.new(@file, "r")
57     @to_close << f
58     g = IO.new(f.fileno)
59     assert_equal(f.fileno, g.fileno)
60     assert_raises(IOError) { g.puts }
61     # If g closes then g knows that it was once a valid descriptor.
62     # So it throws an IOError.
63     g.close
64     assert_raises(IOError) { g.gets }
65   end
67   def test_puts_on_a_recursive_array
68     # Puts a recursive array
69     x = []
70     x << 2 << x
71     f = File.new(@file, "w")
72     @to_close << f
73     g = IO.new(f.fileno)
74     @to_close << g
75     g.puts x
76     g.close
78     f = File.new(@file, "r")
79     @to_close << f
80     g = IO.new(f.fileno)
81     @to_close << g
82     a = f.gets
83     b = f.gets
84     assert_equal("2\n", a)
85     assert_equal("[...]\n", b)
86   end
88   def test_premature_close_raises_appropriate_errors
89     ensure_files @file
90     # In this case we will have f close (which will pull the rug
91     # out from under g) and thus make g try the ops and fail
92     f = File.open(@file)
93     g = IO.new(f.fileno)
94     @to_close << g
95     f.close
96     assert_raises(Errno::EBADF) { g.readchar }
97     assert_raises(Errno::EBADF) { g.readline }
98     assert_raises(Errno::EBADF) { g.gets }
99     assert_raises(Errno::EBADF) { g.close }
100     assert_raises(IOError) { g.getc }
101     assert_raises(IOError) { g.readchar }
102     assert_raises(IOError) { g.read }
103     assert_raises(IOError) { g.sysread 1 }
105     f = File.open(@file, "w")
106     g = IO.new(f.fileno)
107     @to_close << g
108     f.close
109     assert_nothing_raised { g.print "" }
110     assert_nothing_raised { g.write "" }
111     assert_nothing_raised { g.puts "" }
112     assert_nothing_raised { g.putc 'c' }
113     assert_raises(Errno::EBADF) { g.syswrite "" }
114   end
116   def test_ios_with_incompatible_flags
117     ensure_files @file, @file2
118     # Cannot open an IO which does not have compatible permission with
119     # original IO
120     f = File.new(@file2, "w")
121     @to_close << f
122     assert_raises(Errno::EINVAL) { g = IO.new(f.fileno, "r") }
123     f.close
125     f = File.new(@file, "r")
126     @to_close << f
127     assert_raises(Errno::EINVAL) { g = IO.new(f.fileno, "w") }
128     f.close
129   end
131   def test_ios_with_compatible_flags
132     ensure_files @file
133     # However, you can open a second with less permissions
134     f = File.new(@file, "r+")
135     @to_close << f
136     g = IO.new(f.fileno, "r")
137     @to_close << g
138     g.gets
139     f.puts "HEH"
140     assert_raises(IOError) { g.write "HOH" }
141     assert_equal(f.fileno, g.fileno)
142     f.close
143   end
145   def test_seek
146     ensure_files @file
147     f = File.new(@file)
148     @to_close << f
149     assert_raises(Errno::EINVAL) { f.seek(-1) }
150     # Advance one + single arg seek
151     f.seek(1)
152     assert_equal(f.pos, 1)
153     f.close
154   end
156   def test_empty_write_does_not_complain
157     # empty write...writes nothing and does not complain
158     f = File.new(@file, "w")
159     @to_close << f
160     i = f.syswrite("")
161     assert_equal(i, 0)
162     i = f.syswrite("heh")
163     assert_equal(i, 3)
164     f.close
165   end
167   def test_enoent
168     assert_raises(Errno::ENOENT) { File.foreach("nonexistent_file") {} }
169   end
171   def test_reopen
172     ensure_files @file, @file2    
173     file = File.open(@file)
174     @to_close << file
175     file.gets
176     file2 = File.open(@file2)
177     @to_close << file2
178     file2_fileno = file2.fileno;
179     file2 = file2.reopen(file)
180     assert_equal(file.pos, file2.pos)
181     assert_equal(file2_fileno, file2.fileno);
182     assert(file.fileno != file2.fileno);
183     file2.close
184     file.close
185     
186     # reopen of a filename after a close should succeed (JRUBY-1885)
187     assert_nothing_raised { file.reopen(@file) }
188   end
190   def test_file_puts_gets_readline
191     f = File.open(@file, "w")
192     @to_close << f
193     f.puts("line1");
194     f.puts("line2");
195     f.puts("line3");
196     f.close
198     f = File.open(@file)
199     assert_equal(f.gets(), $_)
200     assert_equal(f.readline(), $_)
201     f.close
202   end
204   def test_file_read
205     ensure_files @file
206     # test that read returns correct values
207     f = File.open(@file)
208     @to_close << f
209     f.read # read all
210     assert_equal("", f.read)
211     assert_equal(nil, f.read(1))
212     f.close
213   end
214   
215   # MRI 1.8.5 and 1.8.6 permit nil buffers with reads.
216   def test_file_read_with_nil_buffer
217      ensure_files @file
218      
219      f = File.open(@file)
220      @to_close << f
221      assert_equal " ", f.read(1, nil) 
222   end
223     
224   def test_open
225     ensure_files @file
227     assert_raises(ArgumentError) { io = IO.open }
229     f = File.open(@file)
230     @to_close << f
231     assert_raises(ArgumentError) { io = IO.open(f.fileno, "r", :gratuitous) }
232     io = IO.open(f.fileno, "r")
233     @to_close << io
234     assert_equal(f.fileno, io.fileno)
235     assert(!io.closed?)
236     io.close
237     assert(io.closed?)
239     assert(!f.closed?)
240     assert_raises(Errno::EBADF) { f.close }
241   end
243   def test_open_with_block
244     ensure_files @file
246     f = File.open(@file)
247     @to_close << f
248     IO.open(f.fileno, "r") do |io|
249       assert_equal(f.fileno, io.fileno)
250       assert(!io.closed?)
251     end
253     assert(!f.closed?)
254     assert_raises(Errno::EBADF) { f.close }
255   end
257   def test_delete
258     ensure_files @file, @file2, @file3
259     # Test deleting files
260     assert(File.delete(@file, @file2, @file3))
261   end
263   def test_select
264     ##### select #####
265     assert_equal(nil, select(nil, nil, nil, 0))
266     assert_raises(ArgumentError) { select(nil, nil, nil, -1) }
267   end
269   class FakeStream
270     attr_accessor :data
271     def initialize(stream, passthrough = false)
272       @stream = stream
273       @passthrough = passthrough
274     end
275     def write(content)
276       @data = content
277       @stream.write(content) if @passthrough
278     end
279   end
281   def test_puts_and_warn_redirection
282     require 'stringio'
283     begin
284       $stdout = StringIO.new
285       $stderr = StringIO.new
286       $stdout.print ":"
287       $stderr.print ":"
288       puts "hi"
289       warn "hello"
290       assert_equal ":hi\n", $stdout.string
291       assert_equal ":hello\n", $stderr.string
292     ensure
293       $stderr = STDERR
294       $stdout = STDOUT
295     end
296   end
298   # JRUBY-1894
299   def test_getc_255
300      File.open(@file, "wb") do |file|
301        file.putc(255)
302      end
303      File.open(@file, "rb") do |file|
304        assert_equal(255, file.getc)
305      end
306   end
308   # JRUBY-2202
309   def test_ungetc_empty_file
310     File.open(@file, "w+") {}
311     File.open(@file) do |file|
312       assert_nil(file.getc)
313       assert_equal(0, file.pos)
314       file.ungetc(100)
316       # The following line is an intentional regression tests,
317       # it checks that JRuby doesn't break.
318       assert_equal(0, file.pos)
320       assert_equal(100, file.getc)
321      end
322   end
324   # JRUBY-2202
325   def test_ungetc_nonempty_file
326     File.open(@file, "w+") { |file| file.puts("HELLO") }
327     File.open(@file) do |file|
328       assert_equal(72, file.getc)
329       assert_equal(1, file.pos)
330       file.ungetc(100)
331       assert_equal(0, file.pos)
332       assert_equal(100, file.getc)
333       assert_equal(1, file.pos)
334      end
335   end
337   # JRUBY-2202
338   def test_ungetc_position_change
339     File.open(@file, "w+") { |file| file.puts("HELLO") }
341     # getc/ungetc the same char
342     File.open(@file) do |f|
343       f.ungetc(f.getc)
344       assert_equal(0, f.pos)
345       assert_equal("HELLO", f.read(5))
346       assert_equal(5, f.pos)
347     end
349     # getc/ungetc different char
350     File.open(@file) do |f|
351       f.getc
352       f.ungetc(100)
353       assert_equal(0, f.pos)
354       assert_equal("dELLO", f.read(5))
355       assert_equal(5, f.pos)
356      end
357   end
359   # JRUBY-2203
360   # unget char should be discarded after position changing calls
361   def test_unget_before_position_change
362     File.open(@file, "w+") { |file| file.puts("HELLO") }
363     File.open(@file) do |f|
364       f.read(3)
365       f.ungetc(100)
366       f.pos = 2
367       assert_equal("LLO", f.read(3))
369       f.ungetc(100)
370       f.seek(2)
371       assert_equal("LLO", f.read(3))
372       
373       f.ungetc(100)
374       f.rewind
375       assert_equal("HELLO", f.read(5))
376       
377       f.ungetc(100)
378       f.seek(-3, IO::SEEK_END)
379       assert_equal("LO", f.read(2))
380     end
381   end
383   # JRUBY-1987
384   def test_reopen_doesnt_close_same_handler
385     f = File.open(@file, "w")
386     @to_close << f
387     x = IO.new(f.fileno)
388     @to_close << x
389     f.print "."
390     y = x.dup
391     @to_close << y
392     x.reopen(y)
393     f.print "."
394     f.close
395     out = File.read(@file)
396     assert_equal "..", out
397   end
398   
399   # JRUBY-1698
400   def test_very_big_read
401     # See JRUBY-1686: this caused OOM
402     ensure_files @file
403     f = File.open(@file)
404     @to_close << f
405     assert_nothing_raised { f.read(1000000000) }
406   end
407   
408   # JRUBY-2023, multithreaded writes
409   def test_multithreaded_writes
410     f = File.open("__temp1", "w")
411     @to_close << f
412     threads = []
413     100.times {
414       threads << Thread.new { 100.times { f.print('.') } }
415     }
416     threads.each {|thread| thread.join}
417     f.close
418     assert File.size("__temp1") == 100*100
419   ensure
420     File.unlink("__temp1")
421   end
423   #JRUBY-2145
424   def test_eof_on_dev_null
425     File.open(@devnull, 'rb') { |f|
426       assert(f.eof?)
427     }
428   end
430   #JRUBY-2145
431   def test_read_dev_null
432     File.open(@devnull, 'rb') { |f|
433       assert_equal("", f.read)
434       assert_equal(nil, f.read(1))
435       assert_equal([], f.readlines)
436       assert_raise EOFError do
437         f.readline
438       end
439     }
440   end
442   #JRUBY-2145
443   if (!WINDOWS)
444     def test_copy_dev_null
445       require 'fileutils'
446       begin
447         FileUtils.cp(@devnull, 'somefile')
448         assert(File.exists?('somefile'))
449         assert_equal(0, File.size('somefile'))
450       ensure
451         File.delete('somefile') rescue nil
452       end
453     end
454   end
456   if (WINDOWS)
457     #JRUBY-2158
458     def test_null_open_windows
459       null_names = ['NUL', 'NUL:', 'nul', 'nul:']
460       null_names.each { |name|
461         File.open(name) { |f|
462           assert_equal("", f.read)
463           assert(f.eof?)
464         }
465         File.open(name, 'r+') { |f|
466           assert_nil(f.puts("test"))
467         }
468       }
469     end
470   end
471   
472   def test_file_constants_included
473     assert IO.include?(File::Constants)
474     assert_equal ["APPEND", "BINARY", "CREAT", "EXCL", "Enumerator", "FNM_CASEFOLD",
475                    "FNM_DOTMATCH", "FNM_NOESCAPE", "FNM_PATHNAME", "FNM_SYSCASE",
476                    "LOCK_EX", "LOCK_NB", "LOCK_SH", "LOCK_UN", "NOCTTY", "NONBLOCK",
477                    "RDONLY", "RDWR", "SEEK_CUR", "SEEK_END", "SEEK_SET", "SYNC", "TRUNC",
478                    "WRONLY"], IO.constants.sort
479   end
481   private
482   def ensure_files(*files)
483     files.each {|f| File.open(f, "w") {|g| g << " " } }
484   end