blob: f1c42e0e559564a16396b47e6c01f5efe7178305 [file] [log] [blame]
Himabindu Puchad964ef02015-06-30 01:10:47 -07001// Copyright 2015 The Vanadium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package vsync
6
7import (
8 "fmt"
9 "math/rand"
10 "reflect"
11 "testing"
12 "time"
13
Himabindu Puchad964ef02015-06-30 01:10:47 -070014 "v.io/v23/naming"
15 "v.io/v23/rpc"
16 "v.io/v23/security"
Raja Daoudd4543072015-06-30 11:15:55 -070017 _ "v.io/x/ref/runtime/factories/generic"
Adam Sadovskyf2efeb52015-08-31 14:17:49 -070018 "v.io/x/ref/services/syncbase/server/interfaces"
19 "v.io/x/ref/services/syncbase/server/watchable"
Himabindu Puchad964ef02015-06-30 01:10:47 -070020)
21
22// TestDiffPrefixGenVectors tests diffing prefix gen vectors.
23func TestDiffPrefixGenVectors(t *testing.T) {
24 svc := createService(t)
25 defer destroyService(t, svc)
26 s := svc.sync
27 s.id = 10 //responder. Initiator is id 11.
28
29 tests := []struct {
30 respPVec, initPVec interfaces.PrefixGenVector
31 genDiffIn genRangeVector
32 genDiffWant genRangeVector
33 }{
34 { // responder and initiator are at identical vectors.
35 respPVec: interfaces.PrefixGenVector{10: 1, 11: 10, 12: 20, 13: 2},
36 initPVec: interfaces.PrefixGenVector{10: 1, 11: 10, 12: 20, 13: 2},
37 genDiffIn: make(genRangeVector),
38 },
39 { // responder and initiator are at identical vectors.
40 respPVec: interfaces.PrefixGenVector{10: 0},
41 initPVec: interfaces.PrefixGenVector{10: 0},
42 genDiffIn: make(genRangeVector),
43 },
44 { // responder has no updates.
45 respPVec: interfaces.PrefixGenVector{10: 0},
46 initPVec: interfaces.PrefixGenVector{10: 5, 11: 10, 12: 20, 13: 8},
47 genDiffIn: make(genRangeVector),
48 },
49 { // responder and initiator have no updates.
50 respPVec: interfaces.PrefixGenVector{10: 0},
51 initPVec: interfaces.PrefixGenVector{11: 0},
52 genDiffIn: make(genRangeVector),
53 },
54 { // responder is staler than initiator.
55 respPVec: interfaces.PrefixGenVector{10: 1, 11: 10, 12: 20, 13: 2},
56 initPVec: interfaces.PrefixGenVector{10: 1, 11: 10, 12: 20, 13: 8, 14: 5},
57 genDiffIn: make(genRangeVector),
58 },
59 { // responder is more up-to-date than initiator for local updates.
60 respPVec: interfaces.PrefixGenVector{10: 5, 11: 10, 12: 20, 13: 2},
61 initPVec: interfaces.PrefixGenVector{10: 1, 11: 10, 12: 20, 13: 2},
62 genDiffIn: make(genRangeVector),
63 genDiffWant: genRangeVector{10: &genRange{min: 2, max: 5}},
64 },
65 { // responder is fresher than initiator for local updates and one device.
66 respPVec: interfaces.PrefixGenVector{10: 5, 11: 10, 12: 22, 13: 2},
67 initPVec: interfaces.PrefixGenVector{10: 1, 11: 10, 12: 20, 13: 2, 14: 40},
68 genDiffIn: make(genRangeVector),
69 genDiffWant: genRangeVector{
70 10: &genRange{min: 2, max: 5},
71 12: &genRange{min: 21, max: 22},
72 },
73 },
74 { // responder is fresher than initiator in all but one device.
75 respPVec: interfaces.PrefixGenVector{10: 1, 11: 2, 12: 3, 13: 4},
76 initPVec: interfaces.PrefixGenVector{10: 0, 11: 2, 12: 0},
77 genDiffIn: make(genRangeVector),
78 genDiffWant: genRangeVector{
79 10: &genRange{min: 1, max: 1},
80 12: &genRange{min: 1, max: 3},
81 13: &genRange{min: 1, max: 4},
82 },
83 },
84 { // initiator has no updates.
85 respPVec: interfaces.PrefixGenVector{10: 1, 11: 2, 12: 3, 13: 4},
86 initPVec: interfaces.PrefixGenVector{},
87 genDiffIn: make(genRangeVector),
88 genDiffWant: genRangeVector{
89 10: &genRange{min: 1, max: 1},
90 11: &genRange{min: 1, max: 2},
91 12: &genRange{min: 1, max: 3},
92 13: &genRange{min: 1, max: 4},
93 },
94 },
95 { // initiator has no updates, pre-existing diff.
96 respPVec: interfaces.PrefixGenVector{10: 1, 11: 2, 12: 3, 13: 4},
97 initPVec: interfaces.PrefixGenVector{13: 1},
98 genDiffIn: genRangeVector{
99 10: &genRange{min: 5, max: 20},
100 13: &genRange{min: 1, max: 3},
101 },
102 genDiffWant: genRangeVector{
103 10: &genRange{min: 1, max: 20},
104 11: &genRange{min: 1, max: 2},
105 12: &genRange{min: 1, max: 3},
106 13: &genRange{min: 1, max: 4},
107 },
108 },
109 }
110
111 for _, test := range tests {
112 want := test.genDiffWant
113 got := test.genDiffIn
Himabindu Puchab41fc142015-09-10 17:10:57 -0700114 rSt := newResponderState(nil, nil, s, interfaces.DeltaReqData{}, "fakeInitiator")
Himabindu Puchad964ef02015-06-30 01:10:47 -0700115 rSt.diff = got
116 rSt.diffPrefixGenVectors(test.respPVec, test.initPVec)
117 checkEqualDevRanges(t, got, want)
118 }
119}
120
121// TestSendDeltas tests the computation of the delta bound (computeDeltaBound)
122// and if the log records on the wire are correctly ordered (phases 2 and 3 of
123// SendDeltas).
Himabindu Puchab41fc142015-09-10 17:10:57 -0700124func TestSendDataDeltas(t *testing.T) {
Himabindu Puchad964ef02015-06-30 01:10:47 -0700125 appName := "mockapp"
126 dbName := "mockdb"
127
128 tests := []struct {
129 respVec, initVec, outVec interfaces.GenVector
130 respGen uint64
131 genDiff genRangeVector
132 keyPfxs []string
133 }{
134 { // Identical prefixes, local and remote updates.
135 respVec: interfaces.GenVector{
136 "foo": interfaces.PrefixGenVector{12: 8},
137 "foobar": interfaces.PrefixGenVector{12: 10},
138 },
139 initVec: interfaces.GenVector{
140 "foo": interfaces.PrefixGenVector{11: 5},
141 "foobar": interfaces.PrefixGenVector{11: 5},
142 },
143 respGen: 5,
144 outVec: interfaces.GenVector{
145 "foo": interfaces.PrefixGenVector{10: 5, 12: 8},
146 "foobar": interfaces.PrefixGenVector{10: 5, 12: 10},
147 },
148 genDiff: genRangeVector{
149 10: &genRange{min: 1, max: 5},
150 12: &genRange{min: 1, max: 10},
151 },
152 keyPfxs: []string{"baz", "wombat", "f", "foo", "foobar", ""},
153 },
154 { // Identical prefixes, local and remote updates.
155 respVec: interfaces.GenVector{
156 "bar": interfaces.PrefixGenVector{12: 20},
157 "foo": interfaces.PrefixGenVector{12: 8},
158 "foobar": interfaces.PrefixGenVector{12: 10},
159 },
160 initVec: interfaces.GenVector{
161 "foo": interfaces.PrefixGenVector{11: 5},
162 "foobar": interfaces.PrefixGenVector{11: 5, 12: 10},
163 "bar": interfaces.PrefixGenVector{10: 5, 11: 5, 12: 5},
164 },
165 respGen: 5,
166 outVec: interfaces.GenVector{
167 "foo": interfaces.PrefixGenVector{10: 5, 12: 8},
168 "foobar": interfaces.PrefixGenVector{10: 5, 12: 10},
169 "bar": interfaces.PrefixGenVector{10: 5, 12: 20},
170 },
171 genDiff: genRangeVector{
172 10: &genRange{min: 1, max: 5},
173 12: &genRange{min: 1, max: 20},
174 },
175 keyPfxs: []string{"baz", "wombat", "f", "foo", "foobar", "bar", "barbaz", ""},
176 },
177 { // Non-identical prefixes, local only updates.
178 initVec: interfaces.GenVector{
179 "foo": interfaces.PrefixGenVector{11: 5},
180 "foobar": interfaces.PrefixGenVector{11: 5},
181 },
182 respGen: 5,
183 outVec: interfaces.GenVector{
184 "foo": interfaces.PrefixGenVector{10: 5},
185 },
186 genDiff: genRangeVector{
187 10: &genRange{min: 1, max: 5},
188 },
189 keyPfxs: []string{"baz", "wombat", "f", "foo", "foobar", "", "fo", "fooxyz"},
190 },
191 { // Non-identical prefixes, local and remote updates.
192 respVec: interfaces.GenVector{
193 "f": interfaces.PrefixGenVector{12: 5, 13: 5},
194 "foo": interfaces.PrefixGenVector{12: 10, 13: 10},
195 "foobar": interfaces.PrefixGenVector{12: 20, 13: 20},
196 },
197 initVec: interfaces.GenVector{
198 "foo": interfaces.PrefixGenVector{11: 5, 12: 1},
199 },
200 respGen: 5,
201 outVec: interfaces.GenVector{
202 "foo": interfaces.PrefixGenVector{10: 5, 12: 10, 13: 10},
203 "foobar": interfaces.PrefixGenVector{10: 5, 12: 20, 13: 20},
204 },
205 genDiff: genRangeVector{
206 10: &genRange{min: 1, max: 5},
207 12: &genRange{min: 2, max: 20},
208 13: &genRange{min: 1, max: 20},
209 },
210 keyPfxs: []string{"baz", "wombat", "f", "foo", "foobar", "", "fo", "fooxyz"},
211 },
212 { // Non-identical prefixes, local and remote updates.
213 respVec: interfaces.GenVector{
214 "foobar": interfaces.PrefixGenVector{12: 20, 13: 20},
215 },
216 initVec: interfaces.GenVector{
217 "foo": interfaces.PrefixGenVector{11: 5, 12: 1},
218 },
219 respGen: 5,
220 outVec: interfaces.GenVector{
221 "foo": interfaces.PrefixGenVector{10: 5},
222 "foobar": interfaces.PrefixGenVector{10: 5, 12: 20, 13: 20},
223 },
224 genDiff: genRangeVector{
225 10: &genRange{min: 1, max: 5},
226 12: &genRange{min: 2, max: 20},
227 13: &genRange{min: 1, max: 20},
228 },
229 keyPfxs: []string{"baz", "wombat", "f", "foo", "foobar", "", "fo", "fooxyz"},
230 },
231 { // Non-identical prefixes, local and remote updates.
232 respVec: interfaces.GenVector{
233 "f": interfaces.PrefixGenVector{12: 20, 13: 20},
234 },
235 initVec: interfaces.GenVector{
236 "foo": interfaces.PrefixGenVector{11: 5, 12: 1},
237 },
238 respGen: 5,
239 outVec: interfaces.GenVector{
240 "foo": interfaces.PrefixGenVector{10: 5, 12: 20, 13: 20},
241 },
242 genDiff: genRangeVector{
243 10: &genRange{min: 1, max: 5},
244 12: &genRange{min: 2, max: 20},
245 13: &genRange{min: 1, max: 20},
246 },
247 keyPfxs: []string{"baz", "wombat", "f", "foo", "foobar", "", "fo", "fooxyz"},
248 },
249 { // Non-identical interleaving prefixes.
250 respVec: interfaces.GenVector{
251 "f": interfaces.PrefixGenVector{12: 20, 13: 10},
252 "foo": interfaces.PrefixGenVector{12: 30, 13: 20},
253 "foobar": interfaces.PrefixGenVector{12: 40, 13: 30},
254 },
255 initVec: interfaces.GenVector{
256 "fo": interfaces.PrefixGenVector{11: 5, 12: 1},
257 "foob": interfaces.PrefixGenVector{11: 5, 12: 10},
258 "foobarxyz": interfaces.PrefixGenVector{11: 5, 12: 20},
259 },
260 respGen: 5,
261 outVec: interfaces.GenVector{
262 "fo": interfaces.PrefixGenVector{10: 5, 12: 20, 13: 10},
263 "foo": interfaces.PrefixGenVector{10: 5, 12: 30, 13: 20},
264 "foobar": interfaces.PrefixGenVector{10: 5, 12: 40, 13: 30},
265 },
266 genDiff: genRangeVector{
267 10: &genRange{min: 1, max: 5},
268 12: &genRange{min: 2, max: 40},
269 13: &genRange{min: 1, max: 30},
270 },
271 keyPfxs: []string{"baz", "wombat", "f", "foo", "foobar", "", "fo", "foob", "foobarxyz", "fooxyz"},
272 },
273 { // Non-identical interleaving prefixes.
274 respVec: interfaces.GenVector{
275 "fo": interfaces.PrefixGenVector{12: 20, 13: 10},
276 "foob": interfaces.PrefixGenVector{12: 30, 13: 20},
277 "foobarxyz": interfaces.PrefixGenVector{12: 40, 13: 30},
278 },
279 initVec: interfaces.GenVector{
280 "f": interfaces.PrefixGenVector{11: 5, 12: 1},
281 "foo": interfaces.PrefixGenVector{11: 5, 12: 10},
282 "foobar": interfaces.PrefixGenVector{11: 5, 12: 20},
283 },
284 respGen: 5,
285 outVec: interfaces.GenVector{
286 "f": interfaces.PrefixGenVector{10: 5},
287 "fo": interfaces.PrefixGenVector{10: 5, 12: 20, 13: 10},
288 "foob": interfaces.PrefixGenVector{10: 5, 12: 30, 13: 20},
289 "foobarxyz": interfaces.PrefixGenVector{10: 5, 12: 40, 13: 30},
290 },
291 genDiff: genRangeVector{
292 10: &genRange{min: 1, max: 5},
293 12: &genRange{min: 2, max: 40},
294 13: &genRange{min: 1, max: 30},
295 },
296 keyPfxs: []string{"baz", "wombat", "f", "foo", "foobar", "", "fo", "foob", "foobarxyz", "fooxyz"},
297 },
298 { // Non-identical sibling prefixes.
299 respVec: interfaces.GenVector{
300 "foo": interfaces.PrefixGenVector{12: 20, 13: 10},
301 "foobarabc": interfaces.PrefixGenVector{12: 40, 13: 30},
302 "foobarxyz": interfaces.PrefixGenVector{12: 30, 13: 20},
303 },
304 initVec: interfaces.GenVector{
305 "foo": interfaces.PrefixGenVector{11: 5, 12: 1},
306 },
307 respGen: 5,
308 outVec: interfaces.GenVector{
309 "foo": interfaces.PrefixGenVector{10: 5, 12: 20, 13: 10},
310 "foobarabc": interfaces.PrefixGenVector{10: 5, 12: 40, 13: 30},
311 "foobarxyz": interfaces.PrefixGenVector{10: 5, 12: 30, 13: 20},
312 },
313 genDiff: genRangeVector{
314 10: &genRange{min: 1, max: 5},
315 12: &genRange{min: 2, max: 40},
316 13: &genRange{min: 1, max: 30},
317 },
318 keyPfxs: []string{"baz", "wombat", "f", "foo", "foobar", "", "foobarabc", "foobarxyz", "foobar123", "fooxyz"},
319 },
320 { // Non-identical prefixes, local and remote updates.
321 respVec: interfaces.GenVector{
322 "barbaz": interfaces.PrefixGenVector{12: 18},
323 "f": interfaces.PrefixGenVector{12: 30, 13: 5},
324 "foobar": interfaces.PrefixGenVector{12: 30, 13: 8},
325 },
326 initVec: interfaces.GenVector{
327 "foo": interfaces.PrefixGenVector{11: 5, 12: 5},
328 "foobar": interfaces.PrefixGenVector{11: 5, 12: 5},
329 "bar": interfaces.PrefixGenVector{10: 5, 11: 5, 12: 5},
330 },
331 respGen: 5,
332 outVec: interfaces.GenVector{
333 "foo": interfaces.PrefixGenVector{10: 5, 12: 30, 13: 5},
334 "foobar": interfaces.PrefixGenVector{10: 5, 12: 30, 13: 8},
335 "bar": interfaces.PrefixGenVector{10: 5},
336 "barbaz": interfaces.PrefixGenVector{10: 5, 12: 18},
337 },
338 genDiff: genRangeVector{
339 10: &genRange{min: 1, max: 5},
340 12: &genRange{min: 6, max: 30},
341 13: &genRange{min: 1, max: 8},
342 },
343 keyPfxs: []string{"baz", "wombat", "f", "foo", "foobar", "bar", "barbaz", ""},
344 },
345 }
346
347 for i, test := range tests {
348 svc := createService(t)
349 s := svc.sync
350 s.id = 10 //responder.
351
352 wantDiff, wantVec := test.genDiff, test.outVec
Himabindu Puchab41fc142015-09-10 17:10:57 -0700353 s.syncState[appDbName(appName, dbName)] = &dbSyncStateInMem{
354 data: &localGenInfoInMem{
355 gen: test.respGen,
356 checkptGen: test.respGen,
357 },
358 genvec: test.respVec,
Himabindu Puchad964ef02015-06-30 01:10:47 -0700359 }
Himabindu Puchad964ef02015-06-30 01:10:47 -0700360
361 ////////////////////////////////////////
362 // Test sending deltas.
363
364 // Insert some log records to bootstrap testing below.
365 tRng := rand.New(rand.NewSource(int64(i)))
366 var wantRecs []*localLogRec
367 st := svc.St()
368 tx := st.NewTransaction()
369 objKeyPfxs := test.keyPfxs
370 j := 0
371 for id, r := range wantDiff {
372 pos := uint64(tRng.Intn(50) + 100*j)
373 for k := r.min; k <= r.max; k++ {
374 opfx := objKeyPfxs[tRng.Intn(len(objKeyPfxs))]
375 // Create holes in the log records.
376 if opfx == "" {
377 continue
378 }
Raja Daoud7cb71792015-07-08 12:00:33 -0700379 okey := makeRowKey(fmt.Sprintf("%s~%x", opfx, tRng.Int()))
Himabindu Puchad964ef02015-06-30 01:10:47 -0700380 vers := fmt.Sprintf("%x", tRng.Int())
381 rec := &localLogRec{
382 Metadata: interfaces.LogRecMetadata{Id: id, Gen: k, ObjId: okey, CurVers: vers, UpdTime: time.Now().UTC()},
383 Pos: pos + k,
384 }
Himabindu Puchab41fc142015-09-10 17:10:57 -0700385 if err := putLogRec(nil, tx, logDataPrefix, rec); err != nil {
Himabindu Puchad964ef02015-06-30 01:10:47 -0700386 t.Fatalf("putLogRec(%d:%d) failed rec %v err %v", id, k, rec, err)
387 }
Raja Daoud7cb71792015-07-08 12:00:33 -0700388 value := fmt.Sprintf("value_%s", okey)
389 if err := watchable.PutAtVersion(nil, tx, []byte(okey), []byte(value), []byte(vers)); err != nil {
390 t.Fatalf("PutAtVersion(%d:%d) failed rec %v value %s: err %v", id, k, rec, value, err)
391 }
Himabindu Puchad964ef02015-06-30 01:10:47 -0700392
393 initPfxs := extractAndSortPrefixes(test.initVec)
394 if !filterLogRec(rec, test.initVec, initPfxs) {
395 wantRecs = append(wantRecs, rec)
396 }
397 }
398 j++
399 }
400 if err := tx.Commit(); err != nil {
401 t.Fatalf("cannot commit putting log rec, err %v", err)
402 }
403
Himabindu Puchab41fc142015-09-10 17:10:57 -0700404 req := interfaces.DataDeltaReq{
405 AppName: appName,
406 DbName: dbName,
407 InitVec: test.initVec,
408 }
409
410 rSt := newResponderState(nil, nil, s, interfaces.DeltaReqData{req}, "fakeInitiator")
Himabindu Puchad964ef02015-06-30 01:10:47 -0700411 d := &dummyResponder{}
412 rSt.call = d
Himabindu Puchab41fc142015-09-10 17:10:57 -0700413 var err error
414 rSt.st, err = rSt.sync.getDbStore(nil, nil, rSt.appName, rSt.dbName)
Himabindu Puchad964ef02015-06-30 01:10:47 -0700415 if err != nil {
Himabindu Puchab41fc142015-09-10 17:10:57 -0700416 t.Fatalf("getDbStore failed to get store handle for app/db %v %v", rSt.appName, rSt.dbName)
Himabindu Puchad964ef02015-06-30 01:10:47 -0700417 }
Himabindu Puchab41fc142015-09-10 17:10:57 -0700418
419 err = rSt.computeDataDeltas(nil)
420 if err != nil || !reflect.DeepEqual(rSt.outVec, wantVec) {
421 t.Fatalf("computeDataDeltas failed (I: %v), (R: %v, %v), got %v, want %v err %v", test.initVec, test.respGen, test.respVec, rSt.outVec, wantVec, err)
422 }
423 checkEqualDevRanges(t, rSt.diff, wantDiff)
424
425 if err = rSt.sendDataDeltas(nil); err != nil {
426 t.Fatalf("sendDataDeltas failed, err %v", err)
427 }
428
Himabindu Puchad964ef02015-06-30 01:10:47 -0700429 d.diffLogRecs(t, wantRecs, wantVec)
430
431 destroyService(t, svc)
432 }
433}
434
435//////////////////////////////
436// Helpers
437
438type dummyResponder struct {
Himabindu Puchab41fc142015-09-10 17:10:57 -0700439 gotRecs []*localLogRec
440 outVec interfaces.GenVector
Himabindu Puchad964ef02015-06-30 01:10:47 -0700441}
442
Himabindu Puchad964ef02015-06-30 01:10:47 -0700443func (d *dummyResponder) SendStream() interface {
444 Send(item interfaces.DeltaResp) error
445} {
446 return d
447}
448
449func (d *dummyResponder) Send(item interfaces.DeltaResp) error {
450 switch v := item.(type) {
Himabindu Puchad964ef02015-06-30 01:10:47 -0700451 case interfaces.DeltaRespRespVec:
452 d.outVec = v.Value
453 case interfaces.DeltaRespRec:
454 d.gotRecs = append(d.gotRecs, &localLogRec{Metadata: v.Value.Metadata})
455 }
456 return nil
457}
458
459func (d *dummyResponder) Security() security.Call {
460 return nil
461}
462
463func (d *dummyResponder) Suffix() string {
464 return ""
465}
466
467func (d *dummyResponder) LocalEndpoint() naming.Endpoint {
468 return nil
469}
470
471func (d *dummyResponder) RemoteEndpoint() naming.Endpoint {
472 return nil
473}
474
475func (d *dummyResponder) GrantedBlessings() security.Blessings {
476 return security.Blessings{}
477}
478
Matt Rosencrantz98d6d7c2015-09-04 12:34:08 -0700479func (d *dummyResponder) Server() rpc.Server {
Himabindu Puchad964ef02015-06-30 01:10:47 -0700480 return nil
481}
482
483func (d *dummyResponder) diffLogRecs(t *testing.T, wantRecs []*localLogRec, wantVec interfaces.GenVector) {
Himabindu Puchad964ef02015-06-30 01:10:47 -0700484 if len(d.gotRecs) != len(wantRecs) {
485 t.Fatalf("diffLogRecs failed, gotLen %v, wantLen %v\n", len(d.gotRecs), len(wantRecs))
486 }
487 for i, rec := range d.gotRecs {
488 if !reflect.DeepEqual(rec.Metadata, wantRecs[i].Metadata) {
489 t.Fatalf("diffLogRecs failed, i %v, got %v, want %v\n", i, rec.Metadata, wantRecs[i].Metadata)
490 }
491 }
492 if !reflect.DeepEqual(d.outVec, wantVec) {
493 t.Fatalf("diffLogRecs failed genvector, got %v, want %v\n", d.outVec, wantVec)
494 }
495}
496
497func checkEqualDevRanges(t *testing.T, s1, s2 genRangeVector) {
498 if len(s1) != len(s2) {
499 t.Fatalf("len(s1): %v != len(s2): %v", len(s1), len(s2))
500 }
501 for d1, r1 := range s1 {
502 if r2, ok := s2[d1]; !ok || !reflect.DeepEqual(r1, r2) {
503 t.Fatalf("Dev %v: r1 %v != r2 %v", d1, r1, r2)
504 }
505 }
506}