1require 'test/unit' 2require 'tmpdir' 3 4begin 5 require 'dbm' 6rescue LoadError 7end 8 9if defined? DBM 10 require 'tmpdir' 11 require 'fileutils' 12 13 class TestDBM_RDONLY < Test::Unit::TestCase 14 def TestDBM_RDONLY.uname_s 15 require 'rbconfig' 16 case RbConfig::CONFIG['target_os'] 17 when 'cygwin' 18 require 'Win32API' 19 uname = Win32API.new('cygwin1', 'uname', 'P', 'I') 20 utsname = ' ' * 100 21 raise 'cannot get system name' if uname.call(utsname) == -1 22 23 utsname.unpack('A20' * 5)[0] 24 else 25 RbConfig::CONFIG['target_os'] 26 end 27 end 28 SYSTEM = uname_s 29 30 def setup 31 @tmpdir = Dir.mktmpdir("tmptest_dbm") 32 @prefix = "tmptest_dbm_#{$$}" 33 @path = "#{@tmpdir}/#{@prefix}_" 34 35 # prepare to make readonly DBM file 36 DBM.open("#{@tmpdir}/#{@prefix}_rdonly") {|dbm| 37 dbm['foo'] = 'FOO' 38 } 39 40 File.chmod(0400, *Dir.glob("#{@tmpdir}/#{@prefix}_rdonly.*")) 41 42 assert_instance_of(DBM, @dbm_rdonly = DBM.new("#{@tmpdir}/#{@prefix}_rdonly", nil)) 43 end 44 def teardown 45 assert_nil(@dbm_rdonly.close) 46 ObjectSpace.each_object(DBM) do |obj| 47 obj.close unless obj.closed? 48 end 49 FileUtils.remove_entry_secure @tmpdir 50 end 51 52 def test_delete_rdonly 53 if /^CYGWIN_9/ !~ SYSTEM 54 assert_raise(DBMError) { 55 @dbm_rdonly.delete("foo") 56 } 57 58 assert_nil(@dbm_rdonly.delete("bar")) 59 end 60 end 61 end 62 63 class TestDBM < Test::Unit::TestCase 64 def setup 65 @tmpdir = Dir.mktmpdir("tmptest_dbm") 66 @prefix = "tmptest_dbm_#{$$}" 67 @path = "#{@tmpdir}/#{@prefix}_" 68 assert_instance_of(DBM, @dbm = DBM.new(@path)) 69 end 70 def teardown 71 assert_nil(@dbm.close) unless @dbm.closed? 72 ObjectSpace.each_object(DBM) do |obj| 73 obj.close unless obj.closed? 74 end 75 FileUtils.remove_entry_secure @tmpdir 76 end 77 78 def check_size(expect, dbm=@dbm) 79 assert_equal(expect, dbm.size) 80 n = 0 81 dbm.each { n+=1 } 82 assert_equal(expect, n) 83 if expect == 0 84 assert_equal(true, dbm.empty?) 85 else 86 assert_equal(false, dbm.empty?) 87 end 88 end 89 90 def have_fork? 91 begin 92 fork{} 93 true 94 rescue NotImplementedError 95 false 96 end 97 end 98 99 def test_dbmfile_suffix 100 @dbm.close 101 prefix = File.basename(@path) 102 suffixes = Dir.entries(@tmpdir).grep(/\A#{Regexp.escape prefix}/) { $' }.sort 103 pagname = "#{@path}.pag" 104 dirname = "#{@path}.dir" 105 dbname = "#{@path}.db" 106 case DBM::VERSION 107 when /\bNDBM\b/ 108 assert_equal(%w[.dir .pag], suffixes) 109 assert(File.zero?(pagname)) 110 assert(File.zero?(dirname)) 111 when /\bGDBM\b/ 112 assert_equal(%w[.dir .pag], suffixes) 113 assert(!File.zero?(pagname)) 114 assert(!File.zero?(dirname)) 115 pag = File.binread(pagname, 16) 116 pag_magics = [ 117 0x13579ace, # GDBM_OMAGIC 118 0x13579acd, # GDBM_MAGIC32 119 0x13579acf, # GDBM_MAGIC64 120 ] 121 assert_operator(pag_magics, :include?, 122 pag.unpack("i")[0]) # native endian, native int. 123 if !File.identical?(pagname, dirname) 124 dir = File.binread(dirname, 16) 125 assert_equal("GDBM", dir[0, 4]) 126 end 127 when /\bBerkeley DB\b/ 128 assert_equal(%w[.db], suffixes) 129 assert(!File.zero?(dbname)) 130 db = File.binread(dbname, 16) 131 assert(db[0,4].unpack("N") == [0x00061561] || # Berkeley DB 1 132 db[12,4].unpack("L") == [0x00061561]) # Berkeley DBM 2 or later. 133 when /\bQDBM\b/ 134 assert_equal(%w[.dir .pag], suffixes) 135 assert(!File.zero?(pagname)) 136 assert(!File.zero?(dirname)) 137 dir = File.binread(dirname, 16) 138 assert_equal("[depot]\0\v", dir[0, 9]) 139 pag = File.binread(pagname, 16) 140 if [1].pack("s") == "\x00\x01" # big endian 141 assert_equal("[DEPOT]\n\f", pag[0, 9]) 142 else # little endian 143 assert_equal("[depot]\n\f", pag[0, 9]) 144 end 145 end 146 if suffixes == %w[.db] 147 assert_match(/\bBerkeley DB\b/, DBM::VERSION) 148 end 149 end 150 151 def test_s_new_has_no_block 152 # DBM.new ignore the block 153 foo = true 154 assert_instance_of(DBM, dbm = DBM.new("#{@tmpdir}/#{@prefix}") { foo = false }) 155 assert_equal(foo, true) 156 assert_nil(dbm.close) 157 end 158 159 def test_s_open_no_create 160 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 161 assert_nil(dbm = DBM.open("#{@tmpdir}/#{@prefix}", nil)) 162 ensure 163 dbm.close if dbm 164 end 165 166 def test_s_open_with_block 167 assert_equal(DBM.open("#{@tmpdir}/#{@prefix}") { :foo }, :foo) 168 end 169 170 def test_close 171 assert_instance_of(DBM, dbm = DBM.open("#{@tmpdir}/#{@prefix}")) 172 assert_nil(dbm.close) 173 174 # closed DBM file 175 assert_raise(DBMError) { dbm.close } 176 end 177 178 def test_aref 179 assert_equal('bar', @dbm['foo'] = 'bar') 180 assert_equal('bar', @dbm['foo']) 181 182 assert_nil(@dbm['bar']) 183 end 184 185 def test_fetch 186 assert_equal('bar', @dbm['foo']='bar') 187 assert_equal('bar', @dbm.fetch('foo')) 188 189 # key not found 190 assert_raise(IndexError) { 191 @dbm.fetch('bar') 192 } 193 194 # test for `ifnone' arg 195 assert_equal('baz', @dbm.fetch('bar', 'baz')) 196 197 # test for `ifnone' block 198 assert_equal('foobar', @dbm.fetch('bar') {|key| 'foo' + key }) 199 end 200 201 def test_aset 202 num = 0 203 2.times {|i| 204 assert_equal('foo', @dbm['foo'] = 'foo') 205 assert_equal('foo', @dbm['foo']) 206 assert_equal('bar', @dbm['foo'] = 'bar') 207 assert_equal('bar', @dbm['foo']) 208 209 num += 1 if i == 0 210 assert_equal(num, @dbm.size) 211 212 # assign nil 213 assert_equal('', @dbm['bar'] = '') 214 assert_equal('', @dbm['bar']) 215 216 num += 1 if i == 0 217 assert_equal(num, @dbm.size) 218 219 # empty string 220 assert_equal('', @dbm[''] = '') 221 assert_equal('', @dbm['']) 222 223 num += 1 if i == 0 224 assert_equal(num, @dbm.size) 225 226 # Fixnum 227 assert_equal('200', @dbm['100'] = '200') 228 assert_equal('200', @dbm['100']) 229 230 num += 1 if i == 0 231 assert_equal(num, @dbm.size) 232 233 # Big key and value 234 assert_equal('y' * 100, @dbm['x' * 100] = 'y' * 100) 235 assert_equal('y' * 100, @dbm['x' * 100]) 236 237 num += 1 if i == 0 238 assert_equal(num, @dbm.size) 239 } 240 end 241 242 def test_key 243 assert_equal('bar', @dbm['foo'] = 'bar') 244 assert_equal('foo', @dbm.key('bar')) 245 assert_nil(@dbm['bar']) 246 end 247 248 def test_values_at 249 keys = %w(foo bar baz) 250 values = %w(FOO BAR BAZ) 251 @dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values 252 assert_equal(values.reverse, @dbm.values_at(*keys.reverse)) 253 end 254 255 def test_select_with_block 256 keys = %w(foo bar baz) 257 values = %w(FOO BAR BAZ) 258 @dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values 259 ret = @dbm.select {|k,v| 260 assert_equal(k.upcase, v) 261 k != "bar" 262 } 263 assert_equal([['baz', 'BAZ'], ['foo', 'FOO']], 264 ret.sort) 265 end 266 267 def test_length 268 num = 10 269 assert_equal(0, @dbm.size) 270 num.times {|i| 271 i = i.to_s 272 @dbm[i] = i 273 } 274 assert_equal(num, @dbm.size) 275 276 @dbm.shift 277 278 assert_equal(num - 1, @dbm.size) 279 end 280 281 def test_empty? 282 assert_equal(true, @dbm.empty?) 283 @dbm['foo'] = 'FOO' 284 assert_equal(false, @dbm.empty?) 285 end 286 287 def test_each_pair 288 n = 0 289 @dbm.each_pair { n += 1 } 290 assert_equal(0, n) 291 292 keys = %w(foo bar baz) 293 values = %w(FOO BAR BAZ) 294 295 @dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values 296 297 n = 0 298 ret = @dbm.each_pair {|key, val| 299 assert_not_nil(i = keys.index(key)) 300 assert_equal(val, values[i]) 301 302 n += 1 303 } 304 assert_equal(keys.size, n) 305 assert_equal(@dbm, ret) 306 end 307 308 def test_each_value 309 n = 0 310 @dbm.each_value { n += 1 } 311 assert_equal(0, n) 312 313 keys = %w(foo bar baz) 314 values = %w(FOO BAR BAZ) 315 316 @dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values 317 318 n = 0 319 ret = @dbm.each_value {|val| 320 assert_not_nil(key = @dbm.key(val)) 321 assert_not_nil(i = keys.index(key)) 322 assert_equal(val, values[i]) 323 324 n += 1 325 } 326 assert_equal(keys.size, n) 327 assert_equal(@dbm, ret) 328 end 329 330 def test_each_key 331 n = 0 332 @dbm.each_key { n += 1 } 333 assert_equal(0, n) 334 335 keys = %w(foo bar baz) 336 values = %w(FOO BAR BAZ) 337 338 @dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values 339 340 n = 0 341 ret = @dbm.each_key {|key| 342 assert_not_nil(i = keys.index(key)) 343 assert_equal(@dbm[key], values[i]) 344 345 n += 1 346 } 347 assert_equal(keys.size, n) 348 assert_equal(@dbm, ret) 349 end 350 351 def test_keys 352 assert_equal([], @dbm.keys) 353 354 keys = %w(foo bar baz) 355 values = %w(FOO BAR BAZ) 356 357 @dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values 358 359 assert_equal(keys.sort, @dbm.keys.sort) 360 assert_equal(values.sort, @dbm.values.sort) 361 end 362 363 def test_values 364 test_keys 365 end 366 367 def test_shift 368 assert_nil(@dbm.shift) 369 assert_equal(0, @dbm.size) 370 371 keys = %w(foo bar baz) 372 values = %w(FOO BAR BAZ) 373 374 @dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values 375 376 ret_keys = [] 377 ret_values = [] 378 while ret = @dbm.shift 379 ret_keys.push ret[0] 380 ret_values.push ret[1] 381 382 assert_equal(keys.size - ret_keys.size, @dbm.size) 383 end 384 385 assert_equal(keys.sort, ret_keys.sort) 386 assert_equal(values.sort, ret_values.sort) 387 end 388 389 def test_delete 390 keys = %w(foo bar baz) 391 values = %w(FOO BAR BAZ) 392 key = keys[1] 393 394 assert_nil(@dbm.delete(key)) 395 assert_equal(0, @dbm.size) 396 397 @dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values 398 399 assert_equal('BAR', @dbm.delete(key)) 400 assert_nil(@dbm[key]) 401 assert_equal(2, @dbm.size) 402 403 assert_nil(@dbm.delete(key)) 404 end 405 406 def test_delete_with_block 407 key = 'no called block' 408 @dbm[key] = 'foo' 409 assert_equal('foo', @dbm.delete(key) {|k| k.replace 'called block'; :blockval}) 410 assert_equal(0, @dbm.size) 411 412 key = 'no called block' 413 assert_equal(:blockval, @dbm.delete(key) {|k| k.replace 'called block'; :blockval}) 414 assert_equal(0, @dbm.size) 415 end 416 417 def test_delete_if 418 v = "0" 419 100.times {@dbm[v] = v; v = v.next} 420 421 ret = @dbm.delete_if {|key, val| key.to_i < 50} 422 assert_equal(@dbm, ret) 423 check_size(50, @dbm) 424 425 ret = @dbm.delete_if {|key, val| key.to_i >= 50} 426 assert_equal(@dbm, ret) 427 check_size(0, @dbm) 428 429 # break 430 v = "0" 431 100.times {@dbm[v] = v; v = v.next} 432 check_size(100, @dbm) 433 n = 0; 434 @dbm.delete_if {|key, val| 435 break if n > 50 436 n+=1 437 true 438 } 439 assert_equal(51, n) 440 check_size(49, @dbm) 441 442 @dbm.clear 443 444 # raise 445 v = "0" 446 100.times {@dbm[v] = v; v = v.next} 447 check_size(100, @dbm) 448 n = 0; 449 begin 450 @dbm.delete_if {|key, val| 451 raise "runtime error" if n > 50 452 n+=1 453 true 454 } 455 rescue 456 end 457 assert_equal(51, n) 458 check_size(49, @dbm) 459 end 460 461 def test_reject 462 v = "0" 463 100.times {@dbm[v] = v; v = v.next} 464 465 hash = @dbm.reject {|key, val| key.to_i < 50} 466 assert_instance_of(Hash, hash) 467 assert_equal(100, @dbm.size) 468 469 assert_equal(50, hash.size) 470 hash.each_pair {|key,val| 471 assert_equal(false, key.to_i < 50) 472 assert_equal(key, val) 473 } 474 475 hash = @dbm.reject {|key, val| key.to_i < 100} 476 assert_instance_of(Hash, hash) 477 assert_equal(true, hash.empty?) 478 end 479 480 def test_clear 481 v = "1" 482 100.times {v = v.next; @dbm[v] = v} 483 484 assert_equal(@dbm, @dbm.clear) 485 486 # validate DBM#size 487 i = 0 488 @dbm.each { i += 1 } 489 assert_equal(@dbm.size, i) 490 assert_equal(0, i) 491 end 492 493 def test_invert 494 v = "0" 495 100.times {@dbm[v] = v; v = v.next} 496 497 hash = @dbm.invert 498 assert_instance_of(Hash, hash) 499 assert_equal(100, hash.size) 500 hash.each_pair {|key, val| 501 assert_equal(key.to_i, val.to_i) 502 } 503 end 504 505 def test_update 506 hash = {} 507 v = "0" 508 100.times {v = v.next; hash[v] = v} 509 510 @dbm["101"] = "101" 511 @dbm.update hash 512 assert_equal(101, @dbm.size) 513 @dbm.each_pair {|key, val| 514 assert_equal(key.to_i, val.to_i) 515 } 516 end 517 518 def test_replace 519 hash = {} 520 v = "0" 521 100.times {v = v.next; hash[v] = v} 522 523 @dbm["101"] = "101" 524 @dbm.replace hash 525 assert_equal(100, @dbm.size) 526 @dbm.each_pair {|key, val| 527 assert_equal(key.to_i, val.to_i) 528 } 529 end 530 531 def test_haskey? 532 assert_equal('bar', @dbm['foo']='bar') 533 assert_equal(true, @dbm.has_key?('foo')) 534 assert_equal(false, @dbm.has_key?('bar')) 535 end 536 537 def test_has_value? 538 assert_equal('bar', @dbm['foo']='bar') 539 assert_equal(true, @dbm.has_value?('bar')) 540 assert_equal(false, @dbm.has_value?('foo')) 541 end 542 543 def test_to_a 544 v = "0" 545 100.times {v = v.next; @dbm[v] = v} 546 547 ary = @dbm.to_a 548 assert_instance_of(Array, ary) 549 assert_equal(100, ary.size) 550 ary.each {|key,val| 551 assert_equal(key.to_i, val.to_i) 552 } 553 end 554 555 def test_to_hash 556 v = "0" 557 100.times {v = v.next; @dbm[v] = v} 558 559 hash = @dbm.to_hash 560 assert_instance_of(Hash, hash) 561 assert_equal(100, hash.size) 562 hash.each {|key,val| 563 assert_equal(key.to_i, val.to_i) 564 } 565 end 566 end 567 568 class TestDBM2 < Test::Unit::TestCase 569 def setup 570 @tmproot = Dir.mktmpdir('ruby-dbm') 571 end 572 573 def teardown 574 FileUtils.remove_entry_secure @tmproot if File.directory?(@tmproot) 575 end 576 577 def test_version 578 assert_instance_of(String, DBM::VERSION) 579 end 580 581 def test_reader_open_notexist 582 assert_raise(Errno::ENOENT) { 583 DBM.open("#{@tmproot}/a", 0666, DBM::READER) 584 } 585 end 586 587 def test_writer_open_notexist 588 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 589 assert_raise(Errno::ENOENT) { 590 DBM.open("#{@tmproot}/a", 0666, DBM::WRITER) 591 } 592 end 593 594 def test_wrcreat_open_notexist 595 v = DBM.open("#{@tmproot}/a", 0666, DBM::WRCREAT) 596 assert_instance_of(DBM, v) 597 v.close 598 end 599 600 def test_newdb_open_notexist 601 v = DBM.open("#{@tmproot}/a", 0666, DBM::NEWDB) 602 assert_instance_of(DBM, v) 603 v.close 604 end 605 606 def test_reader_open 607 DBM.open("#{@tmproot}/a") {} # create a db. 608 v = DBM.open("#{@tmproot}/a", nil, DBM::READER) {|d| 609 # Errno::EPERM is raised on Solaris which use ndbm. 610 # DBMError is raised on Debian which use gdbm. 611 assert_raise(Errno::EPERM, DBMError) { d["k"] = "v" } 612 true 613 } 614 assert(v) 615 end 616 617 def test_newdb_open 618 DBM.open("#{@tmproot}/a") {|dbm| 619 dbm["k"] = "v" 620 } 621 v = DBM.open("#{@tmproot}/a", nil, DBM::NEWDB) {|d| 622 assert_equal(0, d.length) 623 assert_nil(d["k"]) 624 true 625 } 626 assert(v) 627 end 628 629 def test_freeze 630 DBM.open("#{@tmproot}/a") {|d| 631 d.freeze 632 assert_raise(RuntimeError) { d["k"] = "v" } 633 } 634 end 635 end 636end 637