1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/test/ruby/test_file.rb
ko1 c39bdb798d $SAFE as a process global state. [Feature #14250]
* vm_core.h (rb_vm_t): move `rb_execution_context_t::safe_level` to
  `rb_vm_t::safe_level_` because `$SAFE` is a process (VM) global state.

* vm_core.h (rb_proc_t): remove `rb_proc_t::safe_level` because `Proc`
  objects don't need to keep `$SAFE` at the creation.
  Also make `is_from_method` and `is_lambda` as 1 bit fields.

* cont.c (cont_restore_thread): no need to keep `$SAFE` for Continuation.

* eval.c (ruby_cleanup): use `rb_set_safe_level_force()` instead of access
  `vm->safe_level_` directly.

* eval_jump.c: End procs `END{}` doesn't keep `$SAFE`.

* proc.c (proc_dup): removed and introduce `rb_proc_dup` in vm.c.

* safe.c (rb_set_safe_level): don't check `$SAFE` 1 -> 0 changes.

* safe.c (safe_setter): use `rb_set_safe_level()`.

* thread.c (rb_thread_safe_level): `Thread#safe_level` returns `$SAFE`.
  It should be obsolete.

* transcode.c (load_transcoder_entry): `rb_safe_level()` only returns
  0 or 1 so that this check is not needed.

* vm.c (vm_proc_create_from_captured): don't need to keep `$SAFE` for Proc.

* vm.c (rb_proc_create): renamed to `proc_create`.

* vm.c (rb_proc_dup): moved from proc.c.

* vm.c (vm_invoke_proc): do not need to set and restore `$SAFE`
  for `Proc#call`.

* vm_eval.c (rb_eval_cmd): rename a local variable to represent clearer
  meaning.

* lib/drb/drb.rb: restore `$SAFE`.

* lib/erb.rb: restore `$SAFE`, too.

* test/lib/leakchecker.rb: check `$SAFE == 0` at the end of tests.

* test/rubygems/test_gem.rb: do not set `$SAFE = 1`.

* bootstraptest/test_proc.rb: catch up this change.

* spec/ruby/optional/capi/string_spec.rb: ditto.

* test/bigdecimal/test_bigdecimal.rb: ditto.

* test/fiddle/test_func.rb: ditto.

* test/fiddle/test_handle.rb: ditto.

* test/net/imap/test_imap_response_parser.rb: ditto.

* test/pathname/test_pathname.rb: ditto.

* test/readline/test_readline.rb: ditto.

* test/ruby/test_file.rb: ditto.

* test/ruby/test_optimization.rb: ditto.

* test/ruby/test_proc.rb: ditto.

* test/ruby/test_require.rb: ditto.

* test/ruby/test_thread.rb: ditto.

* test/rubygems/test_gem_specification.rb: ditto.

* test/test_tempfile.rb: ditto.

* test/test_tmpdir.rb: ditto.

* test/win32ole/test_win32ole.rb: ditto.

* test/win32ole/test_win32ole_event.rb: ditto.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@61510 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2017-12-28 20:09:24 +00:00

512 lines
13 KiB
Ruby

# frozen_string_literal: false
require 'test/unit'
require 'tempfile'
require "-test-/file"
require_relative 'ut_eof'
class TestFile < Test::Unit::TestCase
# I don't know Ruby's spec about "unlink-before-close" exactly.
# This test asserts current behaviour.
def test_unlink_before_close
Dir.mktmpdir('rubytest-file') {|tmpdir|
filename = tmpdir + '/' + File.basename(__FILE__) + ".#{$$}"
w = File.open(filename, "w")
w << "foo"
w.close
r = File.open(filename, "r")
begin
if /(mswin|bccwin|mingw|emx)/ =~ RUBY_PLATFORM
assert_raise(Errno::EACCES) {File.unlink(filename)}
else
assert_nothing_raised {File.unlink(filename)}
end
ensure
r.close
File.unlink(filename) if File.exist?(filename)
end
}
end
include TestEOF
def open_file(content)
Tempfile.create("test-eof") {|f|
f << content
f.rewind
yield f
}
end
alias open_file_rw open_file
include TestEOF::Seek
def test_empty_file_bom
bug6487 = '[ruby-core:45203]'
Tempfile.create(__method__.to_s) {|f|
assert_file.exist?(f.path)
assert_nothing_raised(bug6487) {File.read(f.path, mode: 'r:utf-8')}
assert_nothing_raised(bug6487) {File.read(f.path, mode: 'r:bom|utf-8')}
}
end
def assert_bom(bytes, name)
bug6487 = '[ruby-core:45203]'
Tempfile.create(name.to_s) {|f|
f.sync = true
expected = ""
result = nil
bytes[0...-1].each do |x|
f.write x
f.write ' '
f.pos -= 1
expected << x
assert_nothing_raised(bug6487) {result = File.read(f.path, mode: 'rb:bom|utf-8')}
assert_equal("#{expected} ".force_encoding("utf-8"), result)
end
f.write bytes[-1]
assert_nothing_raised(bug6487) {result = File.read(f.path, mode: 'rb:bom|utf-8')}
assert_equal '', result, "valid bom"
}
end
def test_bom_8
assert_bom(["\xEF", "\xBB", "\xBF"], __method__)
end
def test_bom_16be
assert_bom(["\xFE", "\xFF"], __method__)
end
def test_bom_16le
assert_bom(["\xFF", "\xFE"], __method__)
end
def test_bom_32be
assert_bom(["\0", "\0", "\xFE", "\xFF"], __method__)
end
def test_bom_32le
assert_bom(["\xFF\xFE\0", "\0"], __method__)
end
def test_truncate_wbuf
Tempfile.create("test-truncate") {|f|
f.print "abc"
f.truncate(0)
f.print "def"
f.flush
assert_equal("\0\0\0def", File.read(f.path), "[ruby-dev:24191]")
}
end
def test_truncate_rbuf
Tempfile.create("test-truncate") {|f|
f.puts "abc"
f.puts "def"
f.rewind
assert_equal("abc\n", f.gets)
f.truncate(3)
assert_equal(nil, f.gets, "[ruby-dev:24197]")
}
end
def test_truncate_beyond_eof
Tempfile.create("test-truncate") {|f|
f.print "abc"
f.truncate 10
assert_equal("\0" * 7, f.read(100), "[ruby-dev:24532]")
}
end
def test_truncate_size
Tempfile.create("test-truncate") do |f|
q1 = Thread::Queue.new
q2 = Thread::Queue.new
th = Thread.new do
data = ''
64.times do |i|
data << i.to_s
f.rewind
f.print data
f.truncate(data.bytesize)
q1.push data.bytesize
q2.pop
end
q1.push nil
end
while size = q1.pop
assert_equal size, File.size(f.path)
assert_equal size, f.size
q2.push true
end
th.join
end
end
def test_read_all_extended_file
[nil, {:textmode=>true}, {:binmode=>true}].each do |mode|
Tempfile.create("test-extended-file", mode) {|f|
assert_nil(f.getc)
f.print "a"
f.rewind
assert_equal("a", f.read, "mode = <#{mode}>")
}
end
end
def test_gets_extended_file
[nil, {:textmode=>true}, {:binmode=>true}].each do |mode|
Tempfile.create("test-extended-file", mode) {|f|
assert_nil(f.getc)
f.print "a"
f.rewind
assert_equal("a", f.gets("a"), "mode = <#{mode}>")
}
end
end
def test_gets_para_extended_file
[nil, {:textmode=>true}, {:binmode=>true}].each do |mode|
Tempfile.create("test-extended-file", mode) {|f|
assert_nil(f.getc)
f.print "\na"
f.rewind
assert_equal("a", f.gets(""), "mode = <#{mode}>")
}
end
end
def test_each_char_extended_file
[nil, {:textmode=>true}, {:binmode=>true}].each do |mode|
Tempfile.create("test-extended-file", mode) {|f|
assert_nil(f.getc)
f.print "a"
f.rewind
result = []
f.each_char {|b| result << b }
assert_equal([?a], result, "mode = <#{mode}>")
}
end
end
def test_each_byte_extended_file
[nil, {:textmode=>true}, {:binmode=>true}].each do |mode|
Tempfile.create("test-extended-file", mode) {|f|
assert_nil(f.getc)
f.print "a"
f.rewind
result = []
f.each_byte {|b| result << b.chr }
assert_equal([?a], result, "mode = <#{mode}>")
}
end
end
def test_getc_extended_file
[nil, {:textmode=>true}, {:binmode=>true}].each do |mode|
Tempfile.create("test-extended-file", mode) {|f|
assert_nil(f.getc)
f.print "a"
f.rewind
assert_equal(?a, f.getc, "mode = <#{mode}>")
}
end
end
def test_getbyte_extended_file
[nil, {:textmode=>true}, {:binmode=>true}].each do |mode|
Tempfile.create("test-extended-file", mode) {|f|
assert_nil(f.getc)
f.print "a"
f.rewind
assert_equal(?a, f.getbyte.chr, "mode = <#{mode}>")
}
end
end
def test_s_chown
assert_nothing_raised { File.chown(-1, -1) }
assert_nothing_raised { File.chown nil, nil }
end
def test_chown
assert_nothing_raised {
File.open(__FILE__) {|f| f.chown(-1, -1) }
}
assert_nothing_raised("[ruby-dev:27140]") {
File.open(__FILE__) {|f| f.chown nil, nil }
}
end
def test_uninitialized
assert_raise(TypeError) { File::Stat.allocate.readable? }
assert_nothing_raised { File::Stat.allocate.inspect }
end
def test_realpath
Dir.mktmpdir('rubytest-realpath') {|tmpdir|
realdir = File.realpath(tmpdir)
tst = realdir + (File::SEPARATOR*3 + ".")
assert_equal(realdir, File.realpath(tst))
assert_equal(realdir, File.realpath(".", tst))
if File::ALT_SEPARATOR
bug2961 = '[ruby-core:28653]'
assert_equal(realdir, File.realpath(realdir.tr(File::SEPARATOR, File::ALT_SEPARATOR)), bug2961)
end
}
end
def test_realpath_encoding
fsenc = Encoding.find("filesystem")
nonascii = "\u{0391 0410 0531 10A0 05d0 2C00 3042}"
tst = "A"
nonascii.each_char {|c| tst << c.encode(fsenc) rescue nil}
Dir.mktmpdir('rubytest-realpath') {|tmpdir|
realdir = File.realpath(tmpdir)
open(File.join(tmpdir, tst), "w") {}
a = File.join(tmpdir, "x")
begin
File.symlink(tst, a)
rescue Errno::EACCES, Errno::EPERM
skip "need privilege"
end
assert_equal(File.join(realdir, tst), File.realpath(a))
File.unlink(a)
tst = "A" + nonascii
open(File.join(tmpdir, tst), "w") {}
File.symlink(tst, a)
assert_equal(File.join(realdir, tst), File.realpath(a.encode("UTF-8")))
}
end
def test_realpath_taintedness
Dir.mktmpdir('rubytest-realpath') {|tmpdir|
dir = File.realpath(tmpdir).untaint
File.write(File.join(dir, base = "test.file"), '')
base.taint
dir.taint
assert_predicate(File.realpath(base, dir), :tainted?)
base.untaint
dir.taint
assert_predicate(File.realpath(base, dir), :tainted?)
base.taint
dir.untaint
assert_predicate(File.realpath(base, dir), :tainted?)
base.untaint
dir.untaint
assert_not_predicate(File.realpath(base, dir), :tainted?)
assert_predicate(Dir.chdir(dir) {File.realpath(base)}, :tainted?)
}
end
def test_realdirpath
Dir.mktmpdir('rubytest-realdirpath') {|tmpdir|
realdir = File.realpath(tmpdir)
tst = realdir + (File::SEPARATOR*3 + ".")
assert_equal(realdir, File.realdirpath(tst))
assert_equal(realdir, File.realdirpath(".", tst))
assert_equal(File.join(realdir, "foo"), File.realdirpath("foo", tst))
}
begin
result = File.realdirpath("bar", "//:/foo")
rescue SystemCallError
else
if result.start_with?("//")
assert_equal("//:/foo/bar", result)
end
end
end
def test_realdirpath_junction
Dir.mktmpdir('rubytest-realpath') {|tmpdir|
Dir.chdir(tmpdir) do
Dir.mkdir('foo')
skip "cannot run mklink" unless system('mklink /j bar foo > nul')
assert_equal(File.realpath('foo'), File.realpath('bar'))
end
}
end if /mswin|mingw/ =~ RUBY_PLATFORM
def test_utime_with_minus_time_segv
bug5596 = '[ruby-dev:44838]'
assert_in_out_err([], <<-EOS, [bug5596], [])
require "tempfile"
t = Time.at(-1)
begin
Tempfile.create('test_utime_with_minus_time_segv') {|f|
File.utime(t, t, f)
}
rescue
end
puts '#{bug5596}'
EOS
end
def test_utime
bug6385 = '[ruby-core:44776]'
mod_time_contents = Time.at 1306527039
file = Tempfile.new("utime")
file.close
path = file.path
File.utime(File.atime(path), mod_time_contents, path)
stats = File.stat(path)
file.open
file_mtime = file.mtime
file.close(true)
assert_equal(mod_time_contents, file_mtime, bug6385)
assert_equal(mod_time_contents, stats.mtime, bug6385)
end
def test_stat
tb = Process.clock_gettime(Process::CLOCK_REALTIME)
Tempfile.create("stat") {|file|
tb = (tb + Process.clock_gettime(Process::CLOCK_REALTIME)) / 2
file.close
path = file.path
t0 = Process.clock_gettime(Process::CLOCK_REALTIME)
File.write(path, "foo")
sleep 2
File.write(path, "bar")
sleep 2
File.read(path)
File.chmod(0644, path)
sleep 2
File.read(path)
delta = 1
stat = File.stat(path)
assert_in_delta tb, stat.birthtime.to_f, delta
assert_in_delta t0+2, stat.mtime.to_f, delta
if stat.birthtime != stat.ctime
assert_in_delta t0+4, stat.ctime.to_f, delta
end
if /mswin|mingw/ !~ RUBY_PLATFORM && !Bug::File::Fs.noatime?(path)
# Windows delays updating atime
assert_in_delta t0+6, stat.atime.to_f, delta
end
}
rescue NotImplementedError
end
def test_stat_inode
assert_not_equal 0, File.stat(__FILE__).ino
end
def test_chmod_m17n
bug5671 = '[ruby-dev:44898]'
Dir.mktmpdir('test-file-chmod-m17n-') do |tmpdir|
file = File.join(tmpdir, "\u3042")
File.open(file, 'w'){}
assert_equal(File.chmod(0666, file), 1, bug5671)
end
end
def test_file_open_permissions
Dir.mktmpdir(__method__.to_s) do |tmpdir|
tmp = File.join(tmpdir, 'x')
File.open(tmp, :mode => IO::RDWR | IO::CREAT | IO::BINARY,
:encoding => Encoding::ASCII_8BIT) do |x|
assert_predicate(x, :autoclose?)
assert_equal Encoding::ASCII_8BIT, x.external_encoding
x.write 'hello'
x.seek 0, IO::SEEK_SET
assert_equal 'hello', x.read
end
end
end
def test_file_open_double_mode
assert_raise_with_message(ArgumentError, 'mode specified twice') {
File.open("a", 'w', :mode => 'rw+')
}
end
def test_file_share_delete
Dir.mktmpdir(__method__.to_s) do |tmpdir|
tmp = File.join(tmpdir, 'x')
File.open(tmp, mode: IO::WRONLY | IO::CREAT | IO::BINARY | IO::SHARE_DELETE) do |f|
assert_file.exist?(tmp)
assert_nothing_raised do
File.unlink(tmp)
end
end
assert_file.not_exist?(tmp)
end
end
def test_conflicting_encodings
Dir.mktmpdir(__method__.to_s) do |tmpdir|
tmp = File.join(tmpdir, 'x')
File.open(tmp, 'wb', :encoding => Encoding::EUC_JP) do |x|
assert_equal Encoding::EUC_JP, x.external_encoding
end
end
end
def test_untainted_path
bug5374 = '[ruby-core:39745]'
cwd = ("./"*40+".".taint).dup.untaint
in_safe = proc {|safe| $SAFE = safe; File.stat(cwd)}
assert_not_send([cwd, :tainted?])
(0..1).each do |level|
assert_nothing_raised(SecurityError, bug5374) {in_safe[level]}
end
ensure
$SAFE = 0
end
if /(bcc|ms|cyg)win|mingw|emx/ =~ RUBY_PLATFORM
def test_long_unc
feature3399 = '[ruby-core:30623]'
path = File.expand_path(__FILE__)
path.sub!(%r'\A//', 'UNC/')
assert_nothing_raised(Errno::ENOENT, feature3399) do
File.stat("//?/#{path}")
end
end
end
def test_open_nul
Dir.mktmpdir(__method__.to_s) do |tmpdir|
path = File.join(tmpdir, "foo")
assert_raise(ArgumentError) do
open(path + "\0bar", "w") {}
end
assert_file.not_exist?(path)
end
end
def test_open_tempfile_path
Dir.mktmpdir(__method__.to_s) do |tmpdir|
begin
io = File.open(tmpdir, File::RDWR | File::TMPFILE)
rescue Errno::EINVAL
skip 'O_TMPFILE not supported (EINVAL)'
rescue Errno::EOPNOTSUPP
skip 'O_TMPFILE not supported (EOPNOTSUPP)'
end
io.write "foo"
io.flush
assert_equal 3, io.size
assert_raise(IOError) { io.path }
ensure
io&.close
end
end if File::Constants.const_defined?(:TMPFILE)
end