test/test_tdb_mt.rb: fix fragile test
[ruby-tdb.git] / test / test_tdb.rb
blob4b3f223dbe0e120920bb2a34592c0ae6371b32f4
1 # -*- encoding: binary -*-
2 $stdout.sync = $stderr.sync = true
3 require 'test/unit'
4 require 'tempfile'
5 $-w = true
6 require 'tdb'
8 class TestTdb < Test::Unit::TestCase
10   def setup
11     @tmp = @tdb = nil
12   end
14   def teardown
15     @tmp.close! if @tmp.respond_to?(:close!)
16     @tdb.close if @tdb && ! @tdb.closed?
17   end
19   def test_create_file_null_byte_in_path
20     assert_raises(ArgumentError) { TDB.new("hello.tdb\0") }
21   end
23   def test_create_file
24     assert_nothing_raised do
25       @tmp = Tempfile.new('tdb')
26       File.unlink(@tmp.path)
27     end
28     @tdb = TDB.new(@tmp.path)
29     assert_kind_of TDB, @tdb
30     assert File.exist?(@tmp.path)
31   end
33   def test_to_a
34     @tdb = TDB.new(nil)
35     assert_equal [], @tdb.to_a
36   end
38   def test_each
39     @tdb = TDB.new(nil)
40     @tdb["X"] = "Y"
42     tmp = []
43     rc = @tdb.each { |k,v| tmp << [k, v ] }
44     assert_equal([ %w(X Y) ], tmp)
45     assert_equal @tdb.object_id, rc.object_id
47     tmp = []
48     assert_raises(EOFError) {
49       @tdb.each { |k,v| raise EOFError, "FOO"; tmp << [ k, v ] }
50     }
51     assert tmp.empty?
53     tmp = []
54     rc = catch(:zzz) { @tdb.each { |k,v| throw(:zzz, "FOO"); tmp << [ k, v ] } }
55     assert_equal rc, "FOO"
56     assert tmp.empty?
57   end
59   def test_each_bigger
60     @tdb = TDB.new(nil)
61     @tdb["a"] = "A"
62     @tdb["b"] = "B"
63     @tdb["c"] = "C"
65     tmp = []
66     rc = @tdb.each { |k,v| tmp << [k, v ] }
67     assert_equal 3, tmp.size
68     assert_equal @tdb.object_id, rc.object_id
70     tmp = []
71     assert_raises(EOFError) {
72       @tdb.each { |k,v|
73         tmp << [ k, v ]
74         raise EOFError, "FOO"
75       }
76     }
77     assert_equal 1, tmp.size
79     tmp = []
80     rc = catch(:zzz) {
81       @tdb.each { |k,v|
82         tmp << [ k, v ]
83         throw(:zzz, "FOO")
84       }
85     }
86     assert_equal rc, "FOO"
87     assert_equal 1, tmp.size
88   end
90   def test_memory
91     assert_nothing_raised do
92       @tdb = TDB.new(nil)
93     end
94     assert ! @tdb.closed?
95     assert_nil @tdb.close
96     assert @tdb.closed?
97     assert_raises(IOError) { @tdb.close }
98   end
100   def test_delete
101     @tdb = TDB.new(nil)
102     @tdb["hello"] = "X"
103     assert_equal "X", @tdb.delete("hello")
104     assert_nil @tdb["hello"]
105     assert_nil @tdb.fetch("hello")
106     assert_nil @tdb.delete("hello")
107     @tdb["hello"] = "world"
108     assert_equal "world", @tdb.delete("hello")
109     assert_nil @tdb.delete("hello")
110   end
112   def test_nuke!
113     @tdb = TDB.new(nil)
114     assert_equal false, @tdb.nuke!("hello")
115     @tdb["hello"] = "world"
116     assert_equal true, @tdb.nuke!("hello")
117     assert ! @tdb.include?("hello")
118     assert_equal false, @tdb.nuke!("hello")
119   end
121   def test_exists?
122     @tdb = TDB.new(nil)
123     assert_equal false, @tdb.key?("hello")
124     assert_equal false, @tdb.include?("hello")
125     @tdb["hello"] = "world"
126     assert_equal true, @tdb.key?("hello")
127   end
129   def test_store_fetch_mem
130     @tdb = TDB.new(nil)
131     assert_nothing_raised { @tdb["hello"] = "world" }
132     assert_equal "world", @tdb["hello"]
133     @tdb.store("hello", "Z")
134     assert_equal "Z", @tdb["hello"]
135     assert_equal "Z", @tdb.fetch("hello")
136   end
138   def test_store_modify_mem
139     @tdb = TDB.new(nil)
140     assert_nothing_raised { @tdb["hello"] = "world" }
141     assert_equal "world", @tdb["hello"]
142     assert_equal "Z", @tdb.modify("hello", "Z")
143     assert_equal "Z", @tdb["hello"]
145     assert_nil @tdb.modify("none", "Z")
146     assert_raises(TDB::ERR::NOEXIST) { @tdb.modify!("none", "Z") }
147   end
149   def test_store_insert_mem
150     @tdb = TDB.new(nil)
151     assert_equal "world", @tdb.insert("hello", "world")
152     assert_equal "world", @tdb["hello"]
153     assert_nil @tdb.insert("hello", "Z")
154     assert_raises(TDB::ERR::EXISTS) { @tdb.insert!("hello", "Z") }
155     assert_equal "world", @tdb["hello"]
156   end
158   def test_gc
159     assert_nothing_raised do
160       100000.times { TDB.new(nil) }
161       100000.times { TDB.new(Tempfile.new('tdb').path) }
162     end
163   end if ENV["TEST_GC"]
165   def test_new_with_hash_size
166     assert_nothing_raised { TDB.new(nil, :hash_size => 6) }
167     assert_raises(TypeError) { TDB.new(nil, :hash_size => "6") }
168   end
170   def test_const
171     assert_equal 0, TDB::DEFAULT
172     assert_equal 1, TDB::CLEAR_IF_FIRST
173   end
175   def test_new_with_open_flags
176     @tmp = Tempfile.new('tdb_excl')
177     assert_raises(Errno::EEXIST) {
178       TDB.new(@tmp.path, :open_flags => IO::EXCL|IO::CREAT|IO::RDWR)
179     }
180     File.unlink(@tmp.path)
181     assert_nothing_raised {
182       @tdb = TDB.new(@tmp.path, :open_flags => IO::EXCL|IO::CREAT|IO::RDWR)
183     }
184   end
186   def test_open_with_tdb_flags
187     assert_nothing_raised do
188       @tdb = TDB.new("/non/existent/file", :tdb_flags => TDB::INTERNAL)
189     end
190   end
192   def test_alternate_hashes
193     results = {}
194     expect = TDB::HASHES.to_a.map { |k,v| [ k.to_s, v.to_s ] }.sort
195     %w(default siphash24 jenkins_lookup3 djb2 djb3 fnv1a
196        murmur1 murmur1_aligned murmur2 murmur2a murmur2_aligned
197        murmur3a murmur3f).each do |h|
198       assert_nothing_raised do
199         tdb = TDB.new(nil, :hash => h.to_sym)
200         TDB::HASHES.each do |k,v|
201           tdb[k.to_s] = v.to_s
202         end
203         assert_equal expect, tdb.to_a.sort
204         assert_nil tdb.close
205       end
206       assert_raises(ArgumentError) do
207         TDB.new(nil, :hash => h)
208       end
209     end
210   end
212   def test_lock_unlock_all
213     @tmp = Tempfile.new('tdb')
214     File.unlink(@tmp.path)
215     @tdb = TDB.new(@tmp.path)
216     assert_equal true, @tdb.lockall
217     assert_equal true, @tdb.unlockall
218     assert_raises(TDB::ERR::LOCK) { @tdb.unlockall }
219   end
221   def test_read_locks
222     @tmp = Tempfile.new('tdb')
223     File.unlink(@tmp.path)
224     @tdb = TDB.new(@tmp.path)
225     assert_equal true, @tdb.lockall_read
226     assert_equal true, @tdb.unlockall_read
227     assert_raises(TDB::ERR::LOCK) { @tdb.unlockall_read }
228     assert_equal true, @tdb.trylockall_read
229     assert_equal true, @tdb.unlockall_read
230     assert_raises(TDB::ERR::LOCK) { @tdb.unlockall_read }
231   end
233   def test_mark_locks
234     @tmp = Tempfile.new('tdb')
235     File.unlink(@tmp.path)
236     @tdb = TDB.new(@tmp.path)
237     assert_equal true, @tdb.lockall_mark
238     assert_equal true, @tdb.lockall_unmark
239     assert_raises(TDB::ERR::LOCK) { @tdb.lockall_unmark }
240   end
242   def test_trylockall
243     @tmp = Tempfile.new('tdb')
244     File.unlink(@tmp.path)
245     @tdb = TDB.new(@tmp.path)
246     ard, awr = IO.pipe
247     brd, bwr = IO.pipe
248     pid = fork do
249       @tdb.close
250       ard.close
251       bwr.close
252       tdb = TDB.new(@tmp.path)
253       assert_equal true, tdb.lockall
254       awr.close
255       brd.read
256     end
257     awr.close
258     brd.close
259     assert_equal "", ard.read
260     assert_equal false, @tdb.trylockall
261     bwr.close
262     assert Process.waitpid2(pid)[1].success?
263     assert_equal true, @tdb.trylockall
264   end
266   def test_check_constant_typos
267     names = {}
268     TDB.constants.each { |const| names[const] = TDB.const_get(const) }
269     assert_equal TDB.constants.size, names.size
271     values = {}
272     TDB.constants.each { |const| values[TDB.const_get(const)] = const }
273     assert_equal TDB.constants.size, values.size
274   end
276   def test_clear
277     @tdb = TDB.new(nil)
278     @tdb["hello"] = "world"
279     assert_equal @tdb, @tdb.clear
280     assert ! @tdb.include?("hello")
281   end
283   def test_fork
284     @tmp = Tempfile.new('tdb')
285     File.unlink(@tmp.path)
286     @tdb = TDB.new(@tmp.path)
287     @tdb["hello"] = "world"
288     pid = fork do
289       assert_equal "world", @tdb["hello"]
290       @tdb["hello"] = "WORLD"
291       @tdb["#$$"] = "NO"
292       exit 0
293     end
294     _, status = Process.waitpid2(pid)
295     assert status.success?, status.inspect
296     assert_equal "WORLD", @tdb["hello"]
297     assert_equal "NO", @tdb[pid.to_s]
298   end
300   def test_fetch_reuse_buf
301     assert_nothing_raised do
302       @tmp = Tempfile.new('tdb')
303       File.unlink(@tmp.path)
304     end
305     @tdb = TDB.new(@tmp.path)
306     @tdb["HELLO"] = "WORLD"
307     rbuf = "HI"
308     rv = @tdb.fetch("HELLO", rbuf)
309     assert_equal rbuf.object_id, rv.object_id
311     assert_equal "WORLD", rbuf
312     assert_nil @tdb.fetch("HELLO!", rbuf)
313     assert_equal "", rbuf
315     @tdb["HELLO"] = "WORLD" * 100
316     rv = @tdb.fetch("HELLO", rbuf)
317     assert_equal rbuf.object_id, rv.object_id
318     assert_equal "WORLD" * 100, rbuf
319   end
321   def test_delete_reuse_buf
322     assert_nothing_raised do
323       @tmp = Tempfile.new('tdb')
324       File.unlink(@tmp.path)
325     end
326     @tdb = TDB.new(@tmp.path)
327     @tdb["HELLO"] = "WORLD"
328     rbuf = "HI"
329     rv = @tdb.delete("HELLO", rbuf)
330     assert_equal rbuf.object_id, rv.object_id
331     assert_equal "WORLD", rbuf
332     assert ! @tdb.include?("HELLO")
334     assert_nil @tdb.delete("HELLO!", rbuf)
335     assert_equal "", rbuf
337     @tdb["HELLO"] = "WORLD" * 100
338     rv = @tdb.delete("HELLO", rbuf)
339     assert_equal rbuf.object_id, rv.object_id
340     assert_equal "WORLD" * 100, rbuf
341     assert ! @tdb.include?("HELLO")
342   end
344   def test_repack_file
345     assert_nothing_raised do
346       @tmp = Tempfile.new('tdb')
347       File.unlink(@tmp.path)
348     end
349     @tdb = TDB.new(@tmp.path)
350     @tdb["hello"] = "world"
351     assert_equal @tdb, @tdb.repack
352     assert_equal "world", @tdb["hello"]
353   end if TDB.method_defined?(:repack)
355   def test_repack_mem
356     @tdb = TDB.new(nil)
357     @tdb["hello"] = "world"
358     assert_raises(TDB::ERR::EINVAL) { @tdb.repack }
359   end if TDB.method_defined?(:repack)