perobs 4.0.0 → 4.4.0

Sign up to get free protection for your applications and to get access to all the features.
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
+