PageRenderTime 54ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/test/dbm/test_dbm.rb

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