flok 0.0.41 → 0.0.42

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.
@@ -0,0 +1,140 @@
1
+ //Page Factor
2
+ //////////////////////////////////////////////////////////////////////////////////////////
3
+ function PageFactory(head, next) {
4
+ this.head = head;
5
+ this.next = next;
6
+ this.entries = [];
7
+ }
8
+
9
+ //Add an entry
10
+ PageFactory.prototype.addEntry = function(eid, value) {
11
+ this.entries.push({_id: eid, _sig: value, value: value});
12
+ }
13
+
14
+ //This adds up to four entrys that can be represented as a square:
15
+ //-------------
16
+ //| id0 | id1 |
17
+ //-------------
18
+ //| id2 | id3 |
19
+ //-------------
20
+ //Leaving out parts of the values array will not add those entries, e.g. ["Square", null, null, "Triangle"]
21
+ //--------------------|
22
+ //| Square | null |
23
+ //--------------------|
24
+ //| null | Triangle |
25
+ //--------------------|
26
+ //Where 'Square' is id0 and 'Triangle' is id3
27
+ PageFactory.prototype.addEntryFourSquare = function(values) {
28
+ if (values.length != 4) {
29
+ throw "FourSquare requires for values. Make values null if you don't need them"
30
+ }
31
+
32
+ for (var i = 0; i < values.length; ++i) {
33
+ if (values[i]) {
34
+ this.addEntry("id"+i, values[i]);
35
+ }
36
+ }
37
+ }
38
+
39
+ //Same as addEntryFourSquare but takes an index parameter before the
40
+ //value that sets the id of each element
41
+ //e.g. addEntryFourSquareCustomIds([["id0, "A"], ["id2, "B"], ["id1, "C"], ["id3, "D"]]).
42
+ //[
43
+ // {_id: "id0", value: "A", _sig: "A"},
44
+ // {_id: "id3", value: "D", _sig: "D"},
45
+ // {_id: "id2", value: "C", _sig: "C"},
46
+ // {_id: "id1", value: "B", _sig: "B"},
47
+ //]
48
+ PageFactory.prototype.addEntryFourSquareCustomIds = function(values) {
49
+ for (var i = 0; i < values.length; ++i) {
50
+ //Get pair
51
+ var pair = values[i];
52
+ if (pair.length != 2) {
53
+ throw "FourSquareShuffle accepts pairs. E.g. ['id0', 'A']"
54
+ }
55
+
56
+ var id = pair[0];
57
+ var value = pair[1];
58
+
59
+ this.addEntry(id, value);
60
+ }
61
+ }
62
+
63
+ //Returns a page
64
+ PageFactory.prototype.compile = function() {
65
+ var page = vm_create_page("default");
66
+ page._head = this.head;
67
+ page._next = this.next;
68
+
69
+ page.entries = this.entries;
70
+
71
+ vm_rehash_page(page);
72
+ vm_reindex_page(page);
73
+
74
+ return page;
75
+ }
76
+ //////////////////////////////////////////////////////////////////////////////////////////
77
+
78
+ var pf = new PageFactory();
79
+ pf.addEntryFourSquare(["Triangle", "Square", "Z", null]);
80
+ triangle_square_z_null = pf.compile();
81
+
82
+ var pf = new PageFactory();
83
+ pf.addEntryFourSquare(["Triangle", "Circle", null, "Q"]);
84
+ triangle_circle_null_q = pf.compile();
85
+
86
+ var pf = new PageFactory();
87
+ pf.addEntryFourSquare(["Triangle", "Circle", null, "Q"]);
88
+ triangle_circle_null_q = pf.compile();
89
+
90
+ var pf = new PageFactory();
91
+ pf.addEntryFourSquare(["Q", null, "Circle", "Square"]);
92
+ q_null_circle_square = pf.compile();
93
+
94
+ var pf = new PageFactory();
95
+ pf.addEntryFourSquare(["P", "Circle", null, "Q"]);
96
+ p_circle_null_q = pf.compile();
97
+
98
+ var pf = new PageFactory();
99
+ pf.addEntryFourSquare(["P", "Circle", null, null]);
100
+ p_circle_null_null = pf.compile();
101
+
102
+ var pf = new PageFactory();
103
+ pf.addEntryFourSquare(["P", null, null, "Q"]);
104
+ p_null_null_q = pf.compile();
105
+
106
+ var pf = new PageFactory();
107
+ pf.addEntryFourSquare(["P", "Square", null, null]);
108
+ p_square_null_null = pf.compile();
109
+
110
+ var pf = new PageFactory();
111
+ pf.addEntryFourSquare(["Triangle", null, "A", "M"]);
112
+ triangle_null_a_m = pf.compile();
113
+
114
+ var pf = new PageFactory();
115
+ pf.addEntryFourSquare(["Triangle", "Square", null, null]);
116
+ triangle_square_null_null = pf.compile();
117
+
118
+ var pf = new PageFactory();
119
+ pf.addEntryFourSquare(["Triangle", "Z", "Q", null]);
120
+ triangle_z_q_null = pf.compile();
121
+
122
+ var pf = new PageFactory(null);
123
+ head_null = pf.compile();
124
+
125
+ var pf = new PageFactory("world");
126
+ head_world = pf.compile();
127
+
128
+ var pf = new PageFactory(null);
129
+ next_null = pf.compile();
130
+
131
+ var pf = new PageFactory(null, "world");
132
+ next_world = pf.compile();
133
+
134
+ var pf = new PageFactory();
135
+ pf.addEntryFourSquareCustomIds([["id1", "Square"], ["id0", "Triangle"], ["id2", "Z"]]);
136
+ triangle_square_z_null_moved_square_triangle_z = pf.compile();
137
+
138
+ var pf = new PageFactory();
139
+ pf.addEntryFourSquareCustomIds([["id2", "Z"], ["id1", "Square"], ["id0", "Triangle"]]);
140
+ triangle_square_z_null_moved_z_square_triangle = pf.compile();
@@ -0,0 +1,150 @@
1
+ //Page Factor
2
+ //////////////////////////////////////////////////////////////////////////////////////////
3
+ function PageFactory(head, next) {
4
+ this.head = head;
5
+ this.next = next;
6
+ this.entries = [];
7
+ }
8
+
9
+ //Add an entry
10
+ PageFactory.prototype.addEntry = function(eid, value) {
11
+ this.entries.push({_id: eid, _sig: value, value: value});
12
+ }
13
+
14
+ //This adds up to four entrys that can be represented as a square:
15
+ //-------------
16
+ //| id0 | id1 |
17
+ //-------------
18
+ //| id2 | id3 |
19
+ //-------------
20
+ //Leaving out parts of the values array will not add those entries, e.g. ["Square", null, null, "Triangle"]
21
+ //--------------------|
22
+ //| Square | null |
23
+ //--------------------|
24
+ //| null | Triangle |
25
+ //--------------------|
26
+ //Where 'Square' is id0 and 'Triangle' is id3
27
+ PageFactory.prototype.addEntryFourSquare = function(values) {
28
+ if (values.length != 4) {
29
+ throw "FourSquare requires for values. Make values null if you don't need them"
30
+ }
31
+
32
+ for (var i = 0; i < values.length; ++i) {
33
+ if (values[i]) {
34
+ this.addEntry("id"+i, values[i]);
35
+ }
36
+ }
37
+ }
38
+
39
+ //Same as addEntryFourSquare but takes an index parameter before the
40
+ //value that sets the id of each element
41
+ //e.g. addEntryFourSquareCustomIds([["id0, "A"], ["id2, "B"], ["id1, "C"], ["id3, "D"]]).
42
+ //[
43
+ // {_id: "id0", value: "A", _sig: "A"},
44
+ // {_id: "id3", value: "D", _sig: "D"},
45
+ // {_id: "id2", value: "C", _sig: "C"},
46
+ // {_id: "id1", value: "B", _sig: "B"},
47
+ //]
48
+ PageFactory.prototype.addEntryFourSquareCustomIds = function(values) {
49
+ for (var i = 0; i < values.length; ++i) {
50
+ //Get pair
51
+ var pair = values[i];
52
+ if (pair.length != 2) {
53
+ throw "FourSquareShuffle accepts pairs. E.g. ['id0', 'A']"
54
+ }
55
+
56
+ var id = pair[0];
57
+ var value = pair[1];
58
+
59
+ this.addEntry(id, value);
60
+ }
61
+ }
62
+
63
+ //Returns a page
64
+ PageFactory.prototype.compile = function(page_id) {
65
+ if (page_id === undefined) {
66
+ var page = vm_create_page("default");
67
+ } else {
68
+ var page = vm_create_page(page_id);
69
+ }
70
+
71
+ page._head = this.head;
72
+ page._next = this.next;
73
+
74
+ page.entries = this.entries;
75
+
76
+ vm_rehash_page(page);
77
+ vm_reindex_page(page);
78
+
79
+ return page;
80
+ }
81
+ //////////////////////////////////////////////////////////////////////////////////////////
82
+
83
+ var pf = new PageFactory();
84
+ pf.addEntryFourSquare(["Triangle", "Square", "Z", null]);
85
+ triangle_square_z_null = pf.compile();
86
+
87
+ var pf = new PageFactory();
88
+ pf.addEntryFourSquare(["Triangle", "Circle", null, "Q"]);
89
+ triangle_circle_null_q = pf.compile();
90
+
91
+ var pf = new PageFactory();
92
+ pf.addEntryFourSquare(["Triangle", "Circle", null, "Q"]);
93
+ triangle_circle_null_q = pf.compile();
94
+
95
+ var pf = new PageFactory();
96
+ pf.addEntryFourSquare(["Q", null, "Circle", "Square"]);
97
+ q_null_circle_square = pf.compile();
98
+
99
+ var pf = new PageFactory();
100
+ pf.addEntryFourSquare(["P", "Circle", null, "Q"]);
101
+ p_circle_null_q = pf.compile();
102
+
103
+ var pf = new PageFactory();
104
+ pf.addEntryFourSquare(["P", "Circle", null, null]);
105
+ p_circle_null_null = pf.compile();
106
+
107
+ var pf = new PageFactory();
108
+ pf.addEntryFourSquare(["P", null, null, "Q"]);
109
+ p_null_null_q = pf.compile();
110
+
111
+ var pf = new PageFactory();
112
+ pf.addEntryFourSquare(["P", "Square", null, null]);
113
+ p_square_null_null = pf.compile();
114
+
115
+ var pf = new PageFactory();
116
+ pf.addEntryFourSquare(["Triangle", null, "A", "M"]);
117
+ triangle_null_a_m = pf.compile();
118
+
119
+ var pf = new PageFactory();
120
+ pf.addEntryFourSquare(["Triangle", "Square", null, null]);
121
+ triangle_square_null_null = pf.compile();
122
+
123
+ var pf = new PageFactory();
124
+ pf.addEntryFourSquare(["Triangle", "Z", "Q", null]);
125
+ triangle_z_q_null = pf.compile();
126
+
127
+ var pf = new PageFactory(null);
128
+ head_null = pf.compile();
129
+
130
+ var pf = new PageFactory("world");
131
+ head_world = pf.compile();
132
+
133
+ var pf = new PageFactory(null);
134
+ next_null = pf.compile();
135
+
136
+ var pf = new PageFactory(null, "world");
137
+ next_world = pf.compile();
138
+
139
+ var pf = new PageFactory();
140
+ pf.addEntryFourSquareCustomIds([["id1", "Square"], ["id0", "Triangle"], ["id2", "Z"]]);
141
+ triangle_square_z_null_moved_square_triangle_z = pf.compile();
142
+
143
+ var pf = new PageFactory();
144
+ pf.addEntryFourSquareCustomIds([["id2", "Z"], ["id1", "Square"], ["id0", "Triangle"]]);
145
+ triangle_square_z_null_moved_z_square_triangle = pf.compile();
146
+
147
+ //Seperate page
148
+ var pf = new PageFactory();
149
+ pf.addEntryFourSquare(["P", "Square", null, null]);
150
+ default2_square_null_null = pf.compile("default2");
@@ -596,6 +596,25 @@ RSpec.describe "kern:controller_spec" do
596
596
  @driver.mexpect("if_event", [base, "context", {"base" => base, "secret" => "foo"}])
