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
@@ -27,13 +27,16 @@ require 'fileutils'
27
27
 
28
28
  require 'spec_helper'
29
29
  require 'perobs/SpaceTree'
30
+ require 'perobs/ProgressMeter'
30
31
 
31
32
  describe PEROBS::SpaceTree do
32
33
 
33
34
  before(:all) do
34
35
  @db_dir = generate_db_name('SpaceTree')
35
36
  FileUtils.mkdir_p(@db_dir)
36
- @m = PEROBS::SpaceTree.new(@db_dir)
37
+ @m = PEROBS::SpaceTree.new(@db_dir, PEROBS::ProgressMeter.new)
38
+ PEROBS.log.level = Logger::ERROR
39
+ PEROBS.log.open($stderr)
37
40
  end
38
41
 
39
42
  after(:all) do
@@ -33,7 +33,7 @@ class Person < PEROBS::Object
33
33
 
34
34
  attr_persist :name, :zip, :bmi, :married, :related, :relatives
35
35
 
36
- def initialize(store)
36
+ def initialize(p)
37
37
  super
38
38
  attr_init(:name, '')
39
39
  attr_init(:bmi, 22.2)
@@ -46,7 +46,7 @@ class PersonN < PEROBS::Object
46
46
 
47
47
  attr_persist :name, :zip, :bmi, :married, :related, :relatives
48
48
 
49
- def initialize(store)
49
+ def initialize(p)
50
50
  super
51
51
  attr_init(:name, '')
52
52
  attr_init(:bmi, 22.2)
@@ -59,9 +59,9 @@ class O0 < PEROBS::Object
59
59
 
60
60
  attr_persist :child
61
61
 
62
- def initialize(store)
62
+ def initialize(p)
63
63
  super
64
- self.child = @store.new(O1, myself)
64
+ self.child = p.store.new(O1, myself)
65
65
  end
66
66
 
67
67
  end
@@ -84,22 +84,16 @@ describe PEROBS::Store do
84
84
  end
85
85
 
86
86
  after(:each) do
87
- @store.gc
88
- expect { @store.check }.to_not raise_error
89
- expect { @store.delete_store }.to_not raise_error
90
- end
91
-
92
- after(:all) do
93
87
  FileUtils.rm_rf(@db_file)
94
88
  end
95
89
 
96
90
  it 'should @store simple objects' do
97
- @store = PEROBS::Store.new(@db_file, { :serializer => :yaml })
98
- @store['john'] = john = @store.new(Person)
91
+ store = PEROBS::Store.new(@db_file, { :serializer => :yaml })
92
+ store['john'] = john = store.new(Person)
99
93
  john.name = 'John'
100
94
  john.zip = 4060
101
95
  john.bmi = 25.5
102
- @store['jane'] = jane = @store.new(Person)
96
+ store['jane'] = jane = store.new(Person)
103
97
  jane.name = 'Jane'
104
98
  jane.related = john
105
99
  jane.married = true
@@ -110,48 +104,56 @@ describe PEROBS::Store do
110
104
  expect(john.bmi).to eq(25.5)
111
105
  expect(john.married).to be false
112
106
  expect(john.related).to be_nil
113
- jane = @store['jane']
107
+ jane = store['jane']
114
108
  expect(jane.name).to eq('Jane')
115
109
  expect(jane.related).to eq(john)
116
110
  expect(jane.married).to be true
111
+
112
+ capture_io { store.gc }
113
+ capture_io { expect { store.check }.to_not raise_error }
114
+ expect { store.delete_store }.to_not raise_error
117
115
  end
118
116
 
119
117
  it 'should @store and retrieve simple objects' do
120
118
  [ :marshal, :json, :yaml ].each do |serializer|
121
119
  FileUtils.rm_rf(@db_file)
122
- @store = PEROBS::Store.new(@db_file, { :serializer => serializer })
123
- @store['john'] = john = @store.new(Person)
120
+ store = PEROBS::Store.new(@db_file, { :serializer => serializer })
121
+ store['john'] = john = store.new(Person)
124
122
  john.name = 'John'
125
123
  john.zip = 4060
126
124
  john.bmi = 25.5
127
- @store['jane'] = jane = @store.new(Person)
125
+ store['jane'] = jane = store.new(Person)
128
126
  jane.name = 'Jane'
129
127
  jane.related = john
130
128
  jane.married = true
131
129
  jane.relatives = 'test'
132
130
 
133
- @store.exit
131
+ capture_io { store.exit }
134
132
 
