flok 0.0.41 → 0.0.42

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