perobs 4.0.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/lib/perobs.rb +1 -0
  3. data/lib/perobs/Array.rb +66 -19
  4. data/lib/perobs/BTree.rb +83 -12
  5. data/lib/perobs/BTreeBlob.rb +1 -1
  6. data/lib/perobs/BTreeDB.rb +2 -2
  7. data/lib/perobs/BTreeNode.rb +365 -85
  8. data/lib/perobs/BigArray.rb +267 -0
  9. data/lib/perobs/BigArrayNode.rb +998 -0
  10. data/lib/perobs/BigHash.rb +262 -0
  11. data/lib/perobs/BigTree.rb +184 -0
  12. data/lib/perobs/BigTreeNode.rb +873 -0
  13. data/lib/perobs/ConsoleProgressMeter.rb +61 -0
  14. data/lib/perobs/DataBase.rb +4 -3
  15. data/lib/perobs/DynamoDB.rb +57 -15
  16. data/lib/perobs/EquiBlobsFile.rb +143 -51
  17. data/lib/perobs/FNV_Hash_1a_64.rb +54 -0
  18. data/lib/perobs/FlatFile.rb +363 -203
  19. data/lib/perobs/FlatFileBlobHeader.rb +98 -54
  20. data/lib/perobs/FlatFileDB.rb +42 -20
  21. data/lib/perobs/Hash.rb +58 -13
  22. data/lib/perobs/IDList.rb +144 -0
  23. data/lib/perobs/IDListPage.rb +107 -0
  24. data/lib/perobs/IDListPageFile.rb +180 -0
  25. data/lib/perobs/IDListPageRecord.rb +142 -0
  26. data/lib/perobs/Object.rb +18 -15
  27. data/lib/perobs/ObjectBase.rb +38 -4
  28. data/lib/perobs/PersistentObjectCache.rb +53 -67
  29. data/lib/perobs/PersistentObjectCacheLine.rb +24 -12
  30. data/lib/perobs/ProgressMeter.rb +97 -0
  31. data/lib/perobs/SpaceTree.rb +21 -12
  32. data/lib/perobs/SpaceTreeNode.rb +53 -61
  33. data/lib/perobs/Store.rb +71 -32
  34. data/lib/perobs/version.rb +1 -1
  35. data/perobs.gemspec +4 -4
  36. data/test/Array_spec.rb +15 -6
  37. data/test/BTree_spec.rb +5 -2
  38. data/test/BigArray_spec.rb +214 -0
  39. data/test/BigHash_spec.rb +144 -0
  40. data/test/BigTreeNode_spec.rb +153 -0
  41. data/test/BigTree_spec.rb +259 -0
  42. data/test/EquiBlobsFile_spec.rb +105 -1
  43. data/test/FNV_Hash_1a_64_spec.rb +59 -0
  44. data/test/FlatFileDB_spec.rb +63 -14
  45. data/test/Hash_spec.rb +1 -2
  46. data/test/IDList_spec.rb +77 -0
  47. data/test/LegacyDBs/LegacyDB.rb +151 -0
  48. data/test/LegacyDBs/version_3/class_map.json +1 -0
  49. data/test/LegacyDBs/version_3/config.json +1 -0
  50. data/test/LegacyDBs/version_3/database.blobs +0 -0
  51. data/test/LegacyDBs/version_3/database_spaces.blobs +0 -0
  52. data/test/LegacyDBs/version_3/index.blobs +0 -0
  53. data/test/LegacyDBs/version_3/version +1 -0
  54. data/test/LockFile_spec.rb +9 -6
  55. data/test/SpaceTree_spec.rb +4 -1
  56. data/test/Store_spec.rb +290 -199
  57. data/test/spec_helper.rb +9 -4
  58. metadata +47 -10
  59. data/lib/perobs/TreeDB.rb +0 -277