135
- @store = PEROBS::Store.new(@db_file)
136
- john = @store['john']
133
+ store = PEROBS::Store.new(@db_file)
134
+ john = store['john']
137
135
  expect(john.name).to eq('John')
138
136
  expect(john.zip).to eq(4060)
139
137
  expect(john.bmi).to eq(25.5)
140
138
  expect(john.married).to be false
141
139
  expect(john.related).to be_nil
142
- jane = @store['jane']
140
+ jane = store['jane']
143
141
  expect(jane.name).to eq('Jane')
144
142
  expect(jane.related).to eq(john)
145
143
  expect(jane.married).to be true
144
+
145
+ capture_io { store.gc }
146
+ capture_io { expect { store.check }.to_not raise_error }
147
+ expect { store.delete_store }.to_not raise_error
146
148
  end
147
149
  end
148
150
 
149
151
  it 'should flush cached objects when necessary' do
150
- @store = PEROBS::Store.new(@db_file, :cache_bits => 3)
152
+ store = PEROBS::Store.new(@db_file, :cache_bits => 3)
151
153
  last_obj = nil
152
154
  0.upto(20) do |i|
153
- @store["person#{i}"] = obj = @store.new(Person)
154
- expect(@store["person#{i}"]).to eq(obj)
155
+ store["person#{i}"] = obj = store.new(Person)
156
+ expect(store["person#{i}"]).to eq(obj)
155
157
  obj.name = "Person #{i}"
156
158
  expect(obj.name).to eq("Person #{i}")
157
159
  obj.related = last_obj
@@ -159,167 +161,216 @@ describe PEROBS::Store do
159
161
  last_obj = obj
160
162
  end
161
163
  0.upto(20) do |i|
162
- expect(@store["person#{i}"].name).to eq("Person #{i}")
164
+ expect(store["person#{i}"].name).to eq("Person #{i}")
163
165
  end
166
+
167
+ capture_io { store.gc }
168
+ capture_io { expect { store.check }.to_not raise_error }
169
+ expect { store.delete_store }.to_not raise_error
164
170
  end
165
171
 
166
172
  it 'should support renaming of classes' do
167
- @store = PEROBS::Store.new(@db_file)
168
- @store['john'] = john = @store.new(Person)
173
+ store = PEROBS::Store.new(@db_file)
174
+ store['john'] = john = store.new(Person)
169
175
  john.name = 'John'
170
176
  john.zip = 4060
171
177
  john.bmi = 25.5
172
- @store['jane'] = jane = @store.new(Person)
178
+ store['jane'] = jane = store.new(Person)
173
179
  jane.name = 'Jane'
174
180
  jane.related = john
175
181
  jane.married = true
176
182
  jane.relatives = 'test'
177
183
 
178
- @store.exit
184
+ capture_io { store.exit }
179
185
 
180
- @store = PEROBS::Store.new(@db_file)
181
- @store.rename_classes({ 'Person' => 'PersonN' })
182
- john = @store['john']
186
+ store = PEROBS::Store.new(@db_file)
187
+ store.rename_classes({ 'Person' => 'PersonN' })
188
+ john = store['john']
183
189
  expect(john.name).to eq('John')
184
190
  expect(john.zip).to eq(4060)
185
191
  expect(john.bmi).to eq(25.5)
186
192
  expect(john.married).to be false
187
193
  expect(john.related).to be_nil
188
- jane = @store['jane']
194
+ jane = store['jane']
189
195
  expect(jane.name).to eq('Jane')
190
196
  expect(jane.related).to eq(john)
191
197
  expect(jane.married).to be true
198
+
199
+ capture_io { store.gc }
200
+ capture_io { expect { store.check }.to_not raise_error }
201
+ expect { store.delete_store }.to_not raise_error
192
202
  end
193
203
 
194
204
  it 'should detect modification to non-working objects' do
195
- @store = PEROBS::Store.new(@db_file, :cache_bits => 3)
205
+ store = PEROBS::Store.new(@db_file, :cache_bits => 3)
196
206
  0.upto(20) do |i|
197
- @store["person#{i}"] = obj = @store.new(Person)
207
+ store["person#{i}"] = obj = store.new(Person)
198
208
  obj.name = "Person #{i}"
199
209
  end
200
210
  0.upto(20) do |i|
201
- @store["person#{i}"].name = "New Person #{i}"
211
+ store["person#{i}"].name = "New Person #{i}"
202
212
  end
203
- @store.exit
204
- @store = PEROBS::Store.new(@db_file)
213
+ capture_io { store.exit }
214
+ store = PEROBS::Store.new(@db_file)
205
215
  0.upto(20) do |i|
