ruby/test/dbm/test_dbm.rb
naruse 332938df51 merge revision(s) 61501,61758: [Backport #14481]
fix concurrent test.

	* test/rubygems/test_require.rb (test_concurrent_require):
	  Synchronizations should be in ensure clause. Sometimes
	  `require` fails (not sure why) and latch is not released.
	  Such case introduces unlimited awaiting.
	  This patch soleve this problem.


	skip some tests so that no failure occurs in root privilege

	Some tests had failed on `sudo make test-all`, mainly because root can
	access any files regardless of permission.  This change adds `skip`
	guards into such tests.

	Note that almost all tests in which `skip` guards is added, already have
	"windows" guard.  This is because there is no support to avoid read
	access by owner on Windows.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_5@62834 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-03-19 08:15:16 +00:00

634 lines
15 KiB
Ruby

# frozen_string_literal: true
require 'test/unit'
require 'tmpdir'
begin
require 'dbm'
rescue LoadError
end
if defined? DBM
require 'tmpdir'
require 'fileutils'
class TestDBM_RDONLY < Test::Unit::TestCase
def TestDBM_RDONLY.uname_s
require 'rbconfig'
case RbConfig::CONFIG['target_os']
when 'cygwin'
require 'etc'
Etc.uname[:sysname]
else
RbConfig::CONFIG['target_os']
end
end
SYSTEM = uname_s
def setup
@tmpdir = Dir.mktmpdir("tmptest_dbm")
@prefix = "tmptest_dbm_#{$$}"
@path = "#{@tmpdir}/#{@prefix}_"
# prepare to make readonly DBM file
DBM.open("#{@tmpdir}/#{@prefix}_rdonly") {|dbm|
dbm['foo'] = 'FOO'
}
File.chmod(0400, *Dir.glob("#{@tmpdir}/#{@prefix}_rdonly.*"))
assert_instance_of(DBM, @dbm_rdonly = DBM.new("#{@tmpdir}/#{@prefix}_rdonly", nil))
end
def teardown
assert_nil(@dbm_rdonly.close)
ObjectSpace.each_object(DBM) do |obj|
obj.close unless obj.closed?
end
FileUtils.remove_entry_secure @tmpdir
end
def test_delete_rdonly
skip("skipped because root can read anything") if Process.uid == 0
if /^CYGWIN_9/ !~ SYSTEM
assert_raise(DBMError) {
@dbm_rdonly.delete("foo")
}
assert_nil(@dbm_rdonly.delete("bar"))
end
end
def test_fetch_not_found
notfound = nil
result = Object.new
assert_same(result, @dbm_rdonly.fetch("bar") {|k| notfound = k; result})
assert_equal("bar", notfound)
assert_predicate(notfound, :tainted?)
end
end
class TestDBM < Test::Unit::TestCase
def setup
@tmpdir = Dir.mktmpdir("tmptest_dbm")
@prefix = "tmptest_dbm_#{$$}"
@path = "#{@tmpdir}/#{@prefix}_"
assert_instance_of(DBM, @dbm = DBM.new(@path))
end
def teardown
assert_nil(@dbm.close) unless @dbm.closed?
ObjectSpace.each_object(DBM) do |obj|
obj.close unless obj.closed?
end
FileUtils.remove_entry_secure @tmpdir
end
def check_size(expect, dbm=@dbm)
assert_equal(expect, dbm.size)
n = 0
dbm.each { n+=1 }
assert_equal(expect, n)
if expect == 0
assert_equal(true, dbm.empty?)
else
assert_equal(false, dbm.empty?)
end
end
def test_dbmfile_suffix
@dbm.close
prefix = File.basename(@path)
suffixes = Dir.entries(@tmpdir).grep(/\A#{Regexp.escape prefix}/) { $' }.sort
pagname = "#{@path}.pag"
dirname = "#{@path}.dir"
dbname = "#{@path}.db"
case DBM::VERSION
when /\bNDBM\b/
assert_equal(%w[.dir .pag], suffixes)
assert(File.zero?(pagname))
assert(File.zero?(dirname))
when /\bGDBM\b/
assert_equal(%w[.dir .pag], suffixes)
assert(!File.zero?(pagname))
assert(!File.zero?(dirname))
pag = File.binread(pagname, 16)
pag_magics = [
0x13579ace, # GDBM_OMAGIC
0x13579acd, # GDBM_MAGIC32
0x13579acf, # GDBM_MAGIC64
]
assert_operator(pag_magics, :include?,
pag.unpack("i")[0]) # native endian, native int.
if !File.identical?(pagname, dirname)
dir = File.binread(dirname, 16)
assert_equal("GDBM", dir[0, 4])
end
when /\bBerkeley DB\b/
assert_equal(%w[.db], suffixes)
assert(!File.zero?(dbname))
db = File.binread(dbname, 16)
assert(db[0,4].unpack("N") == [0x00061561] || # Berkeley DB 1
db[12,4].unpack("L") == [0x00061561]) # Berkeley DBM 2 or later.
when /\bQDBM\b/
assert_equal(%w[.dir .pag], suffixes)
assert(!File.zero?(pagname))
assert(!File.zero?(dirname))
dir = File.binread(dirname, 16)
assert_equal("[depot]\0\v", dir[0, 9])
pag = File.binread(pagname, 16)
if [1].pack("s") == "\x00\x01" # big endian
assert_equal("[DEPOT]\n\f", pag[0, 9])
else # little endian
assert_equal("[depot]\n\f", pag[0, 9])
end
end
if suffixes == %w[.db]
assert_match(/\bBerkeley DB\b/, DBM::VERSION)
end
end
def test_s_new_has_no_block
# DBM.new ignore the block
foo = true
assert_instance_of(DBM, dbm = DBM.new("#{@tmpdir}/#{@prefix}") { foo = false })
assert_equal(foo, true)
assert_nil(dbm.close)
end
def test_s_open_no_create
skip "dbm_open() is broken on libgdbm 1.8.0 or prior (#{DBM::VERSION})" if /GDBM version 1\.(?:[0-7]\b|8\.0)/ =~ DBM::VERSION
assert_nil(dbm = DBM.open("#{@tmpdir}/#{@prefix}", nil))
ensure
dbm.close if dbm
end
def test_s_open_with_block
assert_equal(DBM.open("#{@tmpdir}/#{@prefix}") { :foo }, :foo)
end
def test_close
assert_instance_of(DBM, dbm = DBM.open("#{@tmpdir}/#{@prefix}"))
assert_nil(dbm.close)
# closed DBM file
assert_raise(DBMError) { dbm.close }
end
def test_aref
assert_equal('bar', @dbm['foo'] = 'bar')
assert_equal('bar', @dbm['foo'])
assert_nil(@dbm['bar'])
end
def test_fetch
assert_equal('bar', @dbm['foo']='bar')
assert_equal('bar', @dbm.fetch('foo'))
# key not found
assert_raise(IndexError) {
@dbm.fetch('bar')
}
# test for `ifnone' arg
assert_equal('baz', @dbm.fetch('bar', 'baz'))
# test for `ifnone' block
assert_equal('foobar', @dbm.fetch('bar') {|key| 'foo' + key })
end
def test_aset
num = 0
2.times {|i|
assert_equal('foo', @dbm['foo'] = 'foo')
assert_equal('foo', @dbm['foo'])
assert_equal('bar', @dbm['foo'] = 'bar')
assert_equal('bar', @dbm['foo'])
num += 1 if i == 0
assert_equal(num, @dbm.size)
# assign nil
assert_equal('', @dbm['bar'] = '')
assert_equal('', @dbm['bar'])
num += 1 if i == 0
assert_equal(num, @dbm.size)
# empty string
assert_equal('', @dbm[''] = '')
assert_equal('', @dbm[''])
num += 1 if i == 0
assert_equal(num, @dbm.size)
# Integer
assert_equal('200', @dbm['100'] = '200')
assert_equal('200', @dbm['100'])
num += 1 if i == 0
assert_equal(num, @dbm.size)
# Big key and value
assert_equal('y' * 100, @dbm['x' * 100] = 'y' * 100)
assert_equal('y' * 100, @dbm['x' * 100])
num += 1 if i == 0
assert_equal(num, @dbm.size)
}
end
def test_key
assert_equal('bar', @dbm['foo'] = 'bar')
assert_equal('foo', @dbm.key('bar'))
assert_nil(@dbm['bar'])
end
def test_values_at
keys = %w(foo bar baz)
values = %w(FOO BAR BAZ)
@dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values
assert_equal(values.reverse, @dbm.values_at(*keys.reverse))
end
def test_select_with_block
keys = %w(foo bar baz)
values = %w(FOO BAR BAZ)
@dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values
ret = @dbm.select {|k,v|
assert_equal(k.upcase, v)
k != "bar"
}
assert_equal([['baz', 'BAZ'], ['foo', 'FOO']],
ret.sort)
end
def test_length
num = 10
assert_equal(0, @dbm.size)
num.times {|i|
i = i.to_s
@dbm[i] = i
}
assert_equal(num, @dbm.size)
@dbm.shift
assert_equal(num - 1, @dbm.size)
end
def test_empty?
assert_equal(true, @dbm.empty?)
@dbm['foo'] = 'FOO'
assert_equal(false, @dbm.empty?)
end
def test_each_pair
n = 0
@dbm.each_pair { n += 1 }
assert_equal(0, n)
keys = %w(foo bar baz)
values = %w(FOO BAR BAZ)
@dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values
n = 0
ret = @dbm.each_pair {|key, val|
assert_not_nil(i = keys.index(key))
assert_equal(val, values[i])
n += 1
}
assert_equal(keys.size, n)
assert_equal(@dbm, ret)
end
def test_each_value
n = 0
@dbm.each_value { n += 1 }
assert_equal(0, n)
keys = %w(foo bar baz)
values = %w(FOO BAR BAZ)
@dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values
n = 0
ret = @dbm.each_value {|val|
assert_not_nil(key = @dbm.key(val))
assert_not_nil(i = keys.index(key))
assert_equal(val, values[i])
n += 1
}
assert_equal(keys.size, n)
assert_equal(@dbm, ret)
end
def test_each_key
n = 0
@dbm.each_key { n += 1 }
assert_equal(0, n)
keys = %w(foo bar baz)
values = %w(FOO BAR BAZ)
@dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values
n = 0
ret = @dbm.each_key {|key|
assert_not_nil(i = keys.index(key))
assert_equal(@dbm[key], values[i])
n += 1
}
assert_equal(keys.size, n)
assert_equal(@dbm, ret)
end
def test_keys
assert_equal([], @dbm.keys)
keys = %w(foo bar baz)
values = %w(FOO BAR BAZ)
@dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values
assert_equal(keys.sort, @dbm.keys.sort)
assert_equal(values.sort, @dbm.values.sort)
end
def test_values
test_keys
end
def test_shift
assert_nil(@dbm.shift)
assert_equal(0, @dbm.size)
keys = %w(foo bar baz)
values = %w(FOO BAR BAZ)
@dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values
ret_keys = []
ret_values = []
while ret = @dbm.shift
ret_keys.push ret[0]
ret_values.push ret[1]
assert_equal(keys.size - ret_keys.size, @dbm.size)
end
assert_equal(keys.sort, ret_keys.sort)
assert_equal(values.sort, ret_values.sort)
end
def test_delete
keys = %w(foo bar baz)
values = %w(FOO BAR BAZ)
key = keys[1]
assert_nil(@dbm.delete(key))
assert_equal(0, @dbm.size)
@dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values
assert_equal('BAR', @dbm.delete(key))
assert_nil(@dbm[key])
assert_equal(2, @dbm.size)
assert_nil(@dbm.delete(key))
end
def test_delete_with_block
key = 'no called block'
@dbm[key] = 'foo'
assert_equal('foo', @dbm.delete(key) {|k| k.replace 'called block'; :blockval})
assert_equal(0, @dbm.size)
key = 'no called block'.dup
assert_equal(:blockval, @dbm.delete(key) {|k| k.replace 'called block'; :blockval})
assert_equal(0, @dbm.size)
end
def test_delete_if
v = "0"
100.times {@dbm[v] = v; v = v.next}
ret = @dbm.delete_if {|key, val| key.to_i < 50}
assert_equal(@dbm, ret)
check_size(50, @dbm)
ret = @dbm.delete_if {|key, val| key.to_i >= 50}
assert_equal(@dbm, ret)
check_size(0, @dbm)
# break
v = "0"
100.times {@dbm[v] = v; v = v.next}
check_size(100, @dbm)
n = 0;
@dbm.delete_if {|key, val|
break if n > 50
n+=1
true
}
assert_equal(51, n)
check_size(49, @dbm)
@dbm.clear
# raise
v = "0"
100.times {@dbm[v] = v; v = v.next}
check_size(100, @dbm)
n = 0;
begin
@dbm.delete_if {|key, val|
raise "runtime error" if n > 50
n+=1
true
}
rescue RuntimeError
end
assert_equal(51, n)
check_size(49, @dbm)
end
def test_reject
v = "0"
100.times {@dbm[v] = v; v = v.next}
hash = @dbm.reject {|key, val| key.to_i < 50}
assert_instance_of(Hash, hash)
assert_equal(100, @dbm.size)
assert_equal(50, hash.size)
hash.each_pair {|key,val|
assert_equal(false, key.to_i < 50)
assert_equal(key, val)
}
hash = @dbm.reject {|key, val| key.to_i < 100}
assert_instance_of(Hash, hash)
assert_equal(true, hash.empty?)
end
def test_clear
v = "1"
100.times {v = v.next; @dbm[v] = v}
assert_equal(@dbm, @dbm.clear)
# validate DBM#size
i = 0
@dbm.each { i += 1 }
assert_equal(@dbm.size, i)
assert_equal(0, i)
end
def test_invert
v = "0"
100.times {@dbm[v] = v; v = v.next}
hash = @dbm.invert
assert_instance_of(Hash, hash)
assert_equal(100, hash.size)
hash.each_pair {|key, val|
assert_equal(key.to_i, val.to_i)
}
end
def test_update
hash = {}
v = "0"
100.times {v = v.next; hash[v] = v}
@dbm["101"] = "101"
@dbm.update hash
assert_equal(101, @dbm.size)
@dbm.each_pair {|key, val|
assert_equal(key.to_i, val.to_i)
}
end
def test_replace
hash = {}
v = "0"
100.times {v = v.next; hash[v] = v}
@dbm["101"] = "101"
@dbm.replace hash
assert_equal(100, @dbm.size)
@dbm.each_pair {|key, val|
assert_equal(key.to_i, val.to_i)
}
end
def test_haskey?
assert_equal('bar', @dbm['foo']='bar')
assert_equal(true, @dbm.has_key?('foo'))
assert_equal(false, @dbm.has_key?('bar'))
end
def test_has_value?
assert_equal('bar', @dbm['foo']='bar')
assert_equal(true, @dbm.has_value?('bar'))
assert_equal(false, @dbm.has_value?('foo'))
end
def test_to_a
v = "0"
100.times {v = v.next; @dbm[v] = v}
ary = @dbm.to_a
assert_instance_of(Array, ary)
assert_equal(100, ary.size)
ary.each {|key,val|
assert_equal(key.to_i, val.to_i)
}
end
def test_to_hash
v = "0"
100.times {v = v.next; @dbm[v] = v}
hash = @dbm.to_hash
assert_instance_of(Hash, hash)
assert_equal(100, hash.size)
hash.each {|key,val|
assert_equal(key.to_i, val.to_i)
}
end
end
class TestDBM2 < Test::Unit::TestCase
def setup
@tmproot = Dir.mktmpdir('ruby-dbm')
end
def teardown
FileUtils.remove_entry_secure @tmproot if File.directory?(@tmproot)
end
def test_version
assert_instance_of(String, DBM::VERSION)
end
def test_reader_open_notexist
assert_raise(Errno::ENOENT) {
DBM.open("#{@tmproot}/a", 0666, DBM::READER)
}
end
def test_writer_open_notexist
skip "dbm_open() is broken on libgdbm 1.8.0 or prior (#{DBM::VERSION})" if /GDBM version 1\.(?:[0-7]\b|8\.0)/ =~ DBM::VERSION
assert_raise(Errno::ENOENT) {
DBM.open("#{@tmproot}/a", 0666, DBM::WRITER)
}
end
def test_wrcreat_open_notexist
v = DBM.open("#{@tmproot}/a", 0666, DBM::WRCREAT)
assert_instance_of(DBM, v)
v.close
end
def test_newdb_open_notexist
v = DBM.open("#{@tmproot}/a", 0666, DBM::NEWDB)
assert_instance_of(DBM, v)
v.close
end
def test_reader_open
DBM.open("#{@tmproot}/a") {} # create a db.
v = DBM.open("#{@tmproot}/a", nil, DBM::READER) {|d|
# Errno::EPERM is raised on Solaris which use ndbm.
# DBMError is raised on Debian which use gdbm.
assert_raise(Errno::EPERM, DBMError) { d["k"] = "v" }
true
}
assert(v)
end
def test_newdb_open
DBM.open("#{@tmproot}/a") {|dbm|
dbm["k"] = "v"
}
v = DBM.open("#{@tmproot}/a", nil, DBM::NEWDB) {|d|
assert_equal(0, d.length)
assert_nil(d["k"])
true
}
assert(v)
end
def test_freeze
DBM.open("#{@tmproot}/a") {|d|
d.freeze
assert_raise(FrozenError) { d["k"] = "v" }
}
end
end
end