# -*- encoding: binary -*- $stdout.sync = $stderr.sync = true require 'test/unit' require 'tempfile' $-w = true require 'tdb' class TestTdb < Test::Unit::TestCase def setup @tmp = @tdb = nil end def teardown @tmp.close! if @tmp.respond_to?(:close!) @tdb.close if @tdb && ! @tdb.closed? end def test_create_file_null_byte_in_path assert_raises(ArgumentError) { TDB.new("hello.tdb\0") } end def test_create_file assert_nothing_raised do @tmp = Tempfile.new('tdb') File.unlink(@tmp.path) end @tdb = TDB.new(@tmp.path) assert_kind_of TDB, @tdb assert File.exist?(@tmp.path) end def test_to_a @tdb = TDB.new(nil) assert_equal [], @tdb.to_a end def test_each @tdb = TDB.new(nil) @tdb["X"] = "Y" tmp = [] rc = @tdb.each { |k,v| tmp << [k, v ] } assert_equal([ %w(X Y) ], tmp) assert_equal @tdb.object_id, rc.object_id tmp = [] assert_raises(EOFError) { @tdb.each { |k,v| raise EOFError, "FOO"; tmp << [ k, v ] } } assert tmp.empty? tmp = [] rc = catch(:zzz) { @tdb.each { |k,v| throw(:zzz, "FOO"); tmp << [ k, v ] } } assert_equal rc, "FOO" assert tmp.empty? end def test_each_bigger @tdb = TDB.new(nil) @tdb["a"] = "A" @tdb["b"] = "B" @tdb["c"] = "C" tmp = [] rc = @tdb.each { |k,v| tmp << [k, v ] } assert_equal 3, tmp.size assert_equal @tdb.object_id, rc.object_id tmp = [] assert_raises(EOFError) { @tdb.each { |k,v| tmp << [ k, v ] raise EOFError, "FOO" } } assert_equal 1, tmp.size tmp = [] rc = catch(:zzz) { @tdb.each { |k,v| tmp << [ k, v ] throw(:zzz, "FOO") } } assert_equal rc, "FOO" assert_equal 1, tmp.size end def test_memory assert_nothing_raised do @tdb = TDB.new(nil) end assert ! @tdb.closed? assert_nil @tdb.close assert @tdb.closed? assert_raises(IOError) { @tdb.close } end def test_delete @tdb = TDB.new(nil) @tdb["hello"] = "X" assert_equal "X", @tdb.delete("hello") assert_nil @tdb["hello"] assert_nil @tdb.fetch("hello") assert_nil @tdb.delete("hello") @tdb["hello"] = "world" assert_equal "world", @tdb.delete("hello") assert_nil @tdb.delete("hello") end def test_nuke! @tdb = TDB.new(nil) assert_equal false, @tdb.nuke!("hello") @tdb["hello"] = "world" assert_equal true, @tdb.nuke!("hello") assert ! @tdb.include?("hello") assert_equal false, @tdb.nuke!("hello") end def test_exists? @tdb = TDB.new(nil) assert_equal false, @tdb.key?("hello") assert_equal false, @tdb.include?("hello") @tdb["hello"] = "world" assert_equal true, @tdb.key?("hello") end def test_store_fetch_mem @tdb = TDB.new(nil) assert_nothing_raised { @tdb["hello"] = "world" } assert_equal "world", @tdb["hello"] @tdb.store("hello", "Z") assert_equal "Z", @tdb["hello"] assert_equal "Z", @tdb.fetch("hello") end def test_store_modify_mem @tdb = TDB.new(nil) assert_nothing_raised { @tdb["hello"] = "world" } assert_equal "world", @tdb["hello"] assert_equal "Z", @tdb.modify("hello", "Z") assert_equal "Z", @tdb["hello"] assert_nil @tdb.modify("none", "Z") assert_raises(TDB::ERR::NOEXIST) { @tdb.modify!("none", "Z") } end def test_store_insert_mem @tdb = TDB.new(nil) assert_equal "world", @tdb.insert("hello", "world") assert_equal "world", @tdb["hello"] assert_nil @tdb.insert("hello", "Z") assert_raises(TDB::ERR::EXISTS) { @tdb.insert!("hello", "Z") } assert_equal "world", @tdb["hello"] end def test_gc assert_nothing_raised do 100000.times { TDB.new(nil) } 100000.times { TDB.new(Tempfile.new('tdb').path) } end end if ENV["TEST_GC"] def test_new_with_hash_size assert_nothing_raised { TDB.new(nil, :hash_size => 6) } assert_raises(TypeError) { TDB.new(nil, :hash_size => "6") } end def test_const assert_equal 0, TDB::DEFAULT assert_equal 1, TDB::CLEAR_IF_FIRST end def test_new_with_open_flags @tmp = Tempfile.new('tdb_excl') assert_raises(Errno::EEXIST) { TDB.new(@tmp.path, :open_flags => IO::EXCL|IO::CREAT|IO::RDWR) } File.unlink(@tmp.path) assert_nothing_raised { @tdb = TDB.new(@tmp.path, :open_flags => IO::EXCL|IO::CREAT|IO::RDWR) } end def test_open_with_tdb_flags assert_nothing_raised do @tdb = TDB.new("/non/existent/file", :tdb_flags => TDB::INTERNAL) end end def test_alternate_hashes results = {} expect = TDB::HASHES.to_a.map { |k,v| [ k.to_s, v.to_s ] }.sort %w(default siphash24 jenkins_lookup3 djb2 djb3 fnv1a murmur1 murmur1_aligned murmur2 murmur2a murmur2_aligned murmur3a murmur3f).each do |h| assert_nothing_raised do tdb = TDB.new(nil, :hash => h.to_sym) TDB::HASHES.each do |k,v| tdb[k.to_s] = v.to_s end assert_equal expect, tdb.to_a.sort assert_nil tdb.close end assert_raises(ArgumentError) do TDB.new(nil, :hash => h) end end end def test_lock_unlock_all @tmp = Tempfile.new('tdb') File.unlink(@tmp.path) @tdb = TDB.new(@tmp.path) assert_equal true, @tdb.lockall assert_equal true, @tdb.unlockall assert_raises(TDB::ERR::LOCK) { @tdb.unlockall } end def test_read_locks @tmp = Tempfile.new('tdb') File.unlink(@tmp.path) @tdb = TDB.new(@tmp.path) assert_equal true, @tdb.lockall_read assert_equal true, @tdb.unlockall_read assert_raises(TDB::ERR::LOCK) { @tdb.unlockall_read } assert_equal true, @tdb.trylockall_read assert_equal true, @tdb.unlockall_read assert_raises(TDB::ERR::LOCK) { @tdb.unlockall_read } end def test_mark_locks @tmp = Tempfile.new('tdb') File.unlink(@tmp.path) @tdb = TDB.new(@tmp.path) assert_equal true, @tdb.lockall_mark assert_equal true, @tdb.lockall_unmark assert_raises(TDB::ERR::LOCK) { @tdb.lockall_unmark } end def test_trylockall @tmp = Tempfile.new('tdb') File.unlink(@tmp.path) @tdb = TDB.new(@tmp.path) ard, awr = IO.pipe brd, bwr = IO.pipe pid = fork do @tdb.close ard.close bwr.close tdb = TDB.new(@tmp.path) assert_equal true, tdb.lockall awr.close brd.read end awr.close brd.close assert_equal "", ard.read assert_equal false, @tdb.trylockall bwr.close assert Process.waitpid2(pid)[1].success? assert_equal true, @tdb.trylockall end def test_check_constant_typos names = {} TDB.constants.each { |const| names[const] = TDB.const_get(const) } assert_equal TDB.constants.size, names.size values = {} TDB.constants.each { |const| values[TDB.const_get(const)] = const } assert_equal TDB.constants.size, values.size end def test_clear @tdb = TDB.new(nil) @tdb["hello"] = "world" assert_equal @tdb, @tdb.clear assert ! @tdb.include?("hello") end def test_fork @tmp = Tempfile.new('tdb') File.unlink(@tmp.path) @tdb = TDB.new(@tmp.path) @tdb["hello"] = "world" pid = fork do assert_equal "world", @tdb["hello"] @tdb["hello"] = "WORLD" @tdb["#$$"] = "NO" exit 0 end _, status = Process.waitpid2(pid) assert status.success?, status.inspect assert_equal "WORLD", @tdb["hello"] assert_equal "NO", @tdb[pid.to_s] end def test_fetch_reuse_buf assert_nothing_raised do @tmp = Tempfile.new('tdb') File.unlink(@tmp.path) end @tdb = TDB.new(@tmp.path) @tdb["HELLO"] = "WORLD" rbuf = "HI" rv = @tdb.fetch("HELLO", rbuf) assert_equal rbuf.object_id, rv.object_id assert_equal "WORLD", rbuf assert_nil @tdb.fetch("HELLO!", rbuf) assert_equal "", rbuf @tdb["HELLO"] = "WORLD" * 100 rv = @tdb.fetch("HELLO", rbuf) assert_equal rbuf.object_id, rv.object_id assert_equal "WORLD" * 100, rbuf end def test_delete_reuse_buf assert_nothing_raised do @tmp = Tempfile.new('tdb') File.unlink(@tmp.path) end @tdb = TDB.new(@tmp.path) @tdb["HELLO"] = "WORLD" rbuf = "HI" rv = @tdb.delete("HELLO", rbuf) assert_equal rbuf.object_id, rv.object_id assert_equal "WORLD", rbuf assert ! @tdb.include?("HELLO") assert_nil @tdb.delete("HELLO!", rbuf) assert_equal "", rbuf @tdb["HELLO"] = "WORLD" * 100 rv = @tdb.delete("HELLO", rbuf) assert_equal rbuf.object_id, rv.object_id assert_equal "WORLD" * 100, rbuf assert ! @tdb.include?("HELLO") end def test_repack_file assert_nothing_raised do @tmp = Tempfile.new('tdb') File.unlink(@tmp.path) end @tdb = TDB.new(@tmp.path) @tdb["hello"] = "world" assert_equal @tdb, @tdb.repack assert_equal "world", @tdb["hello"] end if TDB.method_defined?(:repack) def test_repack_mem @tdb = TDB.new(nil) @tdb["hello"] = "world" assert_raises(TDB::ERR::EINVAL) { @tdb.repack } end if TDB.method_defined?(:repack) end