206
- expect(@store["person#{i}"].name).to eq("New Person #{i}")
216
+ expect(store["person#{i}"].name).to eq("New Person #{i}")
207
217
  end
218
+
219
+ capture_io { store.gc }
220
+ capture_io { expect { store.check }.to_not raise_error }
221
+ expect { store.delete_store }.to_not raise_error
208
222
  end
209
223
 
210
224
  it 'should garbage collect unlinked objects' do
211
- @store = PEROBS::Store.new(@db_file)
212
- @store['person1'] = obj = @store.new(Person)
213
- id1 = obj._id
214
- @store['person2'] = obj = @store.new(Person)
215
- id2 = obj._id
216
- obj.related = obj = @store.new(Person)
217
- id3 = obj._id
218
- @store.sync
219
- @store['person1'] = nil
220
- @store.gc
221
- @store.exit
222
- @store = PEROBS::Store.new(@db_file)
223
- expect(@store.object_by_id(id1)).to be_nil
224
- expect(@store['person2']._id).to eq(id2)
225
- expect(@store['person2'].related._id).to eq(id3)
225
+ store = PEROBS::Store.new(@db_file)
226
+ persons = []
227
+ 0.upto(20) do |i|
228
+ persons[i] = obj = store.new(Person)
229
+ obj.name = "person#{i}"
230
+ if i < 3
231
+ store["person#{i}"] = obj
232
+ else
233
+ persons[i - 3].related = obj
234
+ end
235
+ end
236
+ store.sync
237
+ expect(store.size).to eq(21)
238
+
239
+ store['person0'] = nil
240
+ capture_io { store.gc }
241
+ expect(store.size).to eq(14)
242
+ capture_io { expect { store.check }.to_not raise_error }
243
+ capture_io { store.exit }
244
+ store = PEROBS::Store.new(@db_file)
245
+ capture_io { expect { store.check }.to_not raise_error }
246
+
247
+ person = store['person1']
248
+ i = 0
249
+ while (person = person.related) do
250
+ i += 1
251
+ end
252
+ expect(i).to eq(6)
253
+
254
+ capture_io { store.gc }
255
+ capture_io { expect { store.check }.to_not raise_error }
256
+ expect { store.delete_store }.to_not raise_error
226
257
  end
227
258
 
228
259
  it 'should handle cyclicly linked objects' do
229
- @store = PEROBS::Store.new(@db_file)
230
- @store['person0'] = p0 = @store.new(Person)
260
+ store = PEROBS::Store.new(@db_file)
261
+ store['person0'] = p0 = store.new(Person)
231
262
  id0 = p0._id
232
- p1 = @store.new(Person)
263
+ p1 = store.new(Person)
233
264
  id1 = p1._id
234
- p2 = @store.new(Person)
265
+ p2 = store.new(Person)
235
266
  id2 = p2._id
236
267
  p1.related = p2
237
268
  p2.related = p1
238
269
  p0.related = p1
239
- expect(@store.check).to eq(0)
240
- expect(@store.gc).to eq(0)
270
+ capture_io { expect(store.check).to eq(0) }
271
+ capture_io { expect(store.gc).to eq(0) }
241
272
  p0 = p1 = p2 = nil
242
- @store.exit
273
+ capture_io { store.exit }
243
274
  GC.start
244
- @store = PEROBS::Store.new(@db_file)
245
- expect(@store['person0']._id).to eq(id0)
246
- expect(@store['person0'].related._id).to eq(id1)
247
- expect(@store['person0'].related.related._id).to eq(id2)
275
+ store = PEROBS::Store.new(@db_file)
276
+ expect(store['person0']._id).to eq(id0)
277
+ expect(store['person0'].related._id).to eq(id1)
278
+ expect(store['person0'].related.related._id).to eq(id2)
248
279
 
249
- @store['person0'].related = nil
250
- expect(@store.gc).to eq(2)
280
+ store['person0'].related = nil
281
+ capture_io { expect(store.gc).to eq(2) }
251
282
  GC.start
