perobs 4.0.0 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +27 -16
  3. data/lib/perobs/Array.rb +66 -19
  4. data/lib/perobs/BTree.rb +106 -15
  5. data/lib/perobs/BTreeBlob.rb +4 -3
  6. data/lib/perobs/BTreeDB.rb +5 -4
  7. data/lib/perobs/BTreeNode.rb +482 -156
  8. data/lib/perobs/BTreeNodeLink.rb +10 -0
  9. data/lib/perobs/BigArray.rb +285 -0
  10. data/lib/perobs/BigArrayNode.rb +1002 -0
  11. data/lib/perobs/BigHash.rb +246 -0
  12. data/lib/perobs/BigTree.rb +197 -0
  13. data/lib/perobs/BigTreeNode.rb +873 -0
  14. data/lib/perobs/Cache.rb +48 -10
  15. data/lib/perobs/ConsoleProgressMeter.rb +61 -0
  16. data/lib/perobs/DataBase.rb +4 -3
  17. data/lib/perobs/DynamoDB.rb +57 -15
  18. data/lib/perobs/EquiBlobsFile.rb +155 -50
  19. data/lib/perobs/FNV_Hash_1a_64.rb +54 -0
  20. data/lib/perobs/FlatFile.rb +519 -227
  21. data/lib/perobs/FlatFileBlobHeader.rb +113 -54
  22. data/lib/perobs/FlatFileDB.rb +49 -23
  23. data/lib/perobs/FuzzyStringMatcher.rb +175 -0
  24. data/lib/perobs/Hash.rb +127 -33
  25. data/lib/perobs/IDList.rb +144 -0
  26. data/lib/perobs/IDListPage.rb +107 -0
  27. data/lib/perobs/IDListPageFile.rb +180 -0
  28. data/lib/perobs/IDListPageRecord.rb +142 -0
  29. data/lib/perobs/Object.rb +18 -15
  30. data/lib/perobs/ObjectBase.rb +46 -5
  31. data/lib/perobs/PersistentObjectCache.rb +57 -68
  32. data/lib/perobs/PersistentObjectCacheLine.rb +24 -12
  33. data/lib/perobs/ProgressMeter.rb +97 -0
  34. data/lib/perobs/SpaceManager.rb +273 -0
  35. data/lib/perobs/SpaceTree.rb +21 -12
  36. data/lib/perobs/SpaceTreeNode.rb +53 -61
  37. data/lib/perobs/Store.rb +264 -145
  38. data/lib/perobs/version.rb +1 -1
  39. data/lib/perobs.rb +2 -0
  40. data/perobs.gemspec +4 -4
  41. data/test/Array_spec.rb +15 -6
  42. data/test/BTree_spec.rb +6 -2
  43. data/test/BigArray_spec.rb +261 -0
  44. data/test/BigHash_spec.rb +152 -0
  45. data/test/BigTreeNode_spec.rb +153 -0
  46. data/test/BigTree_spec.rb +259 -0
  47. data/test/EquiBlobsFile_spec.rb +105 -1
  48. data/test/FNV_Hash_1a_64_spec.rb +59 -0
  49. data/test/FlatFileDB_spec.rb +198 -14
  50. data/test/FuzzyStringMatcher_spec.rb +261 -0
  51. data/test/Hash_spec.rb +13 -3
  52. data/test/IDList_spec.rb +77 -0
  53. data/test/LegacyDBs/LegacyDB.rb +155 -0
  54. data/test/LegacyDBs/version_3/class_map.json +1 -0
  55. data/test/LegacyDBs/version_3/config.json +1 -0
  56. data/test/LegacyDBs/version_3/database.blobs +0 -0
  57. data/test/LegacyDBs/version_3/database_spaces.blobs +0 -0
  58. data/test/LegacyDBs/version_3/index.blobs +0 -0
  59. data/test/LegacyDBs/version_3/version +1 -0
  60. data/test/LockFile_spec.rb +9 -6
  61. data/test/SpaceManager_spec.rb +176 -0
  62. data/test/SpaceTree_spec.rb +4 -1
  63. data/test/Store_spec.rb +305 -203
  64. data/test/spec_helper.rb +9 -4
  65. metadata +57 -16
  66. data/lib/perobs/BTreeNodeCache.rb +0 -109
  67. data/lib/perobs/TreeDB.rb +0 -277
data/lib/perobs.rb CHANGED
@@ -27,3 +27,5 @@
27
27
 
28
28
  require "perobs/version"
29
29
  require 'perobs/Store'
30
+ require 'perobs/ConsoleProgressMeter'
31
+ require 'perobs/FuzzyStringMatcher'
data/perobs.gemspec CHANGED
@@ -16,9 +16,9 @@ GEM_SPEC = Gem::Specification.new do |spec|
16
16
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
18
  spec.require_paths = ["lib"]
19
- spec.required_ruby_version = '>=2.0'
19
+ spec.required_ruby_version = '>=2.4'
20
20
 
21
- spec.add_development_dependency 'bundler', '~> 1.6'
22
- spec.add_development_dependency 'yard', '~>0.8.7'
23
- spec.add_development_dependency 'rake', '~> 10.0'
21
+ spec.add_development_dependency 'bundler', '~> 2.3'
22
+ spec.add_development_dependency 'yard', '~>0.9.12'
23
+ spec.add_development_dependency 'rake', '~> 13.0.3'
24
24
  end
data/test/Array_spec.rb CHANGED
@@ -169,8 +169,13 @@ describe PEROBS::Array do
169
169
 
170
170
  it 'should support collect!()' do
171
171
  a = cpa([ 1, 'cat', 1..1 ])
172
- expect(a.collect! { |e| e.class.to_s }).to eq([ 'Integer', 'String', 'Range' ])
173
- pcheck { expect(@store['a'].to_a).to eq([ 'Integer', 'String', 'Range' ]) }
172
+ if RUBY_VERSION < '2.2'
173
+ expect(a.collect! { |e| e.class.to_s }.to_a).to eq([ 'Fixnum', 'String', 'Range' ])
174
+ pcheck { expect(@store['a'].to_a).to eq([ 'Fixnum', 'String', 'Range' ]) }
175
+ else
176
+ expect(a.collect! { |e| e.class.to_s }.to_a).to eq([ 'Integer', 'String', 'Range' ])
177
+ pcheck { expect(@store['a'].to_a).to eq([ 'Integer', 'String', 'Range' ]) }
178
+ end
174
179
 
175
180
  a = cpa([ 1, 'cat', 1..1 ])
176
181
  expect(a.collect! { 99 }).to eq([ 99, 99, 99])
@@ -179,8 +184,13 @@ describe PEROBS::Array do
179
184
 
180
185
  it 'should support map!()' do
181
186
  a = cpa([ 1, 'cat', 1..1 ])
182
- expect(a.map! { |e| e.class.to_s }).to eq([ 'Integer', 'String', 'Range' ])
183
- pcheck { expect(@store['a']).to eq([ 'Integer', 'String', 'Range' ]) }
187
+ if RUBY_VERSION < '2.2'
188
+ expect(a.map! { |e| e.class.to_s }.to_a).to eq([ 'Fixnum', 'String', 'Range' ])
189
+ pcheck { expect(@store['a'].to_a).to eq([ 'Fixnum', 'String', 'Range' ]) }
190
+ else
191
+ expect(a.map! { |e| e.class.to_s }.to_a).to eq([ 'Integer', 'String', 'Range' ])
192
+ pcheck { expect(@store['a'].to_a).to eq([ 'Integer', 'String', 'Range' ]) }
193
+ end
184
194
 
185
195
  a = cpa([ 1, 'cat', 1..1 ])
186
196
  expect(a.map! { 99 }).to eq([ 99, 99, 99])
@@ -242,9 +252,8 @@ describe PEROBS::Array do
242
252
  it 'should catch a leaked PEROBS::ObjectBase object' do
243
253
  @store['a'] = a = @store.new(PEROBS::Array)
244
254
  o = @store.new(PO)
245
- a[0] = o.get_self
246
255
  PEROBS.log.open(StringIO.new)
247
- expect { @store.exit }.to raise_error(PEROBS::FatalError)
256
+ expect { a[0] = o.get_self }.to raise_error(PEROBS::FatalError)
248
257
  PEROBS.log.open($stderr)
249
258
  end
250
259
 
data/test/BTree_spec.rb CHANGED
@@ -27,13 +27,16 @@ require 'fileutils'
27
27
 
28
28
  require 'spec_helper'
29
29
  require 'perobs/BTree'
30
+ require 'perobs/ProgressMeter'
30
31
 
31
32
  describe PEROBS::BTree do
32
33
 
33
34
  before(:all) do
34
35
  @db_dir = generate_db_name('BTree')
35
36
  FileUtils.mkdir_p(@db_dir)
36
- @m = PEROBS::BTree.new(@db_dir, 'btree', 11)
37
+ @m = PEROBS::BTree.new(@db_dir, 'btree', 11, PEROBS::ProgressMeter.new)
38
+ PEROBS.log.level = Logger::ERROR
39
+ PEROBS.log.open($stderr)
37
40
  end
38
41
 
39
42
  after(:all) do
@@ -48,6 +51,7 @@ describe PEROBS::BTree do
48
51
  @m.open
49
52
  expect(@m.to_s).to eql("o--- @1\n")
50
53
  #expect(@m.to_a).to eql([])
54
+ expect(@m.check).to be true
51
55
  end
52
56
 
53
57
  it 'should support adding sequential key/value pairs' do
@@ -128,7 +132,7 @@ describe PEROBS::BTree do
128
132
  it 'should survive a real-world usage test' do
129
133
  @m.clear
130
134
  ref = {}
131
- 0.upto(50000) do
135
+ 0.upto(20000) do
132
136
  case rand(5)
133
137
  when 0
134
138
  0.upto(2) do
@@ -0,0 +1,261 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright (c) 2015, 2016 by Chris Schlaeger <chris@taskjuggler.org>
4
+ #
5
+ # This file contains tests for Array that are similar to the tests for the
6
+ # Array implementation in MRI. The ideas of these tests were replicated in
7
+ # this code.
8
+ #
9
+ # MIT License
10
+ #
11
+ # Permission is hereby granted, free of charge, to any person obtaining
12
+ # a copy of this software and associated documentation files (the
13
+ # "Software"), to deal in the Software without restriction, including
14
+ # without limitation the rights to use, copy, modify, merge, publish,
15
+ # distribute, sublicense, and/or sell copies of the Software, and to
16
+ # permit persons to whom the Software is furnished to do so, subject to
17
+ # the following conditions:
18
+ #
19
+ # The above copyright notice and this permission notice shall be
20
+ # included in all copies or substantial portions of the Software.
21
+ #
22
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29
+
30
+ require 'spec_helper'
31
+
32
+ require 'perobs'
33
+ require 'perobs/BigArray'
34
+
35
+ NODE_ENTRIES = 6
36
+
37
+ describe PEROBS::BigArray do
38
+
39
+ before(:all) do
40
+ @db_name = generate_db_name(__FILE__)
41
+ @store = PEROBS::Store.new(@db_name)
42
+ end
43
+
44
+ before(:each) do
45
+ @store['array'] = @a = @store.new(PEROBS::BigArray, NODE_ENTRIES)
46
+ end
47
+
48
+ after(:each) do
49
+ @store['array'] = @a = nil
50
+ @store.gc
51
+ end
52
+
53
+ after(:all) do
54
+ @store.delete_store
55
+ end
56
+
57
+ it 'should be empty on create' do
58
+ expect(@a.empty?).to be true
59
+ expect(@a.length).to eq(0)
60
+ expect(@a.check).to be true
61
+ expect(@a[0]).to be nil
62
+ expect(@a.first).to be nil
63
+ expect(@a.last).to be nil
64
+ end
65
+
66
+ it 'should append the first element' do
67
+ @a << 0
68
+ expect(@a.empty?).to be false
69
+ expect(@a[0]).to eq(0)
70
+ expect(@a.first).to eq(0)
71
+ expect(@a.last).to eq(0)
72
+ expect(@a.length).to eq(1)
73
+ expect(@a.check).to be true
74
+ end
75
+
76
+ it 'should fill 10 nodes with appends' do
77
+ (10 * NODE_ENTRIES).times do |i|
78
+ @a << i
79
+ expect(@a.check).to be true
80
+ expect(@a.length).to eq(i + 1)
81
+ end
82
+ expect(@a.first).to eq(0)
83
+ expect(@a.last).to eq(10 * NODE_ENTRIES - 1)
84
+ end
85
+
86
+ it 'should insert at 0' do
87
+ @a.insert(0, 0)
88
+ expect(@a.empty?).to be false
89
+ end
90
+
91
+ it 'should insert at end' do
92
+ 0.upto(3 * NODE_ENTRIES) do |i|
93
+ @a.insert(i, i)
94
+ expect(@a.check).to be true
95
+ expect(@a.length).to eq(i + 1)
96
+ end
97
+ end
98
+
99
+ it 'should insert in the middle' do
100
+ 0.upto(NODE_ENTRIES - 1) do |i|
101
+ @a << 9999
102
+ end
103
+ 0.upto(3 * NODE_ENTRIES) do |i|
104
+ @a.insert(i, i)
105
+ expect(@a.check).to be true
106
+ expect(@a.length).to eq(i + 1 + NODE_ENTRIES)
107
+ end
108
+ end
109
+
110
+ it 'should convert to a Ruby Array' do
111
+ expect(@a.to_a).to eql([])
112
+ (3 * NODE_ENTRIES).times do |i|
113
+ @a << i
114
+ end
115
+ expect(@a.to_a).to eql((0..3 * NODE_ENTRIES - 1).to_a)
116
+ end
117
+
118
+ it 'should support the [] operator' do
119
+ expect(@a[0]).to be nil
120
+ expect(@a[-1]).to be nil
121
+ @a[0] = 0
122
+ expect(@a[0]).to eq(0)
123
+ 1.upto(3 * NODE_ENTRIES) do |i|
124
+ @a.insert(i, i)
125
+ end
126
+ 0.upto(3 * NODE_ENTRIES) do |i|
127
+ expect(@a[i]).to eq(i)
128
+ end
129
+ expect(@a[3 * NODE_ENTRIES + 1]).to be nil
130
+ 0.upto(3 * NODE_ENTRIES) do |i|
131
+ expect(@a[-3 * NODE_ENTRIES + i - 1]).to eq(i)
132
+ end
133
+ expect(@a[-3 * NODE_ENTRIES - 2]).to be nil
134
+ (3 * NODE_ENTRIES + 1).upto(4 * NODE_ENTRIES) do |i|
135
+ expect(@a[i]).to be nil
136
+ end
137
+ end
138
+
139
+ it 'should delete elements' do
140
+ expect(@a.delete_at(0)).to be nil
141
+ expect(@a.length).to eq(0)
142
+ expect(@a.check).to be true
143
+ expect(@a.delete_at(-1)).to be nil
144
+ expect(@a.length).to eq(0)
145
+ expect(@a.check).to be true
146
+ @a << 0
147
+ expect(@a.delete_at(0)).to eql(0)
148
+ expect(@a.length).to eq(0)
149
+ expect(@a.check).to be true
150
+
151
+ n = 5 * NODE_ENTRIES
152
+ 0.upto(n) { |i| @a.insert(i, i) }
153
+ 0.upto(n) do |i|
154
+ expect(@a.delete_at(0)).to eql(i)
155
+ expect(@a.check).to be true
156
+ end
157
+
158
+ 0.upto(n) { |i| @a.insert(i, i) }
159
+ n.downto(0) do |i|
160
+ expect(@a.delete_at(-1)).to eql(i)
161
+ expect(@a.check).to be true
162
+ end
163
+
164
+ n = 15 * NODE_ENTRIES
165
+ 0.upto(n - 1) { |i| @a.insert(i, i) }
166
+ expect(@a.delete_at(n + 2)).to be nil
167
+ expect(@a.delete_at(-(n + 2))).to be nil
168
+ expect(@a.size).to eql(n)
169
+
170
+ n.times do |i|
171
+ idx = rand(@a.size)
172
+ @a.delete_at(idx)
173
+ expect(@a.size).to be (n - 1 - i)
174
+ expect(@a.check).to be true
175
+ end
176
+ expect(@a.size).to eql(0)
177
+ end
178
+
179
+ it 'should fill the gaps' do
180
+ 1.upto(4) do |i|
181
+ idx = i * NODE_ENTRIES * NODE_ENTRIES
182
+ @a[idx] = idx
183
+ expect(@a[idx - 1]).to be nil
184
+ expect(@a[idx + 1]).to be nil
185
+ expect(@a.check).to be true
186
+ end
187
+ expect(@a[0]).to be nil
188
+ end
189
+
190
+ it 'should insert after a gap' do
191
+ ref = []
192
+ 10.times do |i|
193
+ idx = 10 + i * 3
194
+ @a[idx] = idx
195
+ ref[idx] = idx
196
+ expect(@a[idx]).to eql(idx)
197
+ expect(@a.check).to be true
198
+ end
199
+ 10.times do |i|
200
+ idx = i * 5
201
+ @a[idx] = idx
202
+ ref[idx] = idx
203
+ expect(@a[idx]).to eql(idx)
204
+ expect(@a.check).to be true
205
+ end
206
+ expect(@a.check).to be true
207
+ expect(@a.to_a).to eql(ref)
208
+ end
209
+
210
+ it 'should iterate over all values' do
211
+ n = 3 * NODE_ENTRIES
212
+ 0.upto(n) { |i| @a.insert(i, i) }
213
+
214
+ i = 0
215
+ @a.each do |v|
216
+ expect(v).to eql(i)
217
+ i += 1
218
+ end
219
+ end
220
+
221
+ it 'should iterate over all values in reverse order' do
222
+ n = 3 * NODE_ENTRIES
223
+ 0.upto(n) { |i| @a.insert(i, i) }
224
+
225
+ i = 0
226
+ @a.reverse_each do |v|
227
+ expect(v).to eql(n - i)
228
+ i += 1
229
+ end
230
+ end
231
+
232
+ it 'should insert at the beginning' do
233
+ (5 * NODE_ENTRIES).downto(0) do |i|
234
+ @a.insert(0, i)
235
+ end
236
+ expect(@a.check).to be true
237
+ a = Array.new(5 * NODE_ENTRIES + 1) { |i| i }
238
+ expect(@a.to_a).to eq(a)
239
+ end
240
+
241
+ it 'should persist the data' do
242
+ db_name = generate_db_name(__FILE__ + "_persist")
243
+ store = PEROBS::Store.new(db_name)
244
+ store['array'] = a = store.new(PEROBS::BigArray, NODE_ENTRIES)
245
+
246
+ (3 * NODE_ENTRIES).times do |i|
247
+ a.insert(i, i)
248
+ end
249
+ expect(a.length).to eq(3 * NODE_ENTRIES)
250
+ store.exit
251
+
252
+ store = PEROBS::Store.new(db_name)
253
+ a = store['array']
254
+ (3 * NODE_ENTRIES).times do |i|
255
+ expect(a[i]).to eql(i)
256
+ end
257
+ expect(a.length).to eq(3 * NODE_ENTRIES)
258
+ store.delete_store
259
+ end
260
+
261
+ end
@@ -0,0 +1,152 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright (c) 2017, 2019 by Chris Schlaeger <chris@taskjuggler.org>
4
+ #
5
+ # This file contains tests for Hash that are similar to the tests for the
6
+ # Hash implementation in MRI. The ideas of these tests were replicated in
7
+ # this code.
8
+ #
9
+ # MIT License
10
+ #
11
+ # Permission is hereby granted, free of charge, to any person obtaining
12
+ # a copy of this software and associated documentation files (the
13
+ # "Software"), to deal in the Software without restriction, including
14
+ # without limitation the rights to use, copy, modify, merge, publish,
15
+ # distribute, sublicense, and/or sell copies of the Software, and to
16
+ # permit persons to whom the Software is furnished to do so, subject to
17
+ # the following conditions:
18
+ #
19
+ # The above copyright notice and this permission notice shall be
20
+ # included in all copies or substantial portions of the Software.
21
+ #
22
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29
+
30
+ require 'spec_helper'
31
+
32
+ require 'perobs'
33
+ require 'perobs/BigHash'
34
+
35
+ class PEROBS::BigHash
36
+
37
+ # Redefine the hash method to make collisions very likely. This will help to
38
+ # test the collision handling with small amounts of data.
39
+ #def hash_key(key)
40
+ # key.hash & 0xF
41
+ #end
42
+
43
+ end
44
+
45
+ ENTRIES = 200
46
+
47
+ describe PEROBS::Hash do
48
+
49
+ before(:all) do
50
+ @db_name = generate_db_name(__FILE__)
51
+ end
52
+
53
+ before(:each) do
54
+ @store = PEROBS::Store.new(@db_name)
55
+ @h = @store.new(PEROBS::BigHash)
56
+ @store['hash'] = @h
57
+ end
58
+
59
+ after(:each) do
60
+ @store.delete_store
61
+ end
62
+
63
+ it 'should support storing and retrieving an object' do
64
+ expect(@h.check).to be true
65
+ expect(@h.length).to eql(0)
66
+ expect(@h.empty?).to be true
67
+ expect(@h.keys).to eql([])
68
+ @h['foo'] = 'bar'
69
+ expect(@h.check).to be true
70
+ expect(@h['foo']).to eql('bar')
71
+ expect(@h.length).to eql(1)
72
+ expect(@h.keys).to eql([ 'foo' ])
73
+ end
74
+
75
+ it 'should store a few objects' do
76
+ 20.times do |i|
77
+ @h["bar#{i}"] = "foo#{i}"
78
+ end
79
+ expect(@h.size).to eql(20)
80
+ end
81
+
82
+ it 'should return nil for unknown objects' do
83
+ expect(@h['bar']).to be_nil
84
+ end
85
+
86
+ it 'should be able to store values with hash collisions' do
87
+ ENTRIES.times do |i|
88
+ @h["key#{i}"] = i
89
+ end
90
+ expect(@h.check).to be true
91
+ expect(@h.length).to eql(ENTRIES)
92
+
93
+ ENTRIES.times do |i|
94
+ expect(@h["key#{i}"]).to eql(i)
95
+ end
96
+ end
97
+
98
+ it 'should replace existing entries' do
99
+ ENTRIES.times do |i|
100
+ @h["key#{i}"] = 2 * i
101
+ end
102
+ expect(@h.check).to be true
103
+ expect(@h.length).to eql(ENTRIES)
104
+
105
+ ENTRIES.times do |i|
106
+ expect(@h["key#{i}"]).to eql(2 * i)
107
+ end
108
+ expect(@h.length).to eql(ENTRIES)
109
+ end
110
+
111
+ it 'should fail to delete a non-existing entry' do
112
+ expect(@h.delete('foo')).to be_nil
113
+ expect(@h.check).to be true
114
+ end
115
+
116
+ it 'should delete existing entries' do
117
+ (1..ENTRIES).to_a.shuffle.each do |i|
118
+ @h["key#{i}"] = 2 * i
119
+ end
120
+ expect(@h.check).to be true
121
+ expect(@h.length).to eql(ENTRIES)
122
+ (1..ENTRIES).to_a.shuffle.each do |i|
123
+ expect(@h.delete("key#{i}")).to eql(2 * i)
124
+ end
125
+ end
126
+
127
+ it 'should persist all objects' do
128
+ db_name = generate_db_name(__FILE__ + "_persist")
129
+ store = PEROBS::Store.new(db_name)
130
+ h = store['hash'] = store.new(PEROBS::BigHash)
131
+ n = ENTRIES
132
+ n.times do |i|
133
+ h["key#{i}"] = 2 * i
134
+ end
135
+ expect(h.check).to be true
136
+ expect(h.length).to eql(n)
137
+ expect(store.check).to eql(0)
138
+ store.exit
139
+
140
+ store = PEROBS::Store.new(db_name)
141
+ expect(store.check).to eql(0)
142
+ h = store['hash']
143
+ n.times do |i|
144
+ expect(h["key#{i}"]).to eql(2 * i)
145
+ end
146
+ expect(h.check).to be true
147
+ expect(h.length).to eql(n)
148
+ store.delete_store
149
+ end
150
+
151
+ end
152
+
@@ -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
+