@@ -0,0 +1,153 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright (c) 2016, 2017 by Chris Schlaeger <chris@taskjuggler.org>
4
+ #
5
+ # MIT License
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining
8
+ # a copy of this software and associated documentation files (the
9
+ # "Software"), to deal in the Software without restriction, including
10
+ # without limitation the rights to use, copy, modify, merge, publish,
11
+ # distribute, sublicense, and/or sell copies of the Software, and to
12
+ # permit persons to whom the Software is furnished to do so, subject to
13
+ # the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+
26
+ require 'fileutils'
27
+
28
+ require 'spec_helper'
29
+ require 'perobs/Store'
30
+ require 'perobs/BigTree'
31
+
32
+ describe PEROBS::BigTreeNode do
33
+
34
+ before(:all) do
35
+ @db_name = generate_db_name(__FILE__)
36
+ @store = PEROBS::Store.new(@db_name)
37
+ @store['bigtree'] = @t = @store.new(PEROBS::BigTree, 4)
38
+ end
39
+
40
+ before(:each) do
41
+ @t.clear
42
+ end
43
+
44
+ after(:all) do
45
+ @store.delete_store
46
+ end
47
+
48
+ it 'should insert a key/value pair' do
49
+ n = @t.root
50
+ n.insert(0, 0)
51
+ expect(@t.check).to be true
52
+ s = @t.statistics
53
+ expect(s.leaf_nodes).to eql(1)
54
+ expect(s.branch_nodes).to eql(0)
55
+ expect(s.min_depth).to eql(1)
56
+ expect(s.max_depth).to eql(1)
57
+ node_chain = @t.node_chain(0)
58
+ expect(node_chain.size).to eql(1)
59
+ expect(node_chain.first).to eql(n)
60
+ end
61
+
62
+ it 'should split a leaf node that becomes full' do
63
+ 4.times { |n| @t.insert(n, n) }
64
+ expect(@t.check).to be true
65
+ s = @t.statistics
66
+ expect(s.leaf_nodes).to eql(1)
67
+ @t.insert(4, 4)
68
+ s = @t.statistics
69
+ expect(s.leaf_nodes).to eql(2)
70
+ expect(s.branch_nodes).to eql(1)
71
+ expect(s.min_depth).to eql(2)
72
+ expect(s.max_depth).to eql(2)
73
+ end
74
+
75
+ it 'should split a branch node that becomes full' do
76
+ 11.times { |n| @t.insert(n, n) }
77
+ s = @t.statistics
78
+ expect(s.leaf_nodes).to eql(5)
79
+ expect(s.branch_nodes).to eql(1)
80
+ expect(s.min_depth).to eql(2)
81
+ expect(s.max_depth).to eql(2)
82
+ @t.insert(11, 11)
83
+ expect(@t.check).to be true
84
+ s = @t.statistics
85
+ expect(s.leaf_nodes).to eql(5)
86
+ expect(s.branch_nodes).to eql(3)
87
+ expect(s.min_depth).to eql(3)
88
+ expect(s.max_depth).to eql(3)
89
+ end
90
+
91
+ it 'should merge leaf node with next sibling' do
92
+ 5.times { |n| @t.insert(n, n) }
93
+ expect(@t.check).to be true
94
+ s = @t.statistics
95
+ expect(s.leaf_nodes).to eql(2)
96
+
97
+ @t.remove(0)
98
+ expect(@t.check).to be true
99
+ s = @t.statistics
100
+ expect(s.leaf_nodes).to eql(1)
101
+ expect(s.min_depth).to eql(1)
102
+ expect(s.max_depth).to eql(1)
103
+ end
104
+
105
+ it 'should merge leaf node with previous siblin' do
106
+ 5.times { |n| @t.insert(n, n) }
107
+ expect(@t.check).to be true
108
+ s = @t.statistics
109
+ expect(s.leaf_nodes).to eql(2)
110
+
111
+ @t.remove(2)
112
+ @t.remove(3)
113
+ expect(@t.check).to be true
114
+ s = @t.statistics
115
+ expect(s.leaf_nodes).to eql(1)
116
+ end
117
+
118
+ it 'should merge branch node with next sibling' do
119
+ 12.times { |n| @t.insert(n, n) }
120
+ expect(@t.check).to be true
121
+ s = @t.statistics
122
+ expect(s.leaf_nodes).to eql(5)
123
+ expect(s.branch_nodes).to eql(3)
124
+
125
+ @t.remove(2)
126
+ @t.remove(3)
127
+ @t.remove(4)
128
+ @t.remove(5)
129
+ expect(@t.check).to be true
130
+ s = @t.statistics
131
+ expect(s.leaf_nodes).to eql(3)
132
+ expect(s.branch_nodes).to eql(1)
133
+ end
134
+
135
+ it 'should merge branch node with previous sibling' do
136
+ 12.times { |n| @t.insert(n, n) }
137
+ expect(@t.check).to be true
138
+ s = @t.statistics
139
+ expect(s.leaf_nodes).to eql(5)
140
+ expect(s.branch_nodes).to eql(3)
141
+
142
+ @t.remove(4)
143
+ @t.remove(5)
144
+ @t.remove(2)
145
+ @t.remove(3)
146
+ expect(@t.check).to be true
147
+ s = @t.statistics
148
+ expect(s.leaf_nodes).to eql(3)
149
+ expect(s.branch_nodes).to eql(1)
150
+ end
151
+
152
+ end
153
+
@@ -0,0 +1,259 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright (c) 2016, 2017 by Chris Schlaeger <chris@taskjuggler.org>
4
+ #
5
+ # MIT License
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining
8
+ # a copy of this software and associated documentation files (the
9
+ # "Software"), to deal in the Software without restriction, including
10
+ # without limitation the rights to use, copy, modify, merge, publish,
11
+ # distribute, sublicense, and/or sell copies of the Software, and to
12
+ # permit persons to whom the Software is furnished to do so, subject to
13
+ # the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+
26
+ require 'fileutils'
27
+
28
+ require 'spec_helper'
29
+ require 'perobs/Store'
30
+ require 'perobs/BigTree'
31
+
32
+ describe PEROBS::BigTree do
33
+
34
+ ORDER = 7
35
+
36
+ before(:all) do
37
+ @db_name = generate_db_name(__FILE__)
38
+ @store = PEROBS::Store.new(@db_name)
39
+ @t = @store.new(PEROBS::BigTree, ORDER)
40
+ end
41
+
42
+ after(:all) do
43
+ @store.delete_store
44
+ end
45
+
46
+ it 'should be empty' do
47
+ expect(@t.empty?).to be true
48
+ expect(@t.length).to eql(0)
49
+ s = @t.statistics
50
+ expect(s.leaf_nodes).to eql(1)
51
+ expect(s.branch_nodes).to eql(0)
52
+ expect(s.min_depth).to eql(1)
53
+ expect(s.max_depth).to eql(1)
54
+ end
55
+
56
+ it 'should deal with requests for unknown keys' do
57
+ expect(@t.has_key?(42)).to be false
58
+ expect(@t.get(42)).to be_nil
59
+ end
60
+
61
+ it 'should support adding sequential key/value pairs' do
62
+ 0.upto(ORDER ** 3) do |i|
63
+ @t.insert(i, 3 * i)
64
+ expect(@t.check).to be true
65
+ expect(@t.length).to eql(i + 1)
66
+ expect(@t.has_key?(i)).to be true
67
+ expect(@t.get(i)).to eql(3 * i)
68
+ end
69
+ end
70
+
71
+ it 'should iterate over the stored key and value pairs' do
72
+ i = 0
73
+ @t.each do |k, v|
74
+ expect(k).to eql(i)
75
+ expect(v).to eql(3 * i)
76
+ i += 1
77
+ end
78
+ expect(i).to eql(ORDER ** 3 + 1)
79
+ end
80
+
81
+ it 'should iterate in reverse order over the stored key and value pairs' do
82
+ i = ORDER ** 3
83
+ @t.reverse_each do |k, v|
84
+ expect(k).to eql(i)
85
+ expect(v).to eql(3 * i)
86
+ i -= 1
87
+ end
88
+ expect(i).to eql(-1)
89
+ end
90
+
91
+ it 'should yield the key/value pairs on check' do
92
+ i = 0
93
+ @t.check do |k, v|
94
+ expect(k).to eql(k)
95
+ expect(v).to eql(3 * k)
96
+ i += 1
97
+ end
98
+ expect(i).to eql(ORDER ** 3 + 1)
99
+ end
100
+
101
+ it 'should support overwriting existing entries' do
102
+ 0.upto(ORDER ** 3) do |i|
103
+ @t.insert(i, 7 * i)
104
+ expect(@t.check).to be true
105
+ expect(@t.length).to eql(ORDER ** 3 + 1)
106
+ expect(@t.has_key?(i)).to be true
107
+ expect(@t.get(i)).to eql(7 * i)
108
+ end
109
+ end
110
+
111
+ it 'should support clearing the tree' do
112
+ @t.clear
113
+ expect(@t.check).to be true
114
+ expect(@t.empty?).to be true
115
+ expect(@t.length).to eql(0)
116
+ i = 0
117
+ @t.each { |k, v| i += 1 }
118
+ expect(i).to eql(0)
119
+ end
120
+
121
+ it 'should support adding random key/value pairs' do
122
+ (1..ORDER ** 3).to_a.shuffle.each do |i|
123
+ @t.insert(i, i * 100)
124
+ end
125
+ expect(@t.check).to be true
126
+ (1..ORDER ** 3).to_a.shuffle.each do |i|
127
+ expect(@t.get(i)).to eql(i * 100)
128
+ end
129
+ end
130
+
131
+ it 'should support removing keys in random order' do
132
+ @t.clear
133
+ (1..ORDER ** 3).to_a.shuffle.each do |i|
134
+ @t.insert(i, i * 100)
135
+ end
136
+ expect(@t.length).to eql(ORDER ** 3)
137
+ (1..ORDER ** 3).to_a.shuffle.each do |i|
138
+ expect(@t.remove(i)).to eql(i * 100)
139
+ expect(@t.check).to be true
140
+ end
141
+ expect(@t.length).to eql(0)
142
+ end
143
+
144
+ it 'should support removing keys in increasing order' do
145
+ @t.clear
146
+ (1..ORDER ** 3).to_a.shuffle.each do |i|
147
+ @t.insert(i, i * 100)
148
+ end
149
+ expect(@t.length).to eql(ORDER ** 3)
150
+ (1..ORDER ** 3).to_a.each do |i|
151
+ expect(@t.remove(i)).to eql(i * 100)
152
+ expect(@t.check).to be true
153
+ end
154
+ expect(@t.length).to eql(0)
155
+ end
156
+
157
+ it 'should support removing keys in reverse order' do
158
+ @t.clear
159
+ (1..ORDER ** 3).to_a.shuffle.each do |i|
160
+ @t.insert(i, i * 100)
161
+ end
162
+ expect(@t.length).to eql(ORDER ** 3)
163
+ (1..ORDER ** 3).to_a.reverse_each do |i|
164
+ expect(@t.remove(i)).to eql(i * 100)
165
+ expect(@t.check).to be true
166
+ end
167
+ expect(@t.length).to eql(0)
168
+ end
169
+
170
+ it 'should persist the data' do
171
+ db_name = generate_db_name(__FILE__ + '_persist')
172
+ store = PEROBS::Store.new(db_name)
173
+ store['bigtree'] = t = store.new(PEROBS::BigTree, 4)
174
+ 10.times do |i|
175
+ t.insert(i, i)
176
+ end
177
+ 10.times do |i|
178
+ expect(t.get(i)).to eql(i)
179
+ end
180
+ store.exit
181
+
182
+ store = PEROBS::Store.new(db_name)
183
+ t = store['bigtree']
184
+ 10.times do |i|
185
+ expect(t.get(i)).to eql(i)
186
+ end
187
+ store.delete_store
188
+ end
189
+
190
+ it 'should delete all entries matching a condition' do
191
+ @t.clear
192
+ (1..50).to_a.shuffle.each do |i|
193
+ @t.insert(i, i)
194
+ end
195
+ @t.delete_if { |k, v| v % 7 == 0 }
196
+ expect(@t.check).to be true
197
+ @t.each do |k, v|
198
+ expect(v % 7).to be > 0, "failed for #{v}"
199
+ end
200
+ expect(@t.length).to eql(43)
201
+ @t.delete_if { |k, v| v % 2 == 0 }
202
+ expect(@t.check).to be true
203
+ @t.each do |k, v|
204
+ expect(v % 2).to be > 0
205
+ end
206
+ expect(@t.length).to eql(21)
207
+ @t.delete_if { |k, v| true }
208
+ expect(@t.check).to be true
209
+ expect(@t.empty?).to be true
210
+ end
211
+
212
+ it 'should survive a real-world usage test' do
213
+ @t.clear
214
+ ref = {}
215
+ 0.upto(1000) do
216
+ case rand(5)
217
+ when 0
218
+ 0.upto(2) do
219
+ key = rand(100000)
220
+ value = key * 10
221
+ @t.insert(key, value)
222
+ ref[key] = value
223
+ end
224
+ when 1
225
+ if ref.length > 0
226
+ key = ref.keys[rand(ref.keys.length)]
227
+ expect(@t.remove(key)).to eql(ref[key])
228
+ ref.delete(key)
229
+ end
230
+ when 2
231
+ if ref.length > 0
232
+ 0.upto(3) do
233
+ key = ref.keys[rand(ref.keys.length)]
234
+ expect(@t.get(key)).to eql(ref[key])
235
+ end
236
+ end
237
+ when 3
238
+ if ref.length > 0
239
+ key = ref.keys[rand(ref.keys.length)]
240
+ value = ref[key] + 1
241
+ @t.insert(key, value)
242
+ ref[key] = value
243
+ end
244
+ when 4
245
+ if rand(50) == 0
246
+ expect(@t.check).to be true
247
+ end
248
+ end
249
+ end
250
+
251
+ i = 0
252
+ @t.each do |k, v|
253
+ expect(ref[k]).to eql(v)
254
+ i += 1
255
+ end
256
+ expect(i).to eql(ref.length)
257
+ end
258
+
259
+ end
@@ -27,13 +27,17 @@ require 'fileutils'
27
27
 
28
28
  require 'spec_helper'
29
29
  require 'perobs/EquiBlobsFile'
30
+ require 'perobs/ProgressMeter'
30
31
 
31
32
  describe PEROBS::EquiBlobsFile do
32
33
 
33
34
  before(:all) do
35
+ PEROBS.log.level = Logger::ERROR
36
+ PEROBS.log.open($stderr)
34
37
  @db_dir = generate_db_name('EquiBlobsFile')
35
38
  FileUtils.mkdir_p(@db_dir)
36
- @bf = PEROBS::EquiBlobsFile.new(@db_dir, 'EquiBlobsFile', 8)
39
+ @bf = PEROBS::EquiBlobsFile.new(@db_dir, 'EquiBlobsFile',
40
+ PEROBS::ProgressMeter.new, 8)
37
41
  end
38
42
 
39
43
  after(:all) do
@@ -134,6 +138,12 @@ describe PEROBS::EquiBlobsFile do
134
138
  expect(@bf.check).to be true
135
139
  end
136
140
 
141
+ it 'should support deleting reserved blobs' do
142
+ adr = @bf.free_address
143
+ @bf.delete_blob(adr)
144
+ expect(@bf.check).to be true
145
+ end
146
+
137
147
  it 'should support clearing the file' do
138
148
  @bf.clear
139
149
  expect(@bf.total_entries).to eql(0)
@@ -191,5 +201,99 @@ describe PEROBS::EquiBlobsFile do
191
201
  expect(@bf.check).to be true
192
202
  end
193
203
 
204
+ it 'should support custom offsets' do
205
+ @bf.close
206
+ @bf.erase
207
+ @bf.clear_custom_data
208
+ @bf.register_custom_data('foo', 42)
209
+ @bf.register_custom_data('bar', 43)
210
+ @bf.open
211
+ expect(@bf.total_entries).to eql(0)
212
+ expect(@bf.total_spaces).to eql(0)
213
+ expect(@bf.check).to be true
214
+ expect(@bf.free_address).to eql(1)
215
+ @bf.store_blob(1,'11111111')
216
+ expect(@bf.free_address).to eql(2)
217
+ @bf.store_blob(2,'22222222')
218
+ expect(@bf.free_address).to eql(3)
219
+ @bf.store_blob(3,'33333333')
220
+ expect(@bf.check).to be true
221
+ expect(@bf.total_entries).to eql(3)
222
+ expect(@bf.total_spaces).to eql(0)
223
+ @bf.delete_blob(2)
224
+ expect(@bf.check).to be true
225
+ expect(@bf.total_entries).to eql(2)
226
+ expect(@bf.total_spaces).to eql(1)
227
+ expect(@bf.free_address).to eql(2)
228
+ @bf.store_blob(2,'44444444')
229
+ expect(@bf.total_entries).to eql(3)
230
+ expect(@bf.total_spaces).to eql(0)
231
+ expect(@bf.check).to be true
232
+ end
233
+
234
+ it 'should support a mix of adds and deletes' do
235
+ @bf.close
236
+ @bf.erase
237
+ @bf.clear_custom_data
238
+ @bf.register_custom_data('foo', 42)
239
+ @bf.register_custom_data('bar', 43)
240
+ @bf.open
241
+
242
+ entries = {}
243
+ 1000.times do |i|
244
+ rand(30).times do
245
+ adr = @bf.free_address
246
+ expect(entries[adr]).to be nil
247
+ val = rand(2 ** 64)
248
+ @bf.store_blob(adr, [ val ].pack('Q'))
249
+ entries[adr] = val
250
+ #expect(@bf.check).to be true
251
+ end
252
+
253
+ rand(15).times do
254
+ unless entries.empty?
255
+ addresses = entries.keys
256
+ adr = addresses[rand(addresses.length)]
257
+ expect(@bf.retrieve_blob(adr).unpack('Q').first).to eql(entries[adr])
258
+ @bf.delete_blob(adr)
259
+ entries.delete(adr)
260
+ #expect(@bf.check).to be true
261
+ end
262
+ end
263
+
264
+ rand(5).times do
265
+ unless entries.empty?
266
+ addresses = entries.keys
267
+ adr = addresses[rand(addresses.length)]
268
+ val = rand(2 ** 64)
269
+ @bf.store_blob(adr, [ val ].pack('Q'))
270
+ entries[adr] = val
271
+ #expect(@bf.check).to be true
272
+ end
273
+ end
274
+
275
+ if rand(100) == 0
276
+ expect(@bf.check).to be true
277
+ end
278
+
279
+ if rand(100) == 0
280
+ @bf.first_entry = i
281
+ @bf.set_custom_data('foo', i)
282
+ end
283
+
284
+ if rand(50) == 0
285
+ @bf.close
286
+ @bf.open
287
+ end
288
+
289
+ if rand(500) == 0
290
+ @bf.clear
291
+ entries = {}
292
+ end
293
+ end
294
+
295
+ expect(@bf.check).to be true
296
+ end
297
+
194
298
  end
195
299