252
- expect(@store.object_by_id(id1)).to be_nil
253
- expect(@store.object_by_id(id2)).to be_nil
254
- @store.exit
255
-
256
- @store = PEROBS::Store.new(@db_file)
257
- expect(@store.check).to eq(0)
258
- expect(@store.object_by_id(id1)).to be_nil
259
- expect(@store.object_by_id(id2)).to be_nil
283
+ expect(store.object_by_id(id1)).to be_nil
284
+ expect(store.object_by_id(id2)).to be_nil
285
+ capture_io { store.exit }
286
+
287
+ store = PEROBS::Store.new(@db_file)
288
+ capture_io { expect(store.check).to eq(0) }
289
+ expect(store.object_by_id(id1)).to be_nil
290
+ expect(store.object_by_id(id2)).to be_nil
291
+
292
+ capture_io { store.gc }
293
+ capture_io { expect { store.check }.to_not raise_error }
294
+ expect { store.delete_store }.to_not raise_error
260
295
  end
261
296
 
262
297
  it 'should support a successful transaction' do
263
- @store = PEROBS::Store.new(@db_file)
264
- @store.transaction do
265
- @store['person0'] = p0 = @store.new(Person)
298
+ store = PEROBS::Store.new(@db_file)
299
+ store.transaction do
300
+ store['person0'] = p0 = store.new(Person)
266
301
  p0.name = 'Jimmy'
267
302
  end
268
- expect(@store['person0'].name).to eq('Jimmy')
303
+ expect(store['person0'].name).to eq('Jimmy')
304
+
305
+ capture_io { store.gc }
306
+ capture_io { expect { store.check }.to_not raise_error }
307
+ expect { store.delete_store }.to_not raise_error
269
308
  end
270
309
 
271
310
  it 'should handle a failed transaction 1' do
272
- @store = PEROBS::Store.new(@db_file)
311
+ store = PEROBS::Store.new(@db_file)
273
312
  begin
274
- @store.transaction do
275
- @store['person0'] = p0 = @store.new(Person)
313
+ store.transaction do
314
+ store['person0'] = p0 = store.new(Person)
276
315
  p0.name = 'Jimmy'
277
316
  raise POSError
278
317
  end
279
318
  rescue POSError
280
319
  end
281
- expect(@store['person0']).to be_nil
320
+ expect(store['person0']).to be_nil
321
+
322
+ capture_io { store.gc }
323
+ capture_io { expect { store.check }.to_not raise_error }
324
+ expect { store.delete_store }.to_not raise_error
282
325
  end
283
326
 
284
327
  it 'should handle a failed transaction 2' do
285
- @store = PEROBS::Store.new(@db_file)
286
- @store['person1'] = p1 = @store.new(Person)
328
+ store = PEROBS::Store.new(@db_file)
329
+ store['person1'] = p1 = store.new(Person)
287
330
  p1.name = 'Joe'
288
331
  begin
289
- @store.transaction do
290
- @store['person0'] = p0 = @store.new(Person)
332
+ store.transaction do
333
+ store['person0'] = p0 = store.new(Person)
291
334
  p0.name = 'Jimmy'
292
335
  raise POSError
293
336
  end
294
337
  rescue POSError
295
338
  end
296
- expect(@store['person1'].name).to eq('Joe')
297
- expect(@store['person0']).to be_nil
339
+ expect(store['person1'].name).to eq('Joe')
340
+ expect(store['person0']).to be_nil
341
+
342
+ capture_io { store.gc }
343
+ capture_io { expect { store.check }.to_not raise_error }
344
+ expect { store.delete_store }.to_not raise_error
298
345
  end
299
346
 
300
347
  it 'should support a successful nested transaction' do
301
- @store = PEROBS::Store.new(@db_file)
302
- @store.transaction do
303
- @store['person0'] = p0 = @store.new(Person)
348
+ store = PEROBS::Store.new(@db_file)
349
+ store.transaction do
350
+ store['person0'] = p0 = store.new(Person)
304
351
  p0.name = 'Jimmy'
305
- @store.transaction do
306
- @store['person1'] = p1 = @store.new(Person)
352
+ store.transaction do
353
+ store['person1'] = p1 = store.new(Person)
307
354
  p1.name = 'Joe'
308
355
  end
309
356
  end
310
- expect(@store['person0'].name).to eq('Jimmy')
311
- expect(@store['person1'].name).to eq('Joe')
357
+ expect(store['person0'].name).to eq('Jimmy')
358
+ expect(store['person1'].name).to eq('Joe')
359
+
360
+ capture_io { store.gc }
361
+ capture_io { expect { store.check }.to_not raise_error }
362
+ expect { store.delete_store }.to_not raise_error
312
363
  end
313
364
 
314
365
  it 'should handle a failed nested transaction 1' do
315
- @store = PEROBS::Store.new(@db_file)
366
+ store = PEROBS::Store.new(@db_file)
316
367
  begin
317
- @store.transaction do
318
- @store['person0'] = p0 = @store.new(Person)
368
+ store.transaction do
369
+ store['person0'] = p0 = store.new(Person)
319
370
  p0.name = 'Jimmy'
320
371
  begin
321
- @store.transaction do
322
- @store['person1'] = p1 = @store.new(Person)
372
+ store.transaction do
373
+ store['person1'] = p1 = store.new(Person)
323
374
  p1.name = 'Joe'
324
375
  raise POSError
325
376
  end
@@ -328,58 +379,70 @@ describe PEROBS::Store do
328
379
  end
329
380
  rescue POSError
330
381
  end
331
- expect(@store['person0'].name).to eq('Jimmy')
332
- expect(@store['person1']).to be_nil
382
+ expect(store['person0'].name).to eq('Jimmy')
383
+ expect(store['person1']).to be_nil
384
+
385
+ capture_io { store.gc }
386
+ capture_io { expect { store.check }.to_not raise_error }
387
+ expect { store.delete_store }.to_not raise_error
333
388
  end
334
389
 
335
390
  it 'should handle a failed nested transaction 2' do
336
- @store = PEROBS::Store.new(@db_file)
391
+ store = PEROBS::Store.new(@db_file)
337
392
  begin
338
- @store.transaction do
339
- @store['person0'] = p0 = @store.new(Person)
393
+ store.transaction do
394
+ store['person0'] = p0 = store.new(Person)
340
395
  p0.name = 'Jimmy'
341
- @store.transaction do
342
- @store['person1'] = p1 = @store.new(Person)
396
+ store.transaction do
397
+ store['person1'] = p1 = store.new(Person)
343
398
  p1.name = 'Joe'
344
399
  end
345
400
  raise POSError
346
401
  end
347
402
  rescue POSError
348
403
  end
349
- expect(@store['person0']).to be_nil
350
- expect(@store['person1']).to be_nil
404
+ expect(store['person0']).to be_nil
405
+ expect(store['person1']).to be_nil
406
+
407
+ capture_io { store.gc }
408
+ capture_io { expect { store.check }.to_not raise_error }
409
+ expect { store.delete_store }.to_not raise_error
351
410
  end
352
411
 
353
412
  it 'should support a successful 2-level nested transaction' do
354
- @store = PEROBS::Store.new(@db_file)
355
- @store.transaction do
356
- @store['person0'] = p0 = @store.new(Person)
413
+ store = PEROBS::Store.new(@db_file)
414
+ store.transaction do
415
+ store['person0'] = p0 = store.new(Person)
357
416
  p0.name = 'Jimmy'
358
- @store.transaction do
359
- @store['person1'] = p1 = @store.new(Person)
417
+ store.transaction do
418
+ store['person1'] = p1 = store.new(Person)
360
419
  p1.name = 'Joe'
361
- @store.transaction do
362
- @store['person2'] = p2 = @store.new(Person)
420
+ store.transaction do
421
+ store['person2'] = p2 = store.new(Person)
363
422
  p2.name = 'Jane'
364
423
  end
365
424
  end
366
425
  end
367
- expect(@store['person0'].name).to eq('Jimmy')
368
- expect(@store['person1'].name).to eq('Joe')
369
- expect(@store['person2'].name).to eq('Jane')
426
+ expect(store['person0'].name).to eq('Jimmy')
427
+ expect(store['person1'].name).to eq('Joe')
428
+ expect(store['person2'].name).to eq('Jane')
429
+
430
+ capture_io { store.gc }
431
+ capture_io { expect { store.check }.to_not raise_error }
432
+ expect { store.delete_store }.to_not raise_error
370
433
  end
371
434
 
372
435
  it 'should handle a failed 2-level nested transaction 1' do
373
- @store = PEROBS::Store.new(@db_file)
374
- @store.transaction do
375
- @store['person0'] = p0 = @store.new(Person)
436
+ store = PEROBS::Store.new(@db_file)
437
+ store.transaction do
438
+ store['person0'] = p0 = store.new(Person)
376
439
  p0.name = 'Jimmy'
377
- @store.transaction do
378
- @store['person1'] = p1 = @store.new(Person)
440
+ store.transaction do
441
+ store['person1'] = p1 = store.new(Person)
379
442
  p1.name = 'Joe'
380
443
  begin
381
- @store.transaction do
382
- @store['person2'] = p2 = @store.new(Person)
444
+ store.transaction do
445
+ store['person2'] = p2 = store.new(Person)
383
446
  p2.name = 'Jane'
384
447
  raise POSError
385
448
  end
@@ -387,22 +450,26 @@ describe PEROBS::Store do
387
450
  end
388
451
  end
389
452
  end
390
- expect(@store['person0'].name).to eq('Jimmy')
391
- expect(@store['person1'].name).to eq('Joe')
392
- expect(@store['person2']).to be_nil
453
+ expect(store['person0'].name).to eq('Jimmy')
454
+ expect(store['person1'].name).to eq('Joe')
455
+ expect(store['person2']).to be_nil
456
+
457
+ capture_io { store.gc }
458
+ capture_io { expect { store.check }.to_not raise_error }
459
+ expect { store.delete_store }.to_not raise_error
393
460
  end
394
461
 
395
462
  it 'should handle a failed 2-level nested transaction 2' do
396
- @store = PEROBS::Store.new(@db_file)
397
- @store.transaction do
398
- @store['person0'] = p0 = @store.new(Person)
463
+ store = PEROBS::Store.new(@db_file)
464
+ store.transaction do
465
+ store['person0'] = p0 = store.new(Person)
399
466
  p0.name = 'Jimmy'
400
- @store.transaction do
401
- @store['person1'] = p1 = @store.new(Person)
467
+ store.transaction do
468
+ store['person1'] = p1 = store.new(Person)
402
469
  p1.name = 'Joe'
403
470
  begin
404
- @store.transaction do
405
- @store['person2'] = p2 = @store.new(Person)
471
+ store.transaction do
472
+ store['person2'] = p2 = store.new(Person)
406
473
  p2.name = 'Jane'
407
474
  raise POSError
408
475
  end
@@ -411,55 +478,67 @@ describe PEROBS::Store do
411
478
  p1.name = 'Jane'
412
479
  end
413
480
  end
414
- expect(@store['person0'].name).to eq('Jimmy')
415
- expect(@store['person1'].name).to eq('Jane')
416
- expect(@store['person2']).to be_nil
481
+ expect(store['person0'].name).to eq('Jimmy')
482
+ expect(store['person1'].name).to eq('Jane')
483
+ expect(store['person2']).to be_nil
484
+
485
+ capture_io { store.gc }
486
+ capture_io { expect { store.check }.to_not raise_error }
487
+ expect { store.delete_store }.to_not raise_error
417
488
  end
418
489
 
419
490
  it 'should track in-memory objects properly' do
420
- @store = PEROBS::Store.new(@db_file)
421
- expect(@store.statistics[:in_memory_objects]).to eq(1)
422
- @store['person'] = @store.new(Person)
491
+ store = PEROBS::Store.new(@db_file)
492
+ expect(store.statistics[:in_memory_objects]).to eq(1)
493
+ store['person'] = store.new(Person)
423
494
  # We have the root hash and the Person object.
424
- expect(@store.statistics[:in_memory_objects]).to eq(2)
425
- @store.sync
495
+ expect(store.statistics[:in_memory_objects]).to eq(2)
496
+ store.sync
426
497
  GC.start
427
498
  # Now the Person should be gone from memory.
428
499
  # Ruby 2.3 and later has changed the GC so that this does not work
429
500
  # reliably anymore. The GC seems to operate lazyly.
430
- #expect(@store.statistics[:in_memory_objects]).to eq(1)
501
+ #expect(store.statistics[:in_memory_objects]).to eq(1)
502
+
503
+ capture_io { store.gc }
504
+ capture_io { expect { store.check }.to_not raise_error }
505
+ expect { store.delete_store }.to_not raise_error
431
506
  end
432
507
 
433
508
  it 'should handle nested constructors' do
434
- @store = PEROBS::Store.new(@db_file)
435
- @store['root'] = @store.new(O0)
436
- @store.sync
437
- expect(@store.check).to eq(0)
438
- @store.exit
439
-
440
- @store = PEROBS::Store.new(@db_file)
441
- expect(@store.check).to eq(0)
442
- expect(@store['root'].child.parent).to eq(@store['root'])
509
+ store = PEROBS::Store.new(@db_file)
510
+ store['root'] = store.new(O0)
511
+ store.sync
512
+ capture_io { expect(store.check).to eq(0) }
513
+ capture_io { store.exit }
514
+
515
+ store = PEROBS::Store.new(@db_file)
516
+ capture_io { expect(store.check).to eq(0) }
517
+ expect(store['root'].child.parent).to eq(store['root'])
518
+
519
+ capture_io { store.gc }
520
+ capture_io { expect { store.check }.to_not raise_error }
521
+ expect { store.delete_store }.to_not raise_error
443
522
  end
444
523
 
445
524
  it 'should handle frequent updates of objects' do
446
- @store = PEROBS::Store.new(@db_file)
525
+ store = PEROBS::Store.new(@db_file)
447
526
  count = 10000
448
527
  0.upto(count) do |i|
449
528
  key = "Obj#{i}"
450
- @store[key] = p = @store.new(Person)
529
+ store[key] = p = store.new(Person)
451
530
  p.name = "0:#{i}:" + 'X' * rand(64)
452
531
  end
453
532
 
454
533
  0.upto(10) do |iteration|
455
534
  0.upto(count) do |i|
456
535
  key = "Obj#{i}"
457
- p = @store[key]
536
+ p = store[key]
458
537
  p.name = "#{iteration}:#{i}:" + 'X' * rand(64)
459
538
  end
460
539
  0.upto(count) do |i|
461
540
  key = "Obj#{i}"
462
- p = @store[key]
541
+ p = store[key]
463
542
  o_it, o_i, o_x = p.name.split(':')
464
543
  if o_it.to_i != iteration
465
544
  $stderr.puts "Mismatch of #{p._id} with value #{o_it}:#{i}"
@@ -467,37 +546,41 @@ describe PEROBS::Store do
467
546
  expect(o_it.to_i).to eql(iteration)
468
547
  expect(o_i.to_i).to eql(i)
469
548
  end
470
- @store.check
549
+ capture_io { expect(store.check).to eql(0) }
471
550
  end
551
+
552
+ capture_io { store.gc }
553
+ capture_io { expect { store.check }.to_not raise_error }
554
+ expect { store.delete_store }.to_not raise_error
472
555
  end
473
556
 
474
557
  it 'should survive a real world usage test' do
475
558
  options = { :engine => PEROBS::FlatFileDB }
476
- @store = PEROBS::Store.new(@db_file, options)
559
+ store = PEROBS::Store.new(@db_file, options)
477
560
  ref = {}
478
561
 
479
562
  deletions_since_last_gc = 0
480
- 0.upto(10000) do |i|
563
+ 0.upto(5000) do |i|
481
564
  key = "o#{i}"
482
565
  case rand(9)
483
566
  when 0
484
567
  # Add 'A' person
485
568
  value = key + 'A' * rand(512)
486
- @store[key] = p = @store.new(Person)
569
+ store[key] = p = store.new(Person)
487
570
  p.name = value
488
571
  ref[key] = value
489
572
  when 1
490
573
  # Add 'B' person
491
574
  value = key + 'B' * rand(32)
492
- @store[key] = p = @store.new(Person)
575
+ store[key] = p = store.new(Person)
493
576
  p.name = value
494
577
  ref[key] = value
495
578
  when 2
496
579
  # Delete a root entry
497
580
  if ref.keys.length > 11
498
581
  key = ref.keys[rand(ref.keys.length)]
499
- expect(@store[key]).not_to be_nil
500
- @store[key] = nil
582
+ expect(store[key]).not_to be_nil
583
+ store[key] = nil
501
584
  ref.delete(key)
502
585
  deletions_since_last_gc += 1
503
586
  end
@@ -505,34 +588,34 @@ describe PEROBS::Store do
505
588
  # Update a person entry
506
589
  if ref.keys.length > 0
507
590
  key = ref.keys[rand(ref.keys.length)]
508
- expect(@store[key]).not_to be_nil
591
+ expect(store[key]).not_to be_nil
509
592
  value = key + 'C' * rand(996)
510
- p = @store[key]
593
+ p = store[key]
511
594
  p.name = value
512
595
  ref[key] = value
513
596
  end
514
597
  when 4
515
598
  # Call garbage collector
516
599
  if rand(60) == 0
517
- @store.gc
518
- stats = @store.statistics
600
+ capture_io { store.gc }
601
+ stats = store.statistics
519
602
  expect(stats.marked_objects).to eq(ref.length)
520
603
  expect(stats.swept_objects).to eq(deletions_since_last_gc)
521
604
  deletions_since_last_gc = 0
522
- expect(@store.gc).to eq(deletions_since_last_gc)
605
+ capture_io { expect(store.gc).to eq(deletions_since_last_gc) }
523
606
  end
524
607
  when 5
525
608
  # Sync store and reload
526
609
  if rand(15) == 0
527
- @store.exit
528
- @store = PEROBS::Store.new(@db_file, options)
610
+ capture_io { store.exit }
611
+ store = PEROBS::Store.new(@db_file, options)
529
612
  end
530
613
  when 6
531
614
  # Replace an entry with 'C' person
532
615
  if ref.keys.length > 13
533
616
  key = ref.keys[(ref.keys.length / 13).to_i]
534
617
  value = key + 'D' * rand(1024)
535
- @store[key] = p = @store.new(Person)
618
+ store[key] = p = store.new(Person)
536
619
  p.name = value
537
620
  ref[key] = value
538
621
  deletions_since_last_gc += 1
@@ -540,57 +623,65 @@ describe PEROBS::Store do
540
623
  when 7
541
624
  # Sync and check store
542
625
  if rand(50) == 0
543
- #@store.sync
544
- expect(@store.check(false)).to eq(0)
626
+ #store.sync
627
+ capture_io { expect(store.check(false)).to eq(0) }
545
628
  end
546
629
  when 8
547
630
  # Compare a random entry with reference entry
548
631
  if ref.keys.length > 0
549
632
  key = ref.keys[rand(ref.keys.length)]
550
- expect(@store[key].name).to eq(ref[key])
633
+ expect(store[key].name).to eq(ref[key])
551
634
  end
552
635
  end
553
636
  #ref.each do |k, v|
554
- # expect(@store[k].name).to eq(v), "failure in mode #{i}"
637
+ # expect(store[k].name).to eq(v), "failure in mode #{i}"
555
638
  #end
556
639
  end
557
640
 
558
641
  ref.each do |k, v|
559
- expect(@store[k].name).to eq(v)
642
+ expect(store[k].name).to eq(v)
560
643
  end
644
+
645
+ capture_io { store.gc }
646
+ capture_io { expect { store.check }.to_not raise_error }
647
+ expect { store.delete_store }.to_not raise_error
561
648
  end
562
649
 
563
650
  it 'should copy the database' do
564
- @store = PEROBS::Store.new(@db_file)
565
- @store['person0'] = p0 = @store.new(Person)
651
+ store = PEROBS::Store.new(@db_file)
652
+ store['person0'] = p0 = store.new(Person)
566
653
  id0 = p0._id
567
- p1 = @store.new(Person)
654
+ p1 = store.new(Person)
568
655
  id1 = p1._id
569
- p2 = @store.new(Person)
656
+ p2 = store.new(Person)
570
657
  id2 = p2._id
571
658
  p1.related = p2
572
659
  p2.related = p1
573
660
  p0.related = p1
574
- p3 = @store.new(PEROBS::Array)
575
- @store['persons'] = p3
661
+ p3 = store.new(PEROBS::Array)
662
+ store['persons'] = p3
576
663
  p3 << p0
577
664
  p3 << p1
578
665
  p3 << p2
579
666
  p0 = p1 = p2 = p3 = nil
580
- expect(@store['person0']._id).to eq(id0)
581
- expect(@store['person0'].related._id).to eq(id1)
582
- expect(@store['person0'].related.related._id).to eq(id2)
583
-
584
- @store.copy(@db_file_new, { :engine => PEROBS::FlatFileDB })
585
- @store.delete_store
586
-
587
- @store = PEROBS::Store.new(@db_file_new, { :engine => PEROBS::FlatFileDB })
588
- expect(@store['person0']._id).to eq(id0)
589
- expect(@store['person0'].related._id).to eq(id1)
590
- expect(@store['person0'].related.related._id).to eq(id2)
591
- expect(@store['persons'][0]).to eq(@store['person0'])
592
- expect(@store['persons'][1]).to eq(@store['person0'].related)
593
- expect(@store['persons'][2]).to eq(@store['person0'].related.related)
667
+ expect(store['person0']._id).to eq(id0)
668
+ expect(store['person0'].related._id).to eq(id1)
669
+ expect(store['person0'].related.related._id).to eq(id2)
670
+
671
+ store.copy(@db_file_new, { :engine => PEROBS::FlatFileDB })
672
+ store.delete_store
673
+
674
+ store = PEROBS::Store.new(@db_file_new, { :engine => PEROBS::FlatFileDB })
675
+ expect(store['person0']._id).to eq(id0)
676
+ expect(store['person0'].related._id).to eq(id1)
677
+ expect(store['person0'].related.related._id).to eq(id2)
678
+ expect(store['persons'][0]).to eq(store['person0'])
679
+ expect(store['persons'][1]).to eq(store['person0'].related)
680
+ expect(store['persons'][2]).to eq(store['person0'].related.related)
681
+
682
+ capture_io { store.gc }
683
+ capture_io { expect { store.check }.to_not raise_error }
684
+ expect { store.delete_store }.to_not raise_error
594
685
  end
595
686
 
596
687
  end