597
597
  end
598
598
 
599
+ it "Does allow service macros in the global on_entry function" do
600
+ #Compile the controller
601
+ ctx = flok_new_user File.read('./spec/kern/assets/global_on_entry5.rb'), File.read("./spec/kern/assets/vm/config5.rb")
602
+
603
+ #Run the embed function
604
+ secret = SecureRandom.hex
605
+ ctx.eval %{
606
+ global_on_entry_called_count = 0;
607
+
608
+ //Call embed on main root view
609
+ base = _embed("my_controller", 0, {}, null);
610
+
611
+ int_dispatch([]);
612
+ }
613
+
614
+ expect(ctx.eval("read_res_called")).to eq(true)
615
+ end
616
+
617
+
599
618
  it "Does allow interval (every) events" do
600
619
  #Compile the controller
601
620
  ctx = flok_new_user File.read('./spec/kern/assets/interval.rb')
@@ -0,0 +1,1111 @@
1
+ #This contains tests for the 'functions' of the vm service system
2
+
3
+ Dir.chdir File.join File.dirname(__FILE__), '../../'
4
+ require './spec/env/kern.rb'
5
+ require './spec/lib/helpers.rb'
6
+ require './spec/lib/io_extensions.rb'
7
+ require './spec/lib/rspec_extensions.rb'
8
+ require 'zlib'
9
+
10
+ #Evaluates the
11
+ def eval_and_dump str
12
+
13
+ end
14
+
15
+ RSpec.describe "kern:vm_service_functional" do
16
+ include Zlib
17
+ include_context "kern"
18
+
19
+ it "Can can use vm_create_page" do
20
+ ctx = flok_new_user File.read('./spec/kern/assets/vm/controller0.rb'), File.read("./spec/kern/assets/vm/config5.rb")
21
+ dump = ctx.evald %{
22
+ dump.new_page = vm_create_page("my_id")
23
+ dump.new_anon_page = vm_create_page();
24
+ }
25
+
26
+ expect(dump["new_page"]).to eq({
27
+ "_head" => nil,
28
+ "_next" => nil,
29
+ "_id" => "my_id",
30
+ "entries" => [],
31
+ "__index" => {},
32
+ "_hash" => nil,
33
+ })
34
+
35
+ expect(dump["new_anon_page"]["_id"]).not_to eq nil
36
+ expect(dump["new_anon_page"]["entries"]).to eq []
37
+ end
38
+
39
+ it "Can can use vm_copy_page" do
40
+ ctx = flok_new_user File.read('./spec/kern/assets/vm/controller0.rb'), File.read("./spec/kern/assets/vm/config5.rb")
41
+ dump = ctx.evald %{
42
+ dump.new_page = vm_create_page("Q")
43
+ dump.no_head_no_next_no_entry = vm_copy_page(dump.new_page);
44
+
45
+ //Modify the new_page with a head, next, and entry; then create a copy
46
+ dump.new_page._head = "Z";
47
+ dump.new_page._next = "Triangle";
48
+ dump.new_page.entries.push({"_id": "id0", "_sig": "Square", "value": "Square"});
49
+ dump.head_z_next_triangle_entry_square = vm_copy_page(dump.new_page);
50
+
51
+ //Modify the new_page's entry in-place and make a copy
52
+ dump.new_page.entries[0]["_sig"] = "Circle";
53
+ dump.new_page.entries[0]["value"] = "Circle";
54
+ dump.head_z_next_triangle_entry_circle = vm_copy_page(dump.new_page);
55
+
56
+ //Modify the new_page's entry again in-place
57
+ dump.new_page.entries[0]["_sig"] = "Triangle"
58
+ dump.new_page.entries[0]["value"] = "Triangle"
59
+ }
60
+
61
+ expect(dump["no_head_no_next_no_entry"]).to eq({
62
+ "_head" => nil,
63
+ "_next" => nil,
64
+ "_id" => "Q",
65
+ "_hash" => nil,
66
+ "entries" => [],
67
+ })
68
+
69
+ expect(dump["head_z_next_triangle_entry_square"]).to eq({
70
+ "_head" => "Z",
71
+ "_next" => "Triangle",
72
+ "_id" => "Q",
73
+ "_hash" => nil,
74
+ "entries" => [
75
+ {"_id" => "id0", "_sig" => "Square", "value" => "Square"},
76
+ ],
77
+ })
78
+
79
+ expect(dump["head_z_next_triangle_entry_circle"]).to eq({
80
+ "_head" => "Z",
81
+ "_next" => "Triangle",
82
+ "_id" => "Q",
83
+ "_hash" => nil,
84
+ "entries" => [
85
+ {"_id" => "id0", "_sig" => "Circle", "value" => "Circle"},
86
+ ],
87
+ })
88
+
89
+ expect(dump["new_page"]).to eq({
90
+ "_head" => "Z",
91
+ "_next" => "Triangle",
92
+ "_id" => "Q",
93
+ "_hash" => nil,
94
+ "entries" => [
95
+ {"_id" => "id0", "_sig" => "Triangle", "value" => "Triangle"},
96
+ ],
97
+ "__index" => {}
98
+ })
99
+ end
100
+
101
+ #vm_rehash_page
102
+ ###########################################################################
103
+ it "vm_rehash_page can calculate the hash correctly" do
104
+ ctx = flok_new_user File.read('./spec/kern/assets/vm/controller0.rb'), File.read("./spec/kern/assets/vm/config3.rb")
105
+
106
+ #Run the check
107
+ res = ctx.eval %{
108
+ //Manually construct a page
109
+ var page = {
110
+ _head: null,
111
+ _next: null,
112
+ _id: "hello",
113
+ entries: [
114
+ {_id: "hello2", _sig: "nohteunth"},
115
+ ]
116
+ }
117
+
118
+ vm_rehash_page(page);
119
+ }
120
+
121
+ #Calculate hash ourselves
122
+ hash = crc32("hello")
123
+ hash = crc32("nohteunth", hash)
124
+ page = JSON.parse(ctx.eval("JSON.stringify(page)"))
125
+ page = JSON.parse(ctx.eval("JSON.stringify(page)"))
126
+
127
+ #Expect the same hash
128
+ expect(page).to eq({
129
+ "_head" => nil,
130
+ "_next" => nil,
131
+ "_id" => "hello",
132
+ "entries" => [
133
+ {"_id" => "hello2", "_sig" => "nohteunth"}
134
+ ],
135
+ "_hash" => hash.to_s
136
+ })
137
+ end
138
+
139
+ it "vm_rehash_page can calculate the hash correctly with head and next" do
140
+ ctx = flok_new_user File.read('./spec/kern/assets/vm/controller0.rb'), File.read("./spec/kern/assets/vm/config3.rb")
141
+
142
+ #Run the check
143
+ res = ctx.eval %{
144
+ //Manually construct a page
145
+ var page = {
146
+ _head: "a",
147
+ _next: "b",
148
+ _id: "hello",
149
+ entries: [
150
+ {_id: "hello2", _sig: "nohteunth"},
151
+ ]
152
+ }
153
+
154
+ vm_rehash_page(page);
155
+ }
156
+
157
+ #Calculate hash ourselves
158
+ hash = crc32("a")
159
+ hash = crc32("b", hash)
160
+ hash = crc32("hello", hash)
161
+ hash = crc32("nohteunth", hash)
162
+ page = JSON.parse(ctx.eval("JSON.stringify(page)"))
163
+
164
+ #Expect the same hash
165
+ expect(page).to eq({
166
+ "_head" => "a",
167
+ "_next" => "b",
168
+ "_id" => "hello",
169
+ "entries" => [
170
+ {"_id" => "hello2", "_sig" => "nohteunth"}
171
+ ],
172
+ "_hash" => hash.to_s
173
+ })
174
+ end
175
+ ###########################################################################
176
+
177
+ #vm_reindex_page
178
+ ###########################################################################
179
+ it "vm_reindex_page can calculate the __index correctly" do
180
+ ctx = flok_new_user File.read('./spec/kern/assets/vm/controller0.rb'), File.read("./spec/kern/assets/vm/config3.rb")
181
+
182
+ #Run the check
183
+ res = ctx.eval %{
184
+ //Manually construct a page
185
+ var page = {
186
+ _head: null,
187
+ _next: null,
188
+ _id: "hello",
189
+ entries: [
190
+ {_id: "hello2", _sig: "nohteunth"},
191
+ {_id: "hello3", _sig: "nohteunth2"},
192
+ ]
193
+ }
194
+
195
+ vm_reindex_page(page);
196
+ }
197
+
198
+ #Expect the same hash
199
+ page = ctx.dump("page")
200
+ expect(page.keys).to include("__index")
201
+ expect(page["__index"]).to eq({
202
+ "hello2" => 0,
203
+ "hello3" => 1
204
+ })
205
+ end
206
+ ###########################################################################
207
+
208
+ #vm_diff
209
+ ###########################################################################
210
+
211
+ #################################################################################################################
212
+ #Each vm_diff_entry is any array in the format of [type, *args] and each matcher is a hash
213
+ #in the format of {:type => "*", :args => [...]}. In order for a matcher to match a vm_diff_entry
214
+ #the matcher's :type must be equal to the vm_diff_entry's type, and the matcher's :args must be
215
+ #equal to the vm_diff_entry's *args. Equivalency for type is defined as absolutely equal. Equivalency
216
+ #for args is defined per element; in order for args to be equivalent, all elements of the args must
217
+ #be equivalent. Each element must be absolutely equal to be equivalent with the exception of argument
218
+ #elements where the matcher element is of type hash. In the hash case, equivalency is true if the vm_diff_entry
219
+ #sibling argument element is of type hash *and* the sibling element contains at-least all the key-value pairs
220
+ #in the forementioned matcher's arg element.
221
+ #e.g.
222
+ #################################################################################################################
223
+ #1. Match without a matcher's args element of type hash.
224
+ # ["foo", "bar", 3] <=> {:type => "foo", :args => ["bar", 3]}
225
+ #2. Match with a matcher's args element of type hash.
226
+ # ["foo", "bar", {"hello" => "world", "goodbye" => "world} <=> {:type => "foo", :args => [{"hello" => "world"}]}
227
+ #################################################################################################################
228
+ def verify_vm_diff vm_diff, matchers
229
+ winning_candidates = []
230
+
231
+ _vm_diff = JSON.parse(vm_diff.to_json)
232
+
233
+ _vm_diff.each do |vm_diff_entry|
234
+ #Get the vm_diff_entry in the format of [type, *args]
235
+ type = vm_diff_entry.shift
236
+ args = vm_diff_entry
237
+
238
+ #Find candidate matchers that have the same type as the vm_diff_entry
239
+ candidates = matchers.select{|e| e[:type] == type}
240
+ raise "verify_vm_diff failed. The given vm_diff contained a type, #{type.inspect} that was not even mentioned in the matchers. \nvm_diff = #{vm_diff.inspect}, \nmatchers = #{matchers.inspect}" unless candidates.length > 0
241
+
242
+ #Find a candidate that matches the args rules listed in the comments
243
+ winning_candidate = nil
244
+ candidates.each do |c|
245
+ catch(:candidate_failed) do
246
+ raise "verify_vm_diff failed. A given matcher with type, \n#{c[:type]}, \ncontained no :args array" unless c[:args]
247
+ next unless c[:args].length == args.length
248
+
249
+ c[:args].each_with_index do |a, i|
250
+ if a.class == Hash
251
+ throw :candidate_failed if args[i].class != Hash
252
+ a.each do |k, v|
253
+ throw :candidate_failed if args[i][k] != v
254
+ end
255
+ else
256
+ #Everything else is exactly equal to be equivalent
257
+ throw :candidate_failed if a != args[i]
258
+ end
259
+ end
260
+
261
+ winning_candidate = c
262
+ break
263
+ end
264
+ end
265
+
266
+ if winning_candidate
267
+ winning_candidates << winning_candidate
268
+ else
269
+ raise "verify_vm_diff failed. Could not find a candidate to match the vm_diff_entry of: #{[type, *args]} with the matchers of #{matchers.inspect}"
270
+ end
271
+ end
272
+
273
+ #Make sure all matchers were used
274
+ left_matchers = matchers - winning_candidates
275
+ raise "verify_vm_diff failed. Matchers did not all match a vm_diff_entry. \nRemaining matchers include \n#{left_matchers.inspect} \nand matched matchers include \n#{winning_candidates.inspect}\n for the vm_diff of\n#{vm_diff.inspect}" if left_matchers.length > 0
276
+ end
277
+
278
+ #Each vm_page["entries"] is an array, this helper function allows you to define a set
279
+ #of matchers which will check the entries array to verify that all matchers are equal to
280
+ #one unique element of the vm_page["entries"]. If all matchers are not exahusted, or all
281
+ #vm_page["entries"] do not have a paired matcher, then this verify function fails. Equivalency
282
+ #for entry matcher implies that all key-value pairs of the matcher are present in a vm_page["entries"] entry.
283
+ #e.g.
284
+ #############################################################################################################
285
+ #Matching entry & matcher pair
286
+ #entry = {"_id" => "my_id", "value" => 4}
287
+ #matcher = {"_id" => "my_id"} or {"value" => 4} or {"_id => "my_id", "value" => 4}
288
+ #############################################################################################################
289
+ def verify_vm_page_entries page, matchers
290
+ matching_matchers = []
291
+ page["entries"].each do |entry|
292
+ #Find matching matcher from matchers
293
+ matching_matcher = nil
294
+ matchers.each do |matcher|
295
+ catch(:matcher_does_not_match) do
296
+ #For all key value pairs in matcher
297
+ matcher.each do |k, v|
298
+ throw :matcher_does_not_match if entry[k] != v
299
+ end
300
+
301
+ matching_matcher = matcher
302
+ break
303
+ end
304
+ end
305
+
306
+ if matching_matcher
307
+ matching_matchers << matching_matcher
308
+ else
309
+ raise "verify_vm_page_entries failed: The entry: #{entry.inspect} had no matchers that would fit the bill, given matchers include: #{matchers.inspect}. The page was: #{page.inspect}"
310
+ end
311
+ end
312
+
313
+ left_matchers = matchers - matching_matchers
314
+ raise "verify_vm_page_entries failed: Matchers did not all match an entry. \nRemaining matchers include\n#{left_matchers.inspect}\n and matched matchers include \n#{matching_matchers.inspect}\n for the page of #{page.inspect}" if left_matchers.length > 0
315
+ end
316
+
317
+ #Same as verify_vm_page_entries, but the order of the matchers is taken into account
318
+ def verify_vm_page_entries_with_order page, matchers
319
+ page["entries"].each_with_index do |entry, i|
320
+ matcher = matchers[i]
321
+
322
+ #Matcher should match all k, v pairs
323
+ matcher.each do |k, v|
324
+ raise "Matcher #{matcher.inspect} did not match entry: #{entry.inspect}\n The order was taken into consideration for entries:\n#{page["entries"].inspect}\nWith Matchers\n#{matchers.inspect}" if entry[k] != v
325
+ end
326
+ end
327
+ end
328
+
329
+
330
+ #Reload the vm_diff_pages.js. Needed because vm_diff functions
331
+ #are often destructive and multiple tests need to have a fresh
332
+ #copy of the pages
333
+ def reload_vm_diff_pages(ctx)
334
+ pages_src = File.read("./spec/kern/assets/vm/vm_diff_pages.js")
335
+ ctx.eval pages_src
336
+ end
337
+
338
+ it "can use vm_diff" do
339
+ ctx = flok_new_user File.read('./spec/kern/assets/vm/controller22.rb'), File.read("./spec/kern/assets/vm/config5.rb")
340
+
341
+ #diff of vm_commit:0, 1
342
+ #| Triangle | Square | -> | Triangle | Circle |
343
+ #| K | | -> | | Q |
344
+ #from to
345
+ reload_vm_diff_pages(ctx)
346
+ dump = ctx.evald %{
347
+ var from = triangle_square_z_null;
348
+ var to = triangle_circle_null_q;
349
+ dump.diff = vm_diff(from, to)
350
+ vm_diff_replay(from, dump.diff);
351
+ dump.replay = from;
352
+ vm_rehash_page(dump.replay);
353
+ vm_reindex_page(dump.replay);
354
+ }
355
+ verify_vm_diff(dump["diff"], [
356
+ {type: "M", args: [{"_id" => "id1", "value" => "Circle"}]},
357
+ {type: "-", args: ["id2"]},
358
+ {type: "+", args: [2, {"_id" => "id3", "value" => "Q"}]}
359
+ ])
360
+ verify_vm_page_entries(dump["replay"], [
361
+ {"_id" => "id0", "value" => "Triangle"},
362
+ {"_id" => "id1", "value" => "Circle"},
363
+ {"_id" => "id3", "value" => "Q"},
364
+ ])
365
+
366
+ #vm_commit:2
367
+ #| Triangle | Square | -> | Q | |
368
+ #| K | | -> | Circle | Square |
369
+ #from to
370
+ reload_vm_diff_pages(ctx)
371
+ dump = ctx.evald %{
372
+ var from = triangle_square_z_null;
373
+ var to = q_null_circle_square;
374
+ dump.diff = vm_diff(from, to)
375
+ vm_diff_replay(from, dump.diff);
376
+ dump.replay = from;
377
+ vm_rehash_page(dump.replay);
378
+ vm_reindex_page(dump.replay);
379
+ }
380
+ verify_vm_diff(dump["diff"], [
381
+ {type: "M", args: [{"_id" => "id0", "value" => "Q"}]},
382
+ {type: "-", args: ["id1"]},
383
+ {type: "M", args: [{"_id" => "id2", "value" => "Circle"}]},
384
+ {type: "+", args: [2, {"_id" => "id3", "value" => "Square"}]}
385
+ ])
386
+ verify_vm_page_entries(dump["replay"], [
387
+ {"_id" => "id0", "value" => "Q"},
388
+ {"_id" => "id2", "value" => "Circle"},
389
+ {"_id" => "id3", "value" => "Square"},
390
+ ])
391
+
392
+ #vm_rebase:1 (diff only)
393
+ #| P | Circle | -> | P | Circle |
394
+ #| | Q | -> | | |
395
+ #from to
396
+ reload_vm_diff_pages(ctx)
397
+ dump = ctx.evald %{
398
+ var from = p_circle_null_q;
399
+ dump.diff = [
400
+ ["+", 0, {"_id": "id1", "_sig": "Square", "value": "Square"}],
401
+ ["M", {"_id": "id2", "_sig": "Z", "value": "Z"}],
402
+ ["-", "id3"],
403
+ ]
404
+ vm_diff_replay(from, dump.diff);
405
+ dump.replay = from;
406
+ vm_rehash_page(dump.replay);
407
+ vm_reindex_page(dump.replay);
408
+ }
409
+ verify_vm_page_entries(dump["replay"], [
410
+ {"_id" => "id0", "value" => "P"},
411
+ {"_id" => "id1", "value" => "Circle"},
412
+ ])
413
+
414
+ #vm_rebase:2a (diff only)
415
+ #| P | Circle | -> | P | |
416
+ #| | Q | -> | | Q |
417
+ #from to
418
+ reload_vm_diff_pages(ctx)
419
+ dump = ctx.evald %{
420
+ var from = p_circle_null_q;
421
+ dump.diff = [
422
+ ["-", "id1"],
423
+ ["M", {"_id": "id2", "_sig": "A", "value": "A"}],
424
+ ["+", 2, {"_id":"id3", "_sig": "M", "value": "M"}],
425
+ ]
426
+ vm_diff_replay(from, dump.diff);
427
+ dump.replay = from;
428
+ vm_rehash_page(dump.replay);
429
+ vm_reindex_page(dump.replay);
430
+ }
431
+ verify_vm_page_entries(dump["replay"], [
432
+ {"_id" => "id0", "value" => "P"},
433
+ {"_id" => "id3", "value" => "Q"},
434
+ ])
435
+
436
+ #vm_rebase:2b (diff only)
437
+ #| P | | -> | P | Square |
438
+ #| | Q | -> | | |
439
+ #from to
440
+ reload_vm_diff_pages(ctx)
441
+ dump = ctx.evald %{
442
+ var from = p_null_null_q;
443
+ dump.diff = [
444
+ ["+", 1, {"_id":"id1", "_sig": "Square", "value": "Square"}],
445
+ ["M", {"_id": "id2", "_sig": "Z", "value": "Z"}],
446
+ ["-", "id3"],
447
+ ]
448
+ vm_diff_replay(from, dump.diff);
449
+ dump.replay = from;
450
+ vm_rehash_page(dump.replay);
451
+ vm_reindex_page(dump.replay);
452
+ }
453
+ verify_vm_page_entries(dump["replay"], [
454
+ {"_id" => "id0", "value" => "P"},
455
+ {"_id" => "id1", "value" => "Square"},
456
+ ])
457
+
458
+ #vm_rebase:2c
459
+ #| P | | -> | P | Square |
460
+ #| | Q | -> | | |
461
+ #from to
462
+ reload_vm_diff_pages(ctx)
463
+ dump = ctx.evald %{
464
+ var from = p_null_null_q;
465
+ var to = p_square_null_null;
466
+ dump.diff = vm_diff(from, to)
467
+ vm_diff_replay(from, dump.diff);
468
+ dump.replay = from;
469
+ vm_rehash_page(dump.replay);
470
+ vm_reindex_page(dump.replay);
471
+ }
472
+ verify_vm_diff(dump["diff"], [
473
+ {type: "+", args: [1, {"_id" => "id1", "value" => "Square"}]},
474
+ {type: "-", args: ["id3"]},
475
+ ])
476
+ verify_vm_page_entries(dump["replay"], [
477
+ {"_id" => "id0", "value" => "P"},
478
+ {"_id" => "id1", "value" => "Square"},
479
+ ])
480
+
481
+ #vm_rebase:2d
482
+ #| Triangle | | -> | Triangle | Square |
483
+ #| A | M | -> | Z | |
484
+ #from to
485
+ reload_vm_diff_pages(ctx)
486
+ dump = ctx.evald %{
487
+ var from = triangle_null_a_m;
488
+ var to = triangle_square_z_null;
489
+ dump.diff = vm_diff(from, to)
490
+ vm_diff_replay(from, dump.diff);
491
+ dump.replay = from;
492
+ vm_rehash_page(dump.replay);
493
+ vm_reindex_page(dump.replay);
494
+ }
495
+ verify_vm_diff(dump["diff"], [
496
+ {type: "+", args: [1, {"_id" => "id1", "value" => "Square"}]},
497
+ {type: "M", args: [{"_id" => "id2", "value" => "Z"}]},
498
+ {type: "-", args: ["id3"]},
499
+ ])
500
+ verify_vm_page_entries(dump["replay"], [
501
+ {"_id" => "id0", "value" => "Triangle"},
502
+ {"_id" => "id1", "value" => "Square"},
503
+ {"_id" => "id2", "value" => "Z"},
504
+ ])
505
+
506
+ #vm_addendum:2a (diff only)
507
+ #| Triangle | Square | -> | Triangle | Z |
508
+ #| | | -> | Q | |
509
+ #from to
510
+ reload_vm_diff_pages(ctx)
511
+ dump = ctx.evald %{
512
+ var from = triangle_square_null_null;
513
+ dump.diff = [
514
+ ["M", {"_id": "id1", "_sig": "Z", "value": "Z"}],
515
+ ["+", 2, {"_id":"id2", "_sig": "Q", "value": "Q"}],
516
+ ["-", "id3"],
517
+ ]
518
+ vm_diff_replay(from, dump.diff);
519
+ dump.replay = from;
520
+ vm_rehash_page(dump.replay);
521
+ vm_reindex_page(dump.replay);
522
+ }
523
+ verify_vm_page_entries(dump["replay"], [
524
+ {"_id" => "id0", "value" => "Triangle"},
525
+ {"_id" => "id1", "value" => "Z"},
526
+ {"_id" => "id2", "value" => "Q"},
527
+ ])
528
+
529
+ #vm_addendum:2b (diff only)
530
+ #| Triangle | Square | -> | Triangle | Z |
531
+ #| | | -> | Q | |
532
+ #from to
533
+ reload_vm_diff_pages(ctx)
534
+ dump = ctx.evald %{
535
+ var from = triangle_square_null_null;
536
+ dump.diff = [
537
+ ["M", {"_id": "id1", "_sig": "Z", "value": "Z"}],
538
+ ["+", 2, {"_id":"id2", "_sig": "Q", "value": "Q"}],
539
+ ["-", "id3"],
540
+ ]
541
+ vm_diff_replay(from, dump.diff);
542
+ dump.replay = from;
543
+ vm_rehash_page(dump.replay);
544
+ vm_reindex_page(dump.replay);
545
+ }
546
+ verify_vm_page_entries(dump["replay"], [
547
+ {"_id" => "id0", "value" => "Triangle"},
548
+ {"_id" => "id1", "value" => "Z"},
549
+ {"_id" => "id2", "value" => "Q"},
550
+ ])
551
+
552
+ #vm_addendum:2c
553
+ #head:null head:world
554
+ #from to
555
+ reload_vm_diff_pages(ctx)
556
+ dump = ctx.evald %{
557
+ var from = head_null;
558
+ var to = head_world;
559
+ dump.diff = vm_diff(from, to)
560
+ vm_diff_replay(from, dump.diff);
561
+ dump.replay = from;
562
+ vm_rehash_page(dump.replay);
563
+ vm_reindex_page(dump.replay);
564
+ }
565
+ verify_vm_diff(dump["diff"], [
566
+ {type: "HEAD_M", args: ["world"]}
567
+ ])
568
+ expect(dump["replay"]["_head"]).to eq("world")
569
+
570
+ #vm_addendum:2d
571
+ #head:world head:null
572
+ #from to
573
+ reload_vm_diff_pages(ctx)
574
+ dump = ctx.evald %{
575
+ var from = head_world;
576
+ var to = head_null;
577
+ dump.diff = vm_diff(from, to)
578
+ vm_diff_replay(from, dump.diff);
579
+ dump.replay = from;
580
+ vm_rehash_page(dump.replay);
581
+ vm_reindex_page(dump.replay);
582
+ }
583
+ verify_vm_diff(dump["diff"], [
584
+ {type: "HEAD_M", args: [nil]}
585
+ ])
586
+ expect(dump["replay"]["_head"]).to eq(nil)
587
+
588
+ #vm_addendum:2e
589
+ #next:null next:world
590
+ #from to
591
+ reload_vm_diff_pages(ctx)
592
+ dump = ctx.evald %{
593
+ var from = next_null;
594
+ var to = next_world;
595
+ dump.diff = vm_diff(from, to)
596
+ vm_diff_replay(from, dump.diff);
597
+ dump.replay = from;
598
+ vm_rehash_page(dump.replay);
599
+ vm_reindex_page(dump.replay);
600
+ }
601
+ verify_vm_diff(dump["diff"], [
602
+ {type: "NEXT_M", args: ["world"]}
603
+ ])
604
+ expect(dump["replay"]["_next"]).to eq("world")
605
+
606
+ #vm_addendum:2f
607
+ #next:world next:null
608
+ #from to
609
+ reload_vm_diff_pages(ctx)
610
+ dump = ctx.evald %{
611
+ var from = next_world;
612
+ var to = next_null;
613
+ dump.diff = vm_diff(from, to)
614
+ vm_diff_replay(from, dump.diff);
615
+ dump.replay = from;
616
+ vm_rehash_page(dump.replay);
617
+ vm_reindex_page(dump.replay);
618
+ }
619
+ verify_vm_diff(dump["diff"], [
620
+ {type: "NEXT_M", args: [nil]}
621
+ ])
622
+ expect(dump["replay"]["_next"]).to eq(nil)
623
+
624
+ #continued... moved, XXXXXXX(N) is the new index
625
+ #| Triangle(0) | Square(1)| -> | Triangle(1)| Square(0)|
626
+ #| Z(2) | | -> | Z(2) | |
627
+ #from to
628
+ reload_vm_diff_pages(ctx)
629
+ dump = ctx.evald %{
630
+ var from = triangle_square_z_null;
631
+ var to = triangle_square_z_null_moved_square_triangle_z;
632
+ dump.diff = vm_diff(from, to)
633
+ vm_diff_replay(from, dump.diff);
634
+ dump.replay = from;
635
+ vm_rehash_page(dump.replay);
636
+ vm_reindex_page(dump.replay);
637
+ }
638
+ verify_vm_diff(dump["diff"], [
639
+ {type: ">", args: [1, "id0"]}
640
+ ])
641
+ verify_vm_page_entries_with_order(dump["replay"], [
642
+ {"_id" => "id1", "value" => "Square"},
643
+ {"_id" => "id0", "value" => "Triangle"},
644
+ {"_id" => "id2", "value" => "Z"},
645
+ ])
646
+
647
+ #moved(2)
648
+ #| Triangle(0) | Square(1)| -> | Triangle(2)| Square(1)|
649
+ #| Z(2) | | -> | Z(0) | |
650
+ #from to
651
+ reload_vm_diff_pages(ctx)
652
+ dump = ctx.evald %{
653
+ var from = triangle_square_z_null;
654
+ var to = triangle_square_z_null_moved_z_square_triangle;
655
+ dump.diff = vm_diff(from, to)
656
+ vm_diff_replay(from, dump.diff);
657
+ dump.replay = from;
658
+ vm_rehash_page(dump.replay);
659
+ vm_reindex_page(dump.replay);
660
+ }
661
+ verify_vm_diff(dump["diff"], [
662
+ {type: ">", args: [2, "id0"]},
663
+ {type: ">", args: [1, "id1"]}
664
+ ])
665
+ verify_vm_page_entries_with_order(dump["replay"], [
666
+ {"_id" => "id2", "value" => "Z"},
667
+ {"_id" => "id1", "value" => "Square"},
668
+ {"_id" => "id0", "value" => "Triangle"},
669
+ ])
670
+
671
+ #moved(3) Not taking a diff, presenting an illegal move diff
672
+ #to use on Q (Which dosen't exist)
673
+ #| Triangle(0) | Square(1)| -> | Triangle(2)| Square(1)|
674
+ #| Z(2) | | -> | Z(0) | |
675
+ #from to
676
+ reload_vm_diff_pages(ctx)
677
+ dump = ctx.evald %{
678
+ var from = triangle_square_z_null;
679
+ dump.diff = [
680
+ [">", 3, "id3"],
681
+ [">", 2, "id0"],
682
+ [">", 1, "id1"],
683
+ ]
684
+ vm_diff_replay(from, dump.diff);
685
+ dump.replay = from;
686
+ vm_rehash_page(dump.replay);
687
+ vm_reindex_page(dump.replay);
688
+ }
689
+ verify_vm_page_entries_with_order(dump["replay"], [
690
+ {"_id" => "id2", "value" => "Z"},
691
+ {"_id" => "id1", "value" => "Square"},
692
+ {"_id" => "id0", "value" => "Triangle"},
693
+ ])
694
+ end
695
+ ###########################################################################
696
+
697
+ #vm commit helpers
698
+ ###########################################################################
699
+ def reload_vm_commit_pages(ctx)
700
+ pages_src = File.read("./spec/kern/assets/vm/vm_commit_pages.js")
701
+ ctx.eval pages_src
702
+ end
703
+
704
+ it "can use vm_commit" do
705
+ ctx = flok_new_user File.read('./spec/kern/assets/vm/controller22.rb'), File.read("./spec/kern/assets/vm/config5.rb")
706
+
707
+ #vm_commit:0
708
+ #| Triangle | Circle | -> | Triangle | Square |
709
+ #| | Q | -> | Z | |
710
+ #newer older
711
+ reload_vm_commit_pages(ctx)
712
+ dump = ctx.evald %{
713
+ dump.newer = triangle_circle_null_q;
714
+ dump.older = triangle_square_z_null;
715
+ vm_commit(dump.older, dump.newer);
716
+ }
717
+
718
+ verify_vm_page_entries(dump["newer"], [
719
+ {"_id" => "id0", "value" => "Triangle"},
720
+ {"_id" => "id1", "value" => "Circle"},
721
+ {"_id" => "id3", "value" => "Q"},
722
+ ])
723
+
724
+ #Changes match
725
+ verify_vm_diff(dump["newer"]["__changes"], [
726
+ {type: "M", args: [{"_id" => "id1", "value" => "Circle"}]},
727
+ {type: "-", args: ["id2"]},
728
+ {type: "+", args: [2, {"_id" => "id3", "value" => "Q"}]},
729
+ ])
730
+
731
+ #No base but does include changes
732
+ expect(dump["newer"]["__changes_id"]).not_to eq(nil)
733
+ expect(dump["newer"]["__base"]).to eq(nil)
734
+
735
+ #vm_commit:1 older[nobase, changes]
736
+ #| Triangle | Circle | -> | Triangle | Square | --__changes-- | ----- |
737
+ #| | Q | -> | Z | | | |x| | Add (+) |
738
+ #newer older | ----- Triangle |
739
+ # | | | | |
740
+ # | ----- |
741
+ # | ----- |
742
+ # | | | | Modify (M) |
743
+ # | ----- Z |
744
+ # | |x| | |
745
+ # | ----- |
746
+ # | ----- |
747
+ # | | | | Remove (-) |
748
+ # | ----- |
749
+ # | | |x| |
750
+ # | ----- |
751
+ reload_vm_commit_pages(ctx)
752
+ dump = ctx.evald %{
753
+ dump.newer = triangle_circle_null_q;
754
+ dump.older = triangle_square_z_null;
755
+ dump.older.__changes_id = "XXXXX";
756
+ dump.older.__changes = [
757
+ ["+", 0, {"_id": "id0", "_sig": "Triangle", "value": "Triangle"}],
758
+ ["M", {"_id": "id2", "_sig": "Z", "value": "Z"}],
759
+ ["-", "id3"],
760
+
761
+ ]
762
+ vm_commit(dump.older, dump.newer);
763
+ }
764
+
765
+ verify_vm_page_entries(dump["newer"], [
766
+ {"_id" => "id0", "value" => "Triangle"},
767
+ {"_id" => "id1", "value" => "Circle"},
768
+ {"_id" => "id3", "value" => "Q"},
769
+ ])
770
+
771
+ #Changes match
772
+ verify_vm_diff(dump["newer"]["__changes"], [
773
+ {type: "M", args: [{"_id" => "id1", "value" => "Circle"}]},
774
+ {type: "-", args: ["id2"]},
775
+ {type: "+", args: [2, {"_id" => "id3", "value" => "Q"}]},
776
+ ])
777
+ expect(dump["newer"]["__changes_id"]).not_to eq(nil)
778
+
779
+ #Base with changes
780
+ verify_vm_page_entries(dump["newer"]["__base"], [
781
+ {"_id" => "id0", "value" => "Triangle"},
782
+ {"_id" => "id1", "value" => "Square"},
783
+ {"_id" => "id2", "value" => "Z"},
784
+ ])
785
+ verify_vm_diff(dump["newer"]["__base"]["__changes"], [
786
+ {type: "+", args: [0, {"_id" => "id0", "value" => "Triangle"}]},
787
+ {type: "-", args: ["id3"]},
788
+ {type: "M", args: [{"_id" => "id2", "value" => "Z"}]},
789
+ ])
790
+ expect(dump["newer"]["__base"]["__changes_id"]).not_to eq(nil)
791
+ expect(dump["newer"]["__base"]["__base"]).to eq(nil)
792
+
793
+ #vm_commit:2 older[base[nobase, changes], changes]
794
+ #| Q | | -> | Triangle | Circle | --__changes-- | ----- |
795
+ #| Circle | Square | -> | | Q | | | |x| Modify (M) |
796
+ #newer ----------------------- | ----- Circle |
797
+ # | __base | | | | | |
798
+ # ----------------------- | ----- |
799
+ # | Triangle | Square | | ----- |
800
+ # | Z | | --__changes- | | | | Remove (-) |
801
+ # older | | ----- |
802
+ # | | |x| | |
803
+ # | | ----- |
804
+ # | | ----- |
805
+ # | | | | | Insert (+) |
806
+ # | | ----- Q |
807
+ # | | | |x| |
808
+ # | | ----- |
809
+ # | --------------------
810
+ # |
811
+ # |- | ----- |
812
+ # | |x| | Add (+) |
813
+ # | ----- Triangle |
814
+ # | | | | |
815
+ # | ----- |
816
+ # | ----- |
817
+ # | | | | Modify (M) |
818
+ # | ----- Z |
819
+ # | |x| | |
820
+ # | ----- |
821
+ # | ----- |
822
+ # | | | | Remove (-) |
823
+ # | ----- |
824
+ # | | |x| |
825
+ # | ----- |
826
+ reload_vm_commit_pages(ctx)
827
+ dump = ctx.evald %{
828
+ dump.newer = q_null_circle_square;
829
+ dump.older = triangle_circle_null_q;
830
+ dump.older.__changes_id = "XXXXX";
831
+ dump.older.__changes = [
832
+ ["M", {"_id": "id0", "_sig": "Circle", "value": "Circle"}],
833
+ ["-", "id2"],
834
+ ["+", 2, {"_id": "id3", "_sig": "Q", "value": "Q"}],
835
+ ]
836
+
837
+ //Also, base on older
838
+ dump.older.__base = triangle_square_z_null;
839
+ dump.older.__base.__changes = [
840
+ ["+", 0, {"_id": "id0", "_sig": "Triangle", "value": "Triangle"}],
841
+ ["M", {"_id": "id2", "_sig": "Z", "value": "Z"}],
842
+ ["-", "id3"],
843
+ ]
844
+ dump.older.__base.__changes_id = "YYYYYYY";
845
+ vm_commit(dump.older, dump.newer);
846
+ }
847
+
848
+ verify_vm_page_entries(dump["newer"], [
849
+ {"_id" => "id0", "value" => "Q"},
850
+ {"_id" => "id2", "value" => "Circle"},
851
+ {"_id" => "id3", "value" => "Square"},
852
+ ])
853
+
854
+ #Changes match
855
+ verify_vm_diff(dump["newer"]["__changes"], [
856
+ {type: "M", args: [{"_id" => "id0", "value" => "Q"}]},
857
+ {type: "-", args: ["id1"]},
858
+ {type: "M", args: [{"_id" => "id2", "value" => "Circle"}]},
859
+ {type: "+", args: [2, {"_id" => "id3", "value" => "Square"}]},
860
+ ])
861
+ expect(dump["newer"]["__changes_id"]).not_to eq(nil)
862
+
863
+ #Base with changes
864
+ verify_vm_page_entries(dump["newer"]["__base"], [
865
+ {"_id" => "id0", "value" => "Triangle"},
866
+ {"_id" => "id1", "value" => "Square"},
867
+ {"_id" => "id2", "value" => "Z"},
868
+ ])
869
+ verify_vm_diff(dump["newer"]["__base"]["__changes"], [
870
+ {type: "+", args: [0, {"_id" => "id0", "value" => "Triangle"}]},
871
+ {type: "M", args: [{"_id" => "id2", "value" => "Z"}]},
872
+ {type: "-", args: ["id3"]},
873
+ ])
874
+ expect(dump["newer"]["__base"]["__changes_id"]).not_to eq(nil)
875
+ expect(dump["newer"]["__base"]["__base"]).to eq(nil)
876
+ end
877
+
878
+ it "can use vm_rebase" do
879
+ ctx = flok_new_user File.read('./spec/kern/assets/vm/controller22.rb'), File.read("./spec/kern/assets/vm/config5.rb")
880
+
881
+ #vm_rebase:0 newer[nobase, nochange]
882
+ #| Triangle | Circle | -> | Triangle | Square |
883
+ #| | Q | -> | Z | |
884
+ #older newer
885
+ reload_vm_commit_pages(ctx)
886
+ dump = ctx.evald %{
887
+ dump.older = triangle_circle_null_q;
888
+ dump.newer = triangle_square_z_null;
889
+ vm_rebase(dump.newer, dump.older);
890
+ }
891
+
892
+ verify_vm_page_entries(dump["older"], [
893
+ {"_id" => "id0", "value" => "Triangle"},
894
+ {"_id" => "id1", "value" => "Circle"},
895
+ {"_id" => "id3", "value" => "Q"},
896
+ ])
897
+
898
+ #No base & No changes
899
+ expect(dump["older"]["__changes"]).to eq(nil)
900
+ expect(dump["older"]["__changes_id"]).to eq(nil)
901
+ expect(dump["older"]["__base"]).to eq(nil)
902
+
903
+ #vm_rebase:1 newer[nobase, changes]
904
+ #| P | Circle | -> | Triangle | Square | --__changes-- | ----- |
905
+ #| | Q | -> | Z | | | | |x| Add (+) |
906
+ #older newer | ----- Square |
907
+ # | | | | |
908
+ # | ----- |
909
+ # | ----- |
910
+ # | | | | Modify (M) |
911
+ # | ----- Z |
912
+ # | |x| | |
913
+ # | ----- |
914
+ # | ----- |
915
+ # | | | | Remove (-) |
916
+ # | ----- |
917
+ # | | |x| |
918
+ # | ----- |
919
+ reload_vm_commit_pages(ctx)
920
+ dump = ctx.evald %{
921
+ dump.older = p_circle_null_q;
922
+ dump.newer = triangle_square_z_null;
923
+ dump.newer.__changes = [
924
+ ["+", 0, {"_id": "id1", "_sig": "Square", "value": "Square"}],
925
+ ["M", {"_id": "id2", "_sig": "Z", "value": "Z"}],
926
+ ["-", "id3"],
927
+ ]
928
+ dump.newer.__changes_id = "XXXXXXXXXXX";
929
+ vm_rebase(dump.newer, dump.older);
930
+ }
931
+
932
+ verify_vm_page_entries(dump["older"], [
933
+ {"_id" => "id0", "value" => "P"},
934
+ {"_id" => "id1", "value" => "Circle"},
935
+ ])
936
+
937
+ #Changes match
938
+ verify_vm_diff(dump["older"]["__changes"], [
939
+ {type: "+", args: [0, {"_id" => "id1", "value" => "Square"}]},
940
+ {type: "M", args: [{"_id" => "id2", "value" => "Z"}]},
941
+ {type: "-", args: ["id3"]},
942
+ ])
943
+ expect(dump["older"]["__changes_id"]).not_to eq(nil)
944
+
945
+ #No base
946
+ expect(dump["older"]["__base"]).to eq(nil)
947
+
948
+ #vm_rebase:2 newer[base[nobase, changes], changes]
949
+ #| P | Circle | -> | Triangle | Square | --__changes-- | ----- |
950
+ #| | Q | -> | K | | | | |x| Add (+) |
951
+ #older ----------------------- | ----- Square |
952
+ # | __base | | | | | |
953
+ # ----------------------- | ----- |
954
+ # | Triangle | | | ----- |
955
+ # | A | M | --__changes- | | | | Modify (M) |
956
+ # newer | | ----- Z |
957
+ # | | |x| | |
958
+ # | | ----- |
959
+ # | | ----- |
960
+ # | | | | | Remove (-) |
961
+ # | | ----- |
962
+ # | | | |x| |
963
+ # | | ----- |
964
+ # | --------------------
965
+ # |
966
+ # |- | ----- |
967
+ # | | |x| Remove (-) |
968
+ # | ----- |
969
+ # | | | | |
970
+ # | ----- |
971
+ # | ----- |
972
+ # | | | | Modify (M) |
973
+ # | ----- A |
974
+ # | |x| | |
975
+ # | ----- |
976
+ # | ----- |
977
+ # | | | | Add (+) |
978
+ # | ----- M |
979
+ # | | |x| |
980
+ # | ----- |
981
+ reload_vm_commit_pages(ctx)
982
+ dump = ctx.evald %{
983
+ dump.older = p_circle_null_q;
984
+ dump.newer = triangle_square_z_null;
985
+ dump.newer.__changes_id = "XXXXX";
986
+ dump.newer.__changes = [
987
+ ["+", 0, {"_id": "id1", "_sig": "Square", "value": "Square"}],
988
+ ["M", {"_id": "id2", "_sig": "Z", "value": "Z"}],
989
+ ["-", "id3"],
990
+ ]
991
+
992
+ //Also, base on older
993
+ dump.newer.__base = triangle_null_a_m;
994
+ dump.newer.__base.__changes = [
995
+ ["-", "id1"],
996
+ ["M", {"_id": "id2", "_sig": "A", "value": "A"}],
997
+ ["+", 2, {"_id": "id3", "_sig": "+", "value": "M"}],
998
+ ]
999
+ dump.newer.__base.__changes_id = "YYYYYYY";
1000
+ vm_rebase(dump.newer, dump.older);
1001
+ }
1002
+
1003
+ verify_vm_page_entries(dump["older"], [
1004
+ {"_id" => "id0", "value" => "P"},
1005
+ {"_id" => "id1", "value" => "Square"},
1006
+ ])
1007
+
1008
+ #Changes match
1009
+ verify_vm_diff(dump["older"]["__changes"], [
1010
+ {type: "+", args: [0, {"_id" => "id1", "value" => "Square"}]},
1011
+ {type: "-", args: ["id3"]},
1012
+ ])
1013
+ expect(dump["older"]["__changes_id"]).not_to eq(nil)
1014
+ expect(dump["older"]["__changes_id"]).not_to eq("XXXXX")
1015
+
1016
+ #Base with changes
1017
+ verify_vm_page_entries(dump["older"]["__base"], [
1018
+ {"_id" => "id0", "value" => "P"},
1019
+ {"_id" => "id3", "value" => "Q"},
1020
+ ])
1021
+ verify_vm_diff(dump["older"]["__base"]["__changes"], [
1022
+ {type: "-", args: ["id1"]},
1023
+ {type: "M", args: [{"_id" => "id2", "value" => "A"}]},
1024
+ {type: "+", args: [2, {"_id" => "id3", "value" => "M"}]},
1025
+ ])
1026
+ expect(dump["older"]["__base"]["__changes_id"]).to eq("YYYYYYY")
1027
+ expect(dump["older"]["__base"]["__base"]).to eq(nil)
1028
+ end
1029
+
1030
+ it "can use vm_mark_changes_synced" do
1031
+ ctx = flok_new_user File.read('./spec/kern/assets/vm/controller22.rb'), File.read("./spec/kern/assets/vm/config5.rb")
1032
+
1033
+ #Case A
1034
+ #Page with no changes. (Nothing happends)
1035
+ dump = ctx.evald %{
1036
+ dump.page = vm_create_page();
1037
+ vm_mark_changes_synced(dump.page, "changes_id");
1038
+
1039
+ dump.__changes_id_is_undefined = (dump.page.__changes_id === undefined)
1040
+ dump.__changes_is_undefined = (dump.page.__changes === undefined)
1041
+ }
1042
+ expect(dump["__changes_id_is_undefined"]).to eq(true)
1043
+ expect(dump["__changes_is_undefined"]).to eq(true)
1044
+
1045
+ #Case B
1046
+ #Page with changes, but changes_id given to vm_mark_changes_synced does not match (Nothing happends).
1047
+ dump = ctx.evald %{
1048
+ dump.page = vm_create_page();
1049
+ dump.page.__changes_id = "foo";
1050
+ dump.page.__changes = ["A"]
1051
+ vm_mark_changes_synced(dump.page, "bar");
1052
+ }
1053
+ expect(dump["page"]["__changes"]).to eq(["A"])
1054
+ expect(dump["page"]["__changes_id"]).to eq("foo")
1055
+
1056
+ #Case C
1057
+ #Page with changes but no base, and changes_id given to vm_mark_changes_synced does match __changes_id of page.
1058
+ #The __changes and __changes_id of the page will be removed.
1059
+ dump = ctx.evald %{
1060
+ dump.page = vm_create_page();
1061
+ dump.page.__changes_id = "foo";
1062
+ dump.page.__changes = ["A"]
1063
+ vm_mark_changes_synced(dump.page, "foo");
1064
+
1065
+ dump.__changes_id_is_undefined = (dump.page.__changes_id === undefined)
1066
+ dump.__changes_is_undefined = (dump.page.__changes === undefined)
1067
+ }
1068
+ expect(dump["__changes_id_is_undefined"]).to eq(true)
1069
+ expect(dump["__changes_is_undefined"]).to eq(true)
1070
+
1071
+ #Case D
1072
+ #Page with changes and a base[changes, nobase], and changes_id given to vm_mark_changes_synced does not match __base.__changes_id of page.
1073
+ #Nothing happends
1074
+ dump = ctx.evald %{
1075
+ dump.page = vm_create_page();
1076
+ dump.page.__changes_id = "foo";
1077
+ dump.page.__changes = ["A"]
1078
+
1079
+ //Attach base [unbased, changes]
1080
+ dump.page.__base = vm_create_page();
1081
+ dump.page.__base.__changes_id = "bar";
1082
+ dump.page.__base.__changes = ["B"]
1083
+
1084
+ vm_mark_changes_synced(dump.page, "foo");
1085
+ }
1086
+ expect(dump["page"]["__changes"]).to eq(["A"])
1087
+ expect(dump["page"]["__changes_id"]).to eq("foo")
1088
+
1089
+ #Case E
1090
+ #Page with changes and a base[changes, nobase], and changes_id given to vm_mark_changes_synced does match __base.__changes_id of page.
1091
+ #The base will be removed, but the page's __changes and __changes_id will remain.
1092
+ dump = ctx.evald %{
1093
+ dump.page = vm_create_page();
1094
+ dump.page.__changes_id = "foo";
1095
+ dump.page.__changes = ["A"]
1096
+
1097
+ //Attach base [unbased, changes]
1098
+ dump.page.__base = vm_create_page();
1099
+ dump.page.__base.__changes_id = "bar";
1100
+ dump.page.__base.__changes = ["B"]
1101
+
1102
+ vm_mark_changes_synced(dump.page, "bar");
1103
+
1104
+ dump.__base_is_undefined = (dump.page.__base === undefined)
1105
+ }
1106
+ expect(dump["__base_is_undefined"]).to eq(true)
1107
+ expect(dump["page"]["__changes"]).to eq(["A"])
1108
+ expect(dump["page"]["__changes_id"]).to eq("foo")
1109
+ end
1110
+ ###########################################################################
1111
+ end