strokedb 0.0.2.1 → 0.0.2.2

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 (192) hide show
  1. data/README +18 -20
  2. data/bench.html +4001 -0
  3. data/bin/strokedb +14 -0
  4. data/examples/movies.rb +105 -0
  5. data/examples/movies2.rb +97 -0
  6. data/examples/strokewiki/README +28 -0
  7. data/examples/strokewiki/view/edit.xhtml +27 -0
  8. data/examples/strokewiki/view/new.xhtml +26 -0
  9. data/examples/strokewiki/view/pages.xhtml +27 -0
  10. data/examples/strokewiki/view/show.xhtml +40 -0
  11. data/examples/strokewiki/view/versions.xhtml +25 -0
  12. data/examples/strokewiki/wiki.rb +106 -0
  13. data/examples/todo.rb +92 -0
  14. data/lib/strokedb.rb +85 -0
  15. data/lib/{config → strokedb}/config.rb +14 -9
  16. data/lib/strokedb/console.rb +87 -0
  17. data/lib/strokedb/core_ext.rb +10 -0
  18. data/lib/{util/ext → strokedb/core_ext}/blank.rb +1 -1
  19. data/lib/{util/ext → strokedb/core_ext}/enumerable.rb +0 -0
  20. data/lib/{util/ext → strokedb/core_ext}/fixnum.rb +0 -0
  21. data/lib/strokedb/core_ext/float.rb +4 -0
  22. data/lib/{util/ext → strokedb/core_ext}/hash.rb +0 -0
  23. data/lib/strokedb/core_ext/infinity.rb +33 -0
  24. data/lib/strokedb/core_ext/kernel.rb +41 -0
  25. data/lib/strokedb/core_ext/object.rb +16 -0
  26. data/lib/{util/ext → strokedb/core_ext}/string.rb +28 -1
  27. data/lib/strokedb/core_ext/symbol.rb +13 -0
  28. data/lib/strokedb/data_structures.rb +5 -0
  29. data/lib/strokedb/data_structures/chunked_skiplist.rb +123 -0
  30. data/lib/{data_structures → strokedb/data_structures}/inverted_list.rb +0 -0
  31. data/lib/{data_structures → strokedb/data_structures}/point_query.rb +0 -0
  32. data/lib/strokedb/data_structures/simple_skiplist.rb +350 -0
  33. data/lib/{data_structures → strokedb/data_structures}/skiplist.rb +1 -1
  34. data/lib/{document → strokedb}/document.rb +180 -71
  35. data/lib/{document → strokedb/document}/callback.rb +0 -0
  36. data/lib/{document → strokedb/document}/delete.rb +2 -2
  37. data/lib/strokedb/document/dsl.rb +4 -0
  38. data/lib/{document → strokedb/document/dsl}/associations.rb +0 -0
  39. data/lib/{document → strokedb/document/dsl}/coercions.rb +0 -0
  40. data/lib/strokedb/document/dsl/meta_dsl.rb +7 -0
  41. data/lib/{document → strokedb/document/dsl}/validations.rb +26 -21
  42. data/lib/{document → strokedb/document/dsl}/virtualize.rb +0 -0
  43. data/lib/{document → strokedb/document}/meta.rb +92 -29
  44. data/lib/{document → strokedb/document}/slot.rb +17 -5
  45. data/lib/{document → strokedb/document}/util.rb +0 -0
  46. data/lib/{document → strokedb/document}/versions.rb +2 -2
  47. data/lib/strokedb/index.rb +2 -0
  48. data/lib/strokedb/nsurl.rb +24 -0
  49. data/lib/strokedb/store.rb +149 -0
  50. data/lib/strokedb/stores.rb +6 -0
  51. data/lib/{stores → strokedb/stores}/chainable_storage.rb +20 -14
  52. data/lib/strokedb/stores/file_storage.rb +118 -0
  53. data/lib/{stores/inverted_list_index → strokedb/stores}/inverted_list_file_storage.rb +50 -0
  54. data/lib/strokedb/stores/memory_storage.rb +80 -0
  55. data/lib/{stores → strokedb/stores}/remote_store.rb +10 -4
  56. data/lib/strokedb/sync.rb +4 -0
  57. data/lib/{sync → strokedb/sync}/chain_sync.rb +0 -0
  58. data/lib/{sync → strokedb/sync}/diff.rb +12 -1
  59. data/lib/{sync/stroke_diff → strokedb/sync/diff}/array.rb +1 -1
  60. data/lib/{sync/stroke_diff → strokedb/sync/diff}/default.rb +0 -0
  61. data/lib/{sync/stroke_diff → strokedb/sync/diff}/hash.rb +1 -1
  62. data/lib/{sync/stroke_diff → strokedb/sync/diff}/string.rb +1 -1
  63. data/lib/{sync → strokedb/sync}/lamport_timestamp.rb +0 -0
  64. data/lib/{sync → strokedb/sync}/store_sync.rb +15 -7
  65. data/lib/strokedb/transaction.rb +78 -0
  66. data/lib/{util → strokedb}/util.rb +14 -7
  67. data/lib/strokedb/util/attach_dsl.rb +29 -0
  68. data/lib/{util → strokedb/util}/blankslate.rb +0 -0
  69. data/lib/strokedb/util/class_optimization.rb +93 -0
  70. data/lib/{util → strokedb/util}/inflect.rb +0 -0
  71. data/lib/strokedb/util/java_util.rb +13 -0
  72. data/lib/{util → strokedb/util}/lazy_array.rb +0 -0
  73. data/lib/{util → strokedb/util}/lazy_mapping_array.rb +4 -0
  74. data/lib/{util → strokedb/util}/lazy_mapping_hash.rb +0 -0
  75. data/lib/{util → strokedb/util}/serialization.rb +21 -0
  76. data/lib/strokedb/util/uuid.rb +159 -0
  77. data/lib/{util → strokedb/util}/xml.rb +0 -0
  78. data/lib/{view → strokedb}/view.rb +2 -2
  79. data/lib/strokedb/volumes.rb +5 -0
  80. data/lib/strokedb/volumes/archive_volume.rb +165 -0
  81. data/lib/strokedb/volumes/block_volume.rb +169 -0
  82. data/lib/strokedb/volumes/distributed_pointer.rb +43 -0
  83. data/lib/strokedb/volumes/fixed_length_skiplist_volume.rb +109 -0
  84. data/lib/strokedb/volumes/map_volume.rb +268 -0
  85. data/meta/MANIFEST +175 -0
  86. data/script/console +2 -70
  87. data/spec/integration/remote_store_spec.rb +70 -0
  88. data/spec/integration/search_spec.rb +76 -0
  89. data/spec/integration/spec_helper.rb +1 -0
  90. data/spec/lib/spec_helper.rb +1 -0
  91. data/spec/lib/strokedb/config_spec.rb +250 -0
  92. data/spec/lib/strokedb/core_ext/blank_spec.rb +20 -0
  93. data/spec/lib/strokedb/core_ext/extract_spec.rb +42 -0
  94. data/spec/lib/strokedb/core_ext/float_spec.rb +62 -0
  95. data/spec/lib/strokedb/core_ext/infinity_spec.rb +40 -0
  96. data/spec/lib/strokedb/core_ext/spec_helper.rb +1 -0
  97. data/spec/lib/strokedb/core_ext/string_spec.rb +25 -0
  98. data/spec/lib/strokedb/core_ext/symbol_spec.rb +8 -0
  99. data/spec/lib/strokedb/data_structures/chunked_skiplist_spec.rb +144 -0
  100. data/spec/lib/strokedb/data_structures/inverted_list_spec.rb +172 -0
  101. data/spec/lib/strokedb/data_structures/simple_skiplist_spec.rb +200 -0
  102. data/spec/lib/strokedb/data_structures/skiplist_spec.rb +253 -0
  103. data/spec/lib/strokedb/data_structures/spec_helper.rb +1 -0
  104. data/spec/lib/strokedb/document/associations_spec.rb +319 -0
  105. data/spec/lib/strokedb/document/callbacks_spec.rb +134 -0
  106. data/spec/lib/strokedb/document/coercions_spec.rb +110 -0
  107. data/spec/lib/strokedb/document/document_spec.rb +1063 -0
  108. data/spec/lib/strokedb/document/meta_meta_spec.rb +30 -0
  109. data/spec/lib/strokedb/document/meta_spec.rb +435 -0
  110. data/spec/lib/strokedb/document/metaslot_spec.rb +43 -0
  111. data/spec/lib/strokedb/document/slot_spec.rb +130 -0
  112. data/spec/lib/strokedb/document/spec_helper.rb +1 -0
  113. data/spec/lib/strokedb/document/validations_spec.rb +1081 -0
  114. data/spec/lib/strokedb/document/virtualize_spec.rb +80 -0
  115. data/spec/lib/strokedb/nsurl_spec.rb +73 -0
  116. data/spec/lib/strokedb/spec_helper.rb +1 -0
  117. data/spec/lib/strokedb/stores/chained_storages_spec.rb +116 -0
  118. data/spec/lib/strokedb/stores/spec_helper.rb +1 -0
  119. data/spec/lib/strokedb/stores/store_spec.rb +201 -0
  120. data/spec/lib/strokedb/stores/transaction_spec.rb +107 -0
  121. data/spec/lib/strokedb/sync/chain_sync_spec.rb +43 -0
  122. data/spec/lib/strokedb/sync/diff_spec.rb +111 -0
  123. data/spec/lib/strokedb/sync/lamport_timestamp_spec.rb +174 -0
  124. data/spec/lib/strokedb/sync/slot_diff_spec.rb +164 -0
  125. data/spec/lib/strokedb/sync/spec_helper.rb +1 -0
  126. data/spec/lib/strokedb/sync/store_sync_spec.rb +181 -0
  127. data/spec/lib/strokedb/sync/stroke_diff/array_spec.rb +97 -0
  128. data/spec/lib/strokedb/sync/stroke_diff/complex_spec.rb +58 -0
  129. data/spec/lib/strokedb/sync/stroke_diff/hash_spec.rb +144 -0
  130. data/spec/lib/strokedb/sync/stroke_diff/scalar_spec.rb +23 -0
  131. data/spec/lib/strokedb/sync/stroke_diff/spec_helper.rb +25 -0
  132. data/spec/lib/strokedb/sync/stroke_diff/string_spec.rb +61 -0
  133. data/spec/lib/strokedb/util/attach_dsl_spec.rb +45 -0
  134. data/spec/lib/strokedb/util/inflect_spec.rb +14 -0
  135. data/spec/lib/strokedb/util/lazy_array_spec.rb +157 -0
  136. data/spec/lib/strokedb/util/lazy_mapping_array_spec.rb +174 -0
  137. data/spec/lib/strokedb/util/lazy_mapping_hash_spec.rb +92 -0
  138. data/spec/lib/strokedb/util/spec_helper.rb +1 -0
  139. data/spec/lib/strokedb/util/uuid_spec.rb +46 -0
  140. data/spec/lib/strokedb/view_spec.rb +228 -0
  141. data/spec/lib/strokedb/volumes/archive_volume_spec.rb +105 -0
  142. data/spec/lib/strokedb/volumes/block_volume_spec.rb +100 -0
  143. data/spec/lib/strokedb/volumes/distributed_pointer_spec.rb +14 -0
  144. data/spec/lib/strokedb/volumes/fixed_length_skiplist_volume_spec.rb +177 -0
  145. data/spec/lib/strokedb/volumes/map_volume_spec.rb +172 -0
  146. data/spec/lib/strokedb/volumes/spec_helper.rb +1 -0
  147. data/spec/regression/docref_spec.rb +94 -0
  148. data/spec/regression/meta_spec.rb +23 -0
  149. data/spec/regression/spec_helper.rb +1 -0
  150. data/spec/regression/sync_spec.rb +36 -0
  151. data/spec/spec.opts +7 -0
  152. data/spec/spec_helper.rb +37 -0
  153. data/spec/temp/storages/TIMESTAMP +1 -0
  154. data/spec/temp/storages/UUID +1 -0
  155. data/spec/temp/storages/database-sync/TIMESTAMP +1 -0
  156. data/spec/temp/storages/database-sync/UUID +1 -0
  157. data/spec/temp/storages/database-sync/config +1 -0
  158. data/spec/temp/storages/database-sync/file/LAST +1 -0
  159. data/spec/temp/storages/database-sync/file/bd/f6/bdf675e5-8a7b-494e-97f2-f74a14ccd95d.av +0 -0
  160. data/spec/temp/storages/database-sync/file/uindex.wal +0 -0
  161. data/spec/temp/storages/database-sync/inverted_list_file/INVERTED_INDEX +1 -0
  162. data/spec/temp/storages/inverted_list_storage/INVERTED_INDEX +0 -0
  163. data/strokedb.gemspec +120 -0
  164. data/task/benchmark.task +9 -0
  165. data/task/ditz.task +30 -0
  166. data/task/echoe.rb +17 -0
  167. data/task/rcov.task +50 -0
  168. data/task/rdoc.task +10 -0
  169. data/task/rspec.task +0 -0
  170. data/vendor/java_inline.rb +106 -0
  171. data/vendor/rbmodexcl/mrimodexcl.rb +82 -0
  172. data/vendor/rbmodexcl/rbmodexcl.rb +5 -0
  173. data/vendor/rbmodexcl/rbxmodexcl.rb +48 -0
  174. data/vendor/rbmodexcl/spec/unextend_spec.rb +50 -0
  175. data/vendor/rbmodexcl/spec/uninclude_spec.rb +26 -0
  176. metadata +271 -79
  177. data/CONTRIBUTORS +0 -7
  178. data/CREDITS +0 -13
  179. data/bin/sdbc +0 -2
  180. data/lib/init.rb +0 -57
  181. data/lib/stores/inverted_list_index/inverted_list_index.rb +0 -49
  182. data/lib/stores/skiplist_store/chunk.rb +0 -119
  183. data/lib/stores/skiplist_store/chunk_storage.rb +0 -21
  184. data/lib/stores/skiplist_store/file_chunk_storage.rb +0 -44
  185. data/lib/stores/skiplist_store/memory_chunk_storage.rb +0 -37
  186. data/lib/stores/skiplist_store/skiplist_store.rb +0 -217
  187. data/lib/stores/store.rb +0 -5
  188. data/lib/sync/stroke_diff/stroke_diff.rb +0 -9
  189. data/lib/util/ext/object.rb +0 -8
  190. data/lib/util/java_util.rb +0 -9
  191. data/lib/util/trigger_partition.rb +0 -136
  192. data/strokedb.rb +0 -75
@@ -0,0 +1 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
@@ -0,0 +1,181 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "Store that syncs documents in" do
4
+
5
+ before(:each) do
6
+ FileUtils.rm_rf TEMP_STORAGES + '/store_sync'
7
+ FileUtils.rm_rf TEMP_STORAGES + '/store_sync_another'
8
+ StrokeDB::Config.build :default => true, :base_path => TEMP_STORAGES + '/store_sync'
9
+ @store = StrokeDB.default_store
10
+ another_cfg = StrokeDB::Config.build :base_path => TEMP_STORAGES + '/store_sync_another'
11
+ @another_store = another_cfg.stores[:default]
12
+ end
13
+
14
+ after(:each) do
15
+ @store.stop_autosync!
16
+ @another_store.stop_autosync!
17
+ FileUtils.rm_rf TEMP_STORAGES + '/store_sync'
18
+ FileUtils.rm_rf TEMP_STORAGES + '/store_sync_another'
19
+ end
20
+
21
+ it "should add document that does not yet exist" do
22
+ doc = Document.create!(@another_store, :hello => 'world')
23
+ doc.test = 'passed'
24
+ doc.save!
25
+ sync_rep = @store.sync!(doc.versions.all.reverse)
26
+ @store.find(doc.uuid,doc.previous_version).should == doc.versions.previous
27
+ @store.find(doc.uuid,doc.version).should == doc
28
+ @store.find(doc.uuid).should == doc
29
+ @store.find(doc.uuid).versions.all.should_not include(nil)
30
+ sync_rep.conflicts.should be_empty
31
+ sync_rep.fast_forwarded_documents.should be_empty
32
+ sync_rep.added_documents.should == [doc]
33
+ end
34
+
35
+ it "should fast-forward document if applicable" do
36
+ doc = Document.create!(@another_store, :hello => 'world')
37
+ doc.test = 'passed'
38
+ doc.save!
39
+ sync_rep = @store.sync!(doc.versions.all.reverse)
40
+ doc_at_store = @store.find(doc.uuid)
41
+ sync_rep.conflicts.should be_empty
42
+ sync_rep.added_documents.should == [doc_at_store]
43
+ sync_rep.fast_forwarded_documents.should be_empty
44
+ doc_at_store.ok = true
45
+ doc_at_store.save!
46
+ another_sync_rep = @another_store.sync!(doc_at_store.versions.all.reverse)
47
+ doc = @another_store.find(doc_at_store.uuid)
48
+ doc.should == doc_at_store
49
+ another_sync_rep.conflicts.should be_empty
50
+ another_sync_rep.added_documents.should be_empty
51
+ another_sync_rep.fast_forwarded_documents.should == [doc]
52
+ end
53
+
54
+ it "should create non-matching report if applicable" do
55
+ doc = Document.create!(@another_store, :hello => 'world')
56
+ doc.test = 'passed'
57
+ doc.save!
58
+ adoc = Document.create!(:uuid => doc.uuid, :world => 'hello')
59
+ sync_rep = @store.sync!(doc.versions.all.reverse)
60
+ sync_rep.conflicts.should be_empty
61
+ sync_rep.non_matching_documents.should == [adoc]
62
+ sync_rep.added_documents.should be_empty
63
+ sync_rep.fast_forwarded_documents.should be_empty
64
+ end
65
+
66
+ it "should do nothing if everything is up-to-date" do
67
+ doc = Document.create!(@another_store, :hello => 'world')
68
+ doc.test = 'passed'
69
+ doc.save!
70
+ sync_rep = @store.sync!(doc.versions.all.reverse)
71
+ sync_rep.conflicts.should be_empty
72
+ sync_rep.non_matching_documents.should be_empty
73
+ sync_rep.added_documents.should_not be_empty
74
+ sync_rep.fast_forwarded_documents.should be_empty
75
+ doc_at_store = @store.find(doc.uuid)
76
+ another_sync_rep = @another_store.sync!(doc_at_store.versions.all.reverse)
77
+ @another_store.find(doc_at_store.uuid).should == doc_at_store
78
+ another_sync_rep.conflicts.should be_empty
79
+ another_sync_rep.non_matching_documents.should be_empty
80
+ another_sync_rep.added_documents.should be_empty
81
+ another_sync_rep.fast_forwarded_documents.should be_empty
82
+ end
83
+
84
+ it "should create SynchronizationConflict if applicable" do
85
+ doc = Document.create!(@another_store, :hello => 'world')
86
+ doc.test = 'passed'
87
+ doc.save!
88
+ @store.sync!(doc.versions.all.reverse)
89
+ doc_at_store = @store.find(doc.uuid)
90
+ doc_at_store.ok = true
91
+ doc_at_store.save!
92
+ doc.ok = false
93
+ doc.save!
94
+ another_sync_rep = @another_store.sync!(doc_at_store.versions.all.reverse)
95
+ another_sync_rep.non_matching_documents.should be_empty
96
+ another_sync_rep.added_documents.should be_empty
97
+ another_sync_rep.fast_forwarded_documents.should be_empty
98
+ another_sync_rep.conflicts.should_not be_empty
99
+ conflict = another_sync_rep.conflicts.first
100
+ conflict.rev1[0].should == doc_at_store.version
101
+ conflict.rev2[0].should == doc.version
102
+ end
103
+
104
+ it "should try to resolve SynchronizationConflict if it was created" do
105
+ doc = Document.create!(@another_store, :hello => 'world')
106
+ doc.test = 'passed'
107
+ doc.save!
108
+ @store.sync!(doc.versions.all.reverse)
109
+ doc_at_store = @store.find(doc.uuid)
110
+ doc_at_store.ok = true
111
+ doc_at_store.save!
112
+ doc.ok = false
113
+ doc.save!
114
+ resolve_called = 0
115
+ SynchronizationConflict.module_eval do
116
+ define_method('resolve!') do
117
+ resolve_called += 1
118
+ end
119
+ end
120
+ another_sync_rep = @another_store.sync!(doc_at_store.versions.all.reverse)
121
+ resolve_called.should == 1
122
+ end
123
+
124
+ it "should add resolution meta to SynchronizationConflict document if it is specified in synchronized document" do
125
+ Object.send!(:remove_const,'SomeStrategy') if defined?(SomeStrategy)
126
+ SomeStrategy = Meta.new
127
+
128
+ Object.send!(:remove_const,'SomeMeta') if defined?(SomeMeta)
129
+ SomeMeta = Meta.new(:resolution_strategy => SomeStrategy)
130
+
131
+ # ensure that all metas exist in both stores
132
+ SomeStrategy.document
133
+ SomeMeta.document
134
+ @another_store.sync!(SomeStrategy.document.versions.all.reverse+SomeMeta.document.versions.all.reverse,@store.timestamp)
135
+
136
+ doc = SomeMeta.create!(@another_store, :hello => 'world')
137
+ doc.test = 'passed'
138
+ doc.save!
139
+ @store.sync!(doc.versions.all.reverse)
140
+ doc_at_store = @store.find(doc.uuid)
141
+ doc_at_store.ok = true
142
+ doc_at_store.save!
143
+ doc.ok = false
144
+ doc.save!
145
+
146
+ some_strategy_resolve = 0
147
+ SomeStrategy.module_eval do
148
+ define_method("resolve!") do
149
+ some_strategy_resolve += 1
150
+ end
151
+ end
152
+
153
+ another_sync_rep = @another_store.sync!(doc_at_store.versions.all.reverse)
154
+ conflict = another_sync_rep.conflicts.first
155
+ conflict.should be_a_kind_of(SomeStrategy)
156
+ some_strategy_resolve.should == 1
157
+ end
158
+
159
+ it "should store original timestamp in synchronization report" do
160
+ original_timestamp = @store.timestamp.counter
161
+ sync_rep = @store.sync!([])
162
+ sync_rep.timestamp.should == original_timestamp
163
+ end
164
+
165
+ it "should store store's document in synchronization report" do
166
+ sync_rep = @store.sync!([])
167
+ sync_rep.store_document.should == @store.document
168
+ end
169
+
170
+ it "should update timestamp prior to.sync! if it is specified" do
171
+ original_timestamp = @store.timestamp
172
+ @store.sync!([],100)
173
+ @store.timestamp.counter.should >= 100
174
+ @store.timestamp.should_not == original_timestamp
175
+ original_timestamp = @store.timestamp
176
+ @store.sync!([],LTS.new(200))
177
+ @store.timestamp.counter.should >= 200
178
+ @store.timestamp.should_not == original_timestamp
179
+ end
180
+
181
+ end
@@ -0,0 +1,97 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "Array diff" do
4
+
5
+ before(:each) do
6
+ @arr = [1, 2]
7
+ @obj = Object.new
8
+ end
9
+
10
+ it "should correctly patch array with array diff" do
11
+ 2000.times {
12
+ from = gen_str.split(//u)
13
+ to = gen_str.split(//u)
14
+ from.stroke_patch(from.stroke_diff(to)).should == to
15
+ }
16
+ end
17
+
18
+ [ nil,
19
+ 1, 2**64, -1.0,
20
+ :sym,
21
+ "string",
22
+ {:arr => 1},
23
+ @obj
24
+ ].each do |obj|
25
+ it "should correctly patch array with a replacement #{obj.inspect}" do
26
+ @arr.stroke_patch(@arr.stroke_diff(obj)).should == obj
27
+ end
28
+ end
29
+
30
+ it "should correctly patch array with objects" do
31
+ arr = (1..10).to_a.map{ Object.new }
32
+ a = [arr[0], arr[2], arr[3], arr[4], arr[8]]
33
+ b = [arr[1], arr[2], arr[3], arr[8], arr[9], arr[4], arr[1]]
34
+ a.stroke_patch(a.stroke_diff(b)).should == b
35
+ end
36
+
37
+ def gen_str
38
+ letters = %w(a b c d)
39
+ str = ""
40
+ len = rand(letters.size*2)
41
+ len.times {
42
+ str << letters[rand(letters.size)]
43
+ }
44
+ str
45
+ end
46
+ end
47
+
48
+ describe "Automerging arrays" do
49
+
50
+ before(:each) do
51
+ @base = [1, 2, 3]
52
+ end
53
+
54
+ it "should do a trivial one-side merge" do
55
+ a = [1, 2, 3]
56
+ b = [1, 2, 4, 3]
57
+ should_merge(@base, a, b, [1, 2, 4, 3])
58
+ end
59
+
60
+ it "should do a trivial two-side merge" do
61
+ a = [ 1, 2, 3, 4]
62
+ b = [0, 1, 2, 3 ]
63
+ should_merge(@base, a, b, [0, 1, 2, 3, 4])
64
+
65
+ a = [ 1, 2, 3, 4, 5]
66
+ b = [-1, 0, 1, 2, 3 ]
67
+ should_merge(@base, a, b, [-1, 0, 1, 2, 3, 4, 5])
68
+ end
69
+
70
+ it "should do a trivial merge with missing elements" do
71
+ a = [ 1, 2, 4]
72
+ b = [0, 1, 2, 3]
73
+ should_merge(@base, a, b, [0, 1, 2, 4])
74
+ end
75
+
76
+ # it "should merge same slot deletion" do
77
+ # should_merge({:a => 1, :b => 2}, {:b => 2}, {:b => 3}, {:b => 3})
78
+ # should_merge({:a => 1, :b => 2}, {:b => 2}, {:b => 2}, {:b => 2})
79
+ # end
80
+ #
81
+ # it "should merge when one of the objects is the same" do
82
+ # bases = [{:a => 1}, {:a => 1, :b => 2}, {:a => 1, :b => 2}]
83
+ # bs = [{:a => 3}, {:a => 1}, {:b => "22"}, nil, 123, Object.new, :symb, false, true]
84
+ #
85
+ # bases.each do |base|
86
+ # bs.each do |b|
87
+ # should_merge(base, base, b, b)
88
+ # end
89
+ # end
90
+ # end
91
+
92
+
93
+ end
94
+
95
+
96
+
97
+
@@ -0,0 +1,58 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "Complex diff" do
4
+ objs = [
5
+ [1,2,"str"]
6
+ ]
7
+ it "should patch scalar" do
8
+ 1000.times do
9
+ a = gen_complex_object
10
+ b = gen_complex_object
11
+ unless a.stroke_patch(a.stroke_diff(b)) == b
12
+ puts ""
13
+ puts "a = #{a.inspect}"
14
+ puts "b = #{b.inspect}"
15
+ puts "a.stroke_diff(b)"
16
+ puts "a.stroke_patch(a.stroke_diff(b))"
17
+ end
18
+ a.stroke_patch(a.stroke_diff(b)).should == b
19
+ end
20
+ end
21
+
22
+ def gen_complex_object
23
+ scalars = [1, :sym, false, true, nil, :hash, :array, :string]
24
+ s = scalars[rand(scalars.size)]
25
+ case s
26
+ when :hash
27
+ gen_hash
28
+ when :array
29
+ gen_array
30
+ when :string
31
+ gen_string
32
+ else
33
+ s
34
+ end
35
+ end
36
+
37
+ def gen_hash
38
+ keys = gen_string.split(//u)
39
+ keys.inject({}) do |h, k|
40
+ h[k] = gen_complex_object
41
+ h
42
+ end
43
+ end
44
+
45
+ def gen_array
46
+ len = rand(4*2)
47
+ (1..len).to_a.map{ gen_complex_object }
48
+ end
49
+
50
+ def gen_string
51
+ letters = %w(a b c d)
52
+ (1..rand(letters.size*2)).inject("") { |s, l|
53
+ s << letters[rand(letters.size)]
54
+ s
55
+ }
56
+ end
57
+
58
+ end
@@ -0,0 +1,144 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "Hash diff" do
4
+
5
+ before(:each) do
6
+ @hash = {:a => 1}
7
+ @obj = Object.new
8
+ end
9
+
10
+ it "should correctly patch hash with hash diff" do
11
+ 2000.times {
12
+ from = Hash[*(gen_str.split(//u)*2)]
13
+ to = Hash[*(gen_str.split(//u)*2)]
14
+ from.stroke_patch(from.stroke_diff(to)).should == to
15
+ }
16
+ end
17
+
18
+ [ nil,
19
+ 1, 2**64, -1.0,
20
+ :sym,
21
+ "string",
22
+ [:arr, 1],
23
+ @obj
24
+ ].each do |obj|
25
+ it "should correctly patch hash with a replacement #{obj.inspect}" do
26
+ @arr.stroke_patch(@arr.stroke_diff(obj)).should == obj
27
+ end
28
+ end
29
+
30
+ it "should correctly patch hash with objects" do
31
+ arr = (1..10).to_a.map{ Object.new }
32
+ a = Hash[1, arr[0], 2, arr[2], 3, arr[3], 4, arr[4], 5, arr[8]]
33
+ b = Hash[1, arr[1], 2, arr[2], 3, arr[3], 4, arr[8], 5, arr[9], 6, arr[4], 7, arr[1]]
34
+ a.stroke_patch(a.stroke_diff(b)).should == b
35
+ end
36
+
37
+ def gen_str
38
+ letters = %w(a b c d)
39
+ str = ""
40
+ len = rand(letters.size*2)
41
+ len.times {
42
+ str << letters[rand(letters.size)]
43
+ }
44
+ str
45
+ end
46
+ end
47
+
48
+ describe "Automerging hashes" do
49
+
50
+ before(:each) do
51
+ @base = {:a => 1, :b => 2, :c =>3}
52
+ end
53
+
54
+ it "should do a trivial merge" do
55
+ a = @base.merge :a => 2, :x => 1
56
+ b = @base.merge :b => 3, :y => 2
57
+ should_merge(@base, a, b, @base.merge(:a => 2, :x => 1, :b => 3, :y => 2))
58
+ end
59
+
60
+ it "should do a trivial merge with missing slots" do
61
+ b1 = @base.dup; b1.delete(:a)
62
+ b2 = @base.dup; b2.delete(:b)
63
+ a = b1.merge :x => 2
64
+ b = b2.merge :y => 3
65
+ should_merge(@base, a, b, {:c => @base[:c], :x => 2, :y => 3})
66
+ end
67
+
68
+ it "should merge intersecting, but identical old slots" do
69
+ a = @base.merge :a => 2, :c => 42
70
+ b = @base.merge :b => 3, :c => 42
71
+ should_merge(@base, a, b, @base.merge(:a => 2, :b => 3, :c => 42))
72
+ end
73
+
74
+ it "should merge intersecting, but identical new slots" do
75
+ a = @base.merge :a => 2, :x => 42
76
+ b = @base.merge :b => 3, :x => 42
77
+ should_merge(@base, a, b, @base.merge(:a => 2, :b => 3, :x => 42))
78
+ end
79
+
80
+ it "should merge deep diffed slots" do
81
+ base = {:s => { :a => 1 }}
82
+ a = {:s => { :a => 2 }}
83
+ b = {:s => { :a => 1, :b => 3 }}
84
+ should_merge(base, a, b, {:s => { :a => 2, :b => 3 }})
85
+ end
86
+
87
+ it "should merge same slot deletion" do
88
+ should_merge({:a => 1, :b => 2}, {:b => 2}, {:b => 3}, {:b => 3})
89
+ should_merge({:a => 1, :b => 2}, {:b => 2}, {:b => 2}, {:b => 2})
90
+ end
91
+
92
+ it "should merge when one of the objects is the same" do
93
+ bases = [{:a => 1}, {:a => 1, :b => 2}, {:a => 1, :b => 2}]
94
+ bs = [{:a => 3}, {:a => 1}, {:b => "22"}, nil, 123, Object.new, :symb, false, true]
95
+
96
+ bases.each do |base|
97
+ bs.each do |b|
98
+ should_merge(base, base, b, b)
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ describe "Merge conflicts in hashes" do
105
+
106
+ it "should yield a diff-diff conflict with partial merge" do
107
+ base = {:a => 1, :b => 2, :c =>3}
108
+ a = {:a => 111, :b => 222, :c =>3}
109
+ b = {:a => 222, :b => 2, :c =>333}
110
+ ra = {:a => 111, :b => 222, :c =>333}
111
+ rb = {:a => 222, :b => 222, :c =>333}
112
+ should_yield_conflict(base, a, b, ra, rb)
113
+ end
114
+
115
+ it "should yield a deletion-diff conflict with partial merge" do
116
+ base = {:a => 1, :b => 2, :c =>3}
117
+ a = { :b => 222, :c =>3}
118
+ b = {:a => 222, :b => 2, :c =>333}
119
+ ra = { :b => 222, :c =>333}
120
+ rb = {:a => 222, :b => 222, :c =>333}
121
+ should_yield_conflict(base, a, b, ra, rb)
122
+ end
123
+
124
+ it "should yield a insertion-insertion conflict with partial merge" do
125
+ base = { :b => 2, :c =>3}
126
+ a = {:a => 111, :b => 222, :c =>3}
127
+ b = {:a => 222, :b => 2, :c =>333}
128
+ ra = {:a => 111, :b => 222, :c =>333}
129
+ rb = {:a => 222, :b => 222, :c =>333}
130
+ should_yield_conflict(base, a, b, ra, rb)
131
+ end
132
+
133
+ it "should yield a deep diff-diff conflict with partial merge" do
134
+ base = {:s => { :a => 1, :b => 2, :c => 3 }, :del => 1 }
135
+ a = {:s => { :a => 2, :b => 22, :c => 3 }, :del => 1, :x => 1 }
136
+ b = {:s => { :a => 3, :b => 2, :c => 33 }}
137
+ ra = {:s => { :a => 2, :b => 22, :c => 33 }, :x => 1}
138
+ rb = {:s => { :a => 3, :b => 22, :c => 33 }, :x => 1}
139
+ should_yield_conflict(base, a, b, ra, rb)
140
+ end
141
+ end
142
+
143
+
144
+