@fluidframework/merge-tree 2.31.0 → 2.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/client.d.ts +7 -1
  3. package/dist/client.d.ts.map +1 -1
  4. package/dist/client.js +153 -44
  5. package/dist/client.js.map +1 -1
  6. package/dist/index.d.ts +1 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +3 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/mergeTree.d.ts +17 -5
  11. package/dist/mergeTree.d.ts.map +1 -1
  12. package/dist/mergeTree.js +188 -79
  13. package/dist/mergeTree.js.map +1 -1
  14. package/dist/mergeTreeNodes.d.ts +16 -18
  15. package/dist/mergeTreeNodes.d.ts.map +1 -1
  16. package/dist/mergeTreeNodes.js +6 -0
  17. package/dist/mergeTreeNodes.js.map +1 -1
  18. package/dist/perspective.d.ts +9 -0
  19. package/dist/perspective.d.ts.map +1 -1
  20. package/dist/perspective.js +14 -1
  21. package/dist/perspective.js.map +1 -1
  22. package/dist/segmentInfos.d.ts +32 -4
  23. package/dist/segmentInfos.d.ts.map +1 -1
  24. package/dist/segmentInfos.js +3 -1
  25. package/dist/segmentInfos.js.map +1 -1
  26. package/dist/sortedSegmentSet.d.ts +1 -0
  27. package/dist/sortedSegmentSet.d.ts.map +1 -1
  28. package/dist/sortedSegmentSet.js +3 -0
  29. package/dist/sortedSegmentSet.js.map +1 -1
  30. package/dist/test/beastTest.spec.js +5 -5
  31. package/dist/test/beastTest.spec.js.map +1 -1
  32. package/dist/test/client.localReference.spec.js +3 -3
  33. package/dist/test/client.localReference.spec.js.map +1 -1
  34. package/dist/test/client.rollback.spec.js +17 -0
  35. package/dist/test/client.rollback.spec.js.map +1 -1
  36. package/dist/test/clientTestHelper.d.ts +100 -0
  37. package/dist/test/clientTestHelper.d.ts.map +1 -0
  38. package/dist/test/clientTestHelper.js +196 -0
  39. package/dist/test/clientTestHelper.js.map +1 -0
  40. package/dist/test/mergeTree.annotate.spec.js +12 -12
  41. package/dist/test/mergeTree.annotate.spec.js.map +1 -1
  42. package/dist/test/mergeTree.markRangeRemoved.deltaCallback.spec.js +1 -1
  43. package/dist/test/mergeTree.markRangeRemoved.deltaCallback.spec.js.map +1 -1
  44. package/dist/test/obliterate.concurrent.spec.js +93 -90
  45. package/dist/test/obliterate.concurrent.spec.js.map +1 -1
  46. package/dist/test/obliterate.deltaCallback.spec.js +121 -116
  47. package/dist/test/obliterate.deltaCallback.spec.js.map +1 -1
  48. package/dist/test/obliterate.rangeExpansion.spec.js +29 -79
  49. package/dist/test/obliterate.rangeExpansion.spec.js.map +1 -1
  50. package/dist/test/obliterate.reconnect.spec.js +235 -58
  51. package/dist/test/obliterate.reconnect.spec.js.map +1 -1
  52. package/dist/test/testClient.js +1 -1
  53. package/dist/test/testClient.js.map +1 -1
  54. package/dist/test/testUtils.d.ts +13 -0
  55. package/dist/test/testUtils.d.ts.map +1 -1
  56. package/dist/test/testUtils.js +22 -1
  57. package/dist/test/testUtils.js.map +1 -1
  58. package/lib/client.d.ts +7 -1
  59. package/lib/client.d.ts.map +1 -1
  60. package/lib/client.js +155 -46
  61. package/lib/client.js.map +1 -1
  62. package/lib/index.d.ts +1 -1
  63. package/lib/index.d.ts.map +1 -1
  64. package/lib/index.js +1 -0
  65. package/lib/index.js.map +1 -1
  66. package/lib/mergeTree.d.ts +17 -5
  67. package/lib/mergeTree.d.ts.map +1 -1
  68. package/lib/mergeTree.js +192 -83
  69. package/lib/mergeTree.js.map +1 -1
  70. package/lib/mergeTreeNodes.d.ts +16 -18
  71. package/lib/mergeTreeNodes.d.ts.map +1 -1
  72. package/lib/mergeTreeNodes.js +7 -1
  73. package/lib/mergeTreeNodes.js.map +1 -1
  74. package/lib/perspective.d.ts +9 -0
  75. package/lib/perspective.d.ts.map +1 -1
  76. package/lib/perspective.js +12 -0
  77. package/lib/perspective.js.map +1 -1
  78. package/lib/segmentInfos.d.ts +32 -4
  79. package/lib/segmentInfos.d.ts.map +1 -1
  80. package/lib/segmentInfos.js +2 -1
  81. package/lib/segmentInfos.js.map +1 -1
  82. package/lib/sortedSegmentSet.d.ts +1 -0
  83. package/lib/sortedSegmentSet.d.ts.map +1 -1
  84. package/lib/sortedSegmentSet.js +3 -0
  85. package/lib/sortedSegmentSet.js.map +1 -1
  86. package/lib/test/beastTest.spec.js +5 -5
  87. package/lib/test/beastTest.spec.js.map +1 -1
  88. package/lib/test/client.localReference.spec.js +3 -3
  89. package/lib/test/client.localReference.spec.js.map +1 -1
  90. package/lib/test/client.rollback.spec.js +18 -1
  91. package/lib/test/client.rollback.spec.js.map +1 -1
  92. package/lib/test/clientTestHelper.d.ts +100 -0
  93. package/lib/test/clientTestHelper.d.ts.map +1 -0
  94. package/lib/test/clientTestHelper.js +192 -0
  95. package/lib/test/clientTestHelper.js.map +1 -0
  96. package/lib/test/mergeTree.annotate.spec.js +12 -12
  97. package/lib/test/mergeTree.annotate.spec.js.map +1 -1
  98. package/lib/test/mergeTree.markRangeRemoved.deltaCallback.spec.js +1 -1
  99. package/lib/test/mergeTree.markRangeRemoved.deltaCallback.spec.js.map +1 -1
  100. package/lib/test/obliterate.concurrent.spec.js +93 -90
  101. package/lib/test/obliterate.concurrent.spec.js.map +1 -1
  102. package/lib/test/obliterate.deltaCallback.spec.js +121 -116
  103. package/lib/test/obliterate.deltaCallback.spec.js.map +1 -1
  104. package/lib/test/obliterate.rangeExpansion.spec.js +1 -51
  105. package/lib/test/obliterate.rangeExpansion.spec.js.map +1 -1
  106. package/lib/test/obliterate.reconnect.spec.js +236 -59
  107. package/lib/test/obliterate.reconnect.spec.js.map +1 -1
  108. package/lib/test/testClient.js +1 -1
  109. package/lib/test/testClient.js.map +1 -1
  110. package/lib/test/testUtils.d.ts +13 -0
  111. package/lib/test/testUtils.d.ts.map +1 -1
  112. package/lib/test/testUtils.js +20 -0
  113. package/lib/test/testUtils.js.map +1 -1
  114. package/package.json +19 -18
  115. package/src/client.ts +286 -55
  116. package/src/index.ts +1 -1
  117. package/src/mergeTree.ts +265 -98
  118. package/src/mergeTreeNodes.ts +24 -18
  119. package/src/perspective.ts +21 -0
  120. package/src/segmentInfos.ts +48 -6
  121. package/src/sortedSegmentSet.ts +4 -0
  122. package/dist/test/partialSyncHelper.d.ts +0 -42
  123. package/dist/test/partialSyncHelper.d.ts.map +0 -1
  124. package/dist/test/partialSyncHelper.js +0 -96
  125. package/dist/test/partialSyncHelper.js.map +0 -1
  126. package/dist/test/reconnectHelper.d.ts +0 -50
  127. package/dist/test/reconnectHelper.d.ts.map +0 -1
  128. package/dist/test/reconnectHelper.js +0 -106
  129. package/dist/test/reconnectHelper.js.map +0 -1
  130. package/lib/test/partialSyncHelper.d.ts +0 -42
  131. package/lib/test/partialSyncHelper.d.ts.map +0 -1
  132. package/lib/test/partialSyncHelper.js +0 -92
  133. package/lib/test/partialSyncHelper.js.map +0 -1
  134. package/lib/test/reconnectHelper.d.ts +0 -50
  135. package/lib/test/reconnectHelper.d.ts.map +0 -1
  136. package/lib/test/reconnectHelper.js +0 -102
  137. package/lib/test/reconnectHelper.js.map +0 -1
@@ -3,11 +3,20 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
  import { strict as assert } from "node:assert";
6
+ import { generatePairwiseOptions } from "@fluid-private/test-pairwise-generator";
6
7
  import { MergeTree } from "../mergeTree.js";
7
- import { ReconnectTestHelper } from "./reconnectHelper.js";
8
- import { useStrictPartialLengthChecks } from "./testUtils.js";
9
- for (const incremental of [true, false]) {
10
- describe(`obliterate partial lengths incremental = ${incremental}`, () => {
8
+ import { Side } from "../sequencePlace.js";
9
+ import { ClientTestHelper } from "./clientTestHelper.js";
10
+ import { itCorrectlyObliterates, useStrictPartialLengthChecks } from "./testUtils.js";
11
+ for (const { incremental, mergeTreeEnableSidedObliterate } of generatePairwiseOptions({
12
+ incremental: [true, false],
13
+ mergeTreeEnableSidedObliterate: [
14
+ false,
15
+ // TODO:AB#31001: Enable this once sided obliterate supports reconnect.
16
+ // true,
17
+ ],
18
+ })) {
19
+ describe(`obliterate partial lengths incremental = ${incremental} enableSidedObliterate = ${mergeTreeEnableSidedObliterate}`, () => {
11
20
  useStrictPartialLengthChecks();
12
21
  beforeEach(() => {
13
22
  MergeTree.options.incrementalUpdate = incremental;
@@ -16,68 +25,60 @@ for (const incremental of [true, false]) {
16
25
  MergeTree.options.incrementalUpdate = true;
17
26
  });
18
27
  it("obliterate does not expand during rebase", () => {
19
- const helper = new ReconnectTestHelper();
28
+ const helper = new ClientTestHelper({ mergeTreeEnableSidedObliterate });
20
29
  helper.insertText("B", 0, "ABCD");
21
30
  helper.processAllOps();
22
31
  helper.removeRange("B", 0, 3);
23
- helper.disconnect(["C"]);
24
- const cOp = helper.obliterateRangeLocal("C", 0, 1);
25
- helper.reconnect(["C"]);
26
- helper.submitDisconnectedOp("C", cOp);
32
+ helper.disconnect("C");
33
+ helper.obliterateRange("C", 0, 1);
34
+ helper.reconnect("C");
27
35
  helper.processAllOps();
28
36
  assert.equal(helper.clients.A.getText(), "D");
29
37
  helper.logger.validate();
30
38
  });
31
39
  it("does delete reconnected insert into obliterate range if insert is rebased", () => {
32
- const helper = new ReconnectTestHelper();
40
+ const helper = new ClientTestHelper({ mergeTreeEnableSidedObliterate });
33
41
  helper.insertText("B", 0, "ABCD");
34
42
  helper.processAllOps();
35
43
  helper.obliterateRange("B", 0, 3);
36
- helper.disconnect(["C"]);
37
- const cOp = helper.insertTextLocal("C", 2, "aaa");
38
- helper.reconnect(["C"]);
39
- helper.submitDisconnectedOp("C", cOp);
44
+ helper.disconnect("C");
45
+ helper.insertText("C", 2, "aaa");
46
+ helper.reconnect("C");
40
47
  helper.processAllOps();
41
48
  assert.equal(helper.clients.A.getText(), "D");
42
49
  assert.equal(helper.clients.C.getText(), "D");
43
50
  helper.logger.validate();
44
51
  });
45
52
  it("deletes reconnected insert into obliterate range when entire string deleted if rebased", () => {
46
- const helper = new ReconnectTestHelper();
53
+ const helper = new ClientTestHelper({ mergeTreeEnableSidedObliterate });
47
54
  helper.insertText("B", 0, "ABCD");
48
55
  helper.processAllOps();
49
56
  helper.obliterateRange("B", 0, 4);
50
- helper.disconnect(["C"]);
51
- const cOp = helper.insertTextLocal("C", 2, "aaa");
52
- helper.reconnect(["C"]);
53
- helper.submitDisconnectedOp("C", cOp);
57
+ helper.disconnect("C");
58
+ helper.insertText("C", 2, "aaa");
59
+ helper.reconnect("C");
54
60
  helper.processAllOps();
55
61
  assert.equal(helper.clients.A.getText(), "");
56
62
  assert.equal(helper.clients.C.getText(), "");
57
63
  helper.logger.validate();
58
64
  });
59
65
  it("obliterates local segment while disconnected", () => {
60
- const helper = new ReconnectTestHelper();
66
+ const helper = new ClientTestHelper({ mergeTreeEnableSidedObliterate });
61
67
  // [C]-D-(E)-F-H-G-B-A
62
68
  helper.insertText("B", 0, "A");
63
- helper.disconnect(["C"]);
64
- const op0 = helper.insertTextLocal("C", 0, "B");
65
- const op1 = helper.insertTextLocal("C", 0, "CDEFG");
66
- const op2 = helper.removeRangeLocal("C", 0, 1);
67
- const op3 = helper.obliterateRangeLocal("C", 1, 2);
68
- const op4 = helper.insertTextLocal("C", 2, "H");
69
- helper.reconnect(["C"]);
70
- helper.submitDisconnectedOp("C", op0);
71
- helper.submitDisconnectedOp("C", op1);
72
- helper.submitDisconnectedOp("C", op2);
73
- helper.submitDisconnectedOp("C", op3);
74
- helper.submitDisconnectedOp("C", op4);
69
+ helper.disconnect("C");
70
+ helper.insertText("C", 0, "B");
71
+ helper.insertText("C", 0, "CDEFG");
72
+ helper.removeRange("C", 0, 1);
73
+ helper.obliterateRange("C", 1, 2);
74
+ helper.insertText("C", 2, "H");
75
+ helper.reconnect("C");
75
76
  helper.processAllOps();
76
77
  assert.equal(helper.clients.A.getText(), "DFHGBA");
77
78
  helper.logger.validate();
78
79
  });
79
80
  it("deletes concurrently inserted segment between separated group ops", () => {
80
- const helper = new ReconnectTestHelper();
81
+ const helper = new ClientTestHelper({ mergeTreeEnableSidedObliterate });
81
82
  // B-A
82
83
  // (B-C-A)
83
84
  helper.insertText("A", 0, "A");
@@ -85,39 +86,37 @@ for (const incremental of [true, false]) {
85
86
  helper.processAllOps();
86
87
  helper.logger.validate();
87
88
  helper.insertText("A", 1, "C");
88
- helper.disconnect(["B"]);
89
- const op = helper.obliterateRangeLocal("B", 0, 2);
90
- helper.reconnect(["B"]);
91
- helper.submitDisconnectedOp("B", op);
89
+ helper.disconnect("B");
90
+ helper.obliterateRange("B", 0, 2);
91
+ helper.reconnect("B");
92
92
  helper.processAllOps();
93
93
  assert.equal(helper.clients.A.getText(), "");
94
94
  helper.logger.validate();
95
95
  });
96
96
  it("removes correct number of pending segments", () => {
97
- const helper = new ReconnectTestHelper();
97
+ const helper = new ClientTestHelper({ mergeTreeEnableSidedObliterate });
98
98
  // (BC)-[A]
99
- const op0 = helper.insertTextLocal("A", 0, "A");
100
- const op1 = helper.insertTextLocal("A", 1, "BC");
101
- const op2 = helper.obliterateRangeLocal("A", 0, 2);
102
- helper.submitDisconnectedOp("A", op0);
103
- helper.submitDisconnectedOp("A", op1);
104
- helper.submitDisconnectedOp("A", op2);
99
+ helper.disconnect("A");
100
+ helper.insertText("A", 0, "A");
101
+ helper.insertText("A", 1, "BC");
102
+ helper.obliterateRange("A", 0, 2);
103
+ helper.reconnect("A");
105
104
  helper.removeRange("A", 0, 1);
106
105
  helper.processAllOps();
107
106
  assert.equal(helper.clients.A.getText(), "");
108
107
  helper.logger.validate();
109
108
  });
110
109
  it("doesn't do obliterate ack traversal when starting segment has been acked", () => {
111
- const helper = new ReconnectTestHelper();
110
+ const helper = new ClientTestHelper({ mergeTreeEnableSidedObliterate });
112
111
  // AB
113
112
  // (E)-[F]-(G-D-(C-A)-B)
114
113
  helper.insertText("B", 0, "AB");
115
114
  helper.processAllOps();
116
115
  helper.logger.validate();
117
- const op0 = helper.insertTextLocal("A", 0, "C");
118
- const op1 = helper.obliterateRangeLocal("A", 0, 2);
119
- helper.submitDisconnectedOp("A", op0);
120
- helper.submitDisconnectedOp("A", op1);
116
+ helper.disconnect("A");
117
+ helper.insertText("A", 0, "C");
118
+ helper.obliterateRange("A", 0, 2);
119
+ helper.reconnect("A");
121
120
  helper.insertText("B", 0, "D");
122
121
  helper.insertText("A", 0, "EFG");
123
122
  helper.obliterateRange("A", 0, 1);
@@ -128,32 +127,210 @@ for (const incremental of [true, false]) {
128
127
  helper.logger.validate();
129
128
  });
130
129
  it("does not delete reconnected insert at start of obliterate range if rebased", () => {
131
- const helper = new ReconnectTestHelper();
130
+ const helper = new ClientTestHelper({ mergeTreeEnableSidedObliterate });
132
131
  helper.insertText("B", 0, "ABCD");
133
132
  helper.processAllOps();
134
133
  helper.obliterateRange("B", 0, 3);
135
- helper.disconnect(["C"]);
136
- const cOp = helper.insertTextLocal("C", 0, "aaa");
137
- helper.reconnect(["C"]);
138
- helper.submitDisconnectedOp("C", cOp);
134
+ helper.disconnect("C");
135
+ helper.insertText("C", 0, "aaa");
136
+ helper.reconnect("C");
139
137
  helper.processAllOps();
140
138
  assert.equal(helper.clients.A.getText(), "aaaD");
141
139
  assert.equal(helper.clients.C.getText(), "aaaD");
142
140
  helper.logger.validate();
143
141
  });
144
142
  it("does not delete reconnected insert at end of obliterate range", () => {
145
- const helper = new ReconnectTestHelper();
143
+ const helper = new ClientTestHelper({ mergeTreeEnableSidedObliterate });
146
144
  helper.insertText("B", 0, "ABCD");
147
145
  helper.processAllOps();
148
146
  helper.obliterateRange("B", 0, 3);
149
- helper.disconnect(["C"]);
150
- const cOp = helper.insertTextLocal("C", 3, "aaa");
151
- helper.reconnect(["C"]);
152
- helper.submitDisconnectedOp("C", cOp);
147
+ helper.disconnect("C");
148
+ helper.insertText("C", 3, "aaa");
149
+ helper.reconnect("C");
153
150
  helper.processAllOps();
154
151
  assert.equal(helper.clients.A.getText(), "aaaD");
155
152
  helper.logger.validate();
156
153
  });
157
154
  });
158
155
  }
156
+ describe("sided obliterate reconnect", () => {
157
+ itCorrectlyObliterates({
158
+ title: "add text, disconnect, obliterate, reconnect, insert adjacent to obliterated range",
159
+ action: (helper) => {
160
+ helper.insertText("A", 0, "hello world");
161
+ helper.processAllOps();
162
+ helper.disconnect("C");
163
+ helper.obliterateRange("C", { pos: 1, side: Side.After }, { pos: 4, side: Side.After });
164
+ // inserting adjacent to the obliterated range start
165
+ helper.reconnect("C");
166
+ helper.insertText("A", 2, "123");
167
+ },
168
+ expectedText: "he world",
169
+ });
170
+ itCorrectlyObliterates({
171
+ title: "add text, disconnect, obliterate, insert adjacent to obliterated range, reconnect",
172
+ action: (helper) => {
173
+ helper.insertText("A", 0, "hello world");
174
+ helper.processAllOps();
175
+ helper.disconnect("C");
176
+ helper.obliterateRange("C", { pos: 1, side: Side.After }, { pos: 4, side: Side.After });
177
+ // inserting adjacent to the obliterated range start
178
+ helper.insertText("A", 2, "123");
179
+ helper.reconnect("C");
180
+ },
181
+ expectedText: "he world",
182
+ });
183
+ describe("obliterate rebasing over", () => {
184
+ for (const { removalType, getRemoveMethod } of [
185
+ {
186
+ removalType: "remove",
187
+ getRemoveMethod: (helper) => helper.removeRange.bind(helper),
188
+ },
189
+ {
190
+ removalType: "obliterate",
191
+ getRemoveMethod: (helper) => helper.obliterateRange.bind(helper),
192
+ },
193
+ ]) {
194
+ itCorrectlyObliterates({
195
+ title: `${removalType} overlapping obliterate start`,
196
+ action: (helper) => {
197
+ helper.insertText("A", 0, "0123456789");
198
+ helper.processAllOps();
199
+ helper.disconnect("B");
200
+ helper.obliterateRange("B", { pos: 1, side: Side.Before }, { pos: 8, side: Side.After });
201
+ helper.insertText("A", 5, "should be obliterated");
202
+ getRemoveMethod(helper)("A", 0, 2);
203
+ helper.reconnect("B");
204
+ },
205
+ expectedText: "9",
206
+ });
207
+ itCorrectlyObliterates({
208
+ title: `${removalType} overlapping obliterate middle`,
209
+ action: (helper) => {
210
+ helper.insertText("A", 0, "0123456789");
211
+ helper.processAllOps();
212
+ helper.disconnect("B");
213
+ helper.obliterateRange("B", { pos: 1, side: Side.Before }, { pos: 8, side: Side.After });
214
+ getRemoveMethod(helper)("A", 3, 4);
215
+ helper.insertText("A", 5, "should be obliterated");
216
+ helper.reconnect("B");
217
+ },
218
+ expectedText: "09",
219
+ });
220
+ itCorrectlyObliterates({
221
+ title: `${removalType} overlapping obliterate end`,
222
+ action: (helper) => {
223
+ helper.insertText("A", 0, "0123456789");
224
+ helper.processAllOps();
225
+ helper.disconnect("B");
226
+ helper.obliterateRange("B", { pos: 1, side: Side.Before }, { pos: 8, side: Side.After });
227
+ getRemoveMethod(helper)("A", 8, 10);
228
+ helper.insertText("A", 5, "should be obliterated");
229
+ helper.reconnect("B");
230
+ },
231
+ expectedText: "0",
232
+ });
233
+ }
234
+ });
235
+ // This test and the analogous endpoint test below provide some rationale for the policy of tending to shrink obliterates
236
+ // inward upon reconnect in the case that regions near their endpoints were removed between the original submission and resubmission.
237
+ itCorrectlyObliterates({
238
+ title: "obliterate shrinks start point to best possible option",
239
+ action: (helper) => {
240
+ helper.insertText("A", 0, "0123456789");
241
+ helper.processAllOps();
242
+ helper.disconnect("B");
243
+ helper.obliterateRange("B", { pos: 3, side: Side.After }, { pos: 8, side: Side.Before }); // 4 through 7
244
+ helper.insertText("A", 7, "inside the original obliterate");
245
+ helper.removeRange("C", 1, 5); // 1234
246
+ helper.advanceClients("B");
247
+ // B recognizes an observer client at this seq will have '056inside the original obliterate789' and B's obliterate should additionally
248
+ // remove '5inside the original obliterate67'.
249
+ // It reconnects at this point and has a choice: it can either specify the startpoint of the obliterate as 'after the 0 character'
250
+ // (preserving the original side) or 'before the 5 character' (which will make its start endpoint no longer "sticky" like
251
+ // the original one). Seeing as the original startpoint has actually already been removed, we should choose the latter.
252
+ helper.reconnect("B");
253
+ // The possibility of another client doing something like this is additional justification for this policy:
254
+ // (point being that B will see this segment as clearly after the "0" character since it was inserted before the 3 in the original string)
255
+ helper.insertText("A", 3, "outside the original obliterate but in danger of being in the new one");
256
+ },
257
+ expectedText: "0outside the original obliterate but in danger of being in the new one89",
258
+ });
259
+ itCorrectlyObliterates({
260
+ title: "obliterate shrinks end point to best possible option",
261
+ action: (helper) => {
262
+ helper.insertText("A", 0, "0123456789");
263
+ helper.processAllOps();
264
+ helper.disconnect("B");
265
+ helper.obliterateRange("B", { pos: 3, side: Side.After }, { pos: 8, side: Side.Before }); // 4 through 7
266
+ helper.insertText("A", 7, "inside the original obliterate");
267
+ helper.removeRange("C", 6, 9); // 678
268
+ helper.advanceClients("B");
269
+ // B recognizes an observer client at this seq will have '056inside the original obliterate789' and B's obliterate should additionally
270
+ // remove '5inside the original obliterate67'.
271
+ // It reconnects at this point and has a choice: it can either specify the startpoint of the obliterate as 'after the 0 character'
272
+ // (preserving the original side) or 'before the 5 character' (which will make its start endpoint no longer "sticky" like
273
+ // the original one). Seeing as the original startpoint has actually already been removed, we should choose the latter.
274
+ helper.reconnect("B");
275
+ // The possibility of another client doing something like this is additional justification for this policy:
276
+ // (point being that B will see this segment as clearly after the "0" character since it was inserted before the 3 in the original string)
277
+ helper.insertText("A", 9 + "inside the original obliterate".length, "outside the original obliterate but in danger of being in the new one");
278
+ },
279
+ expectedText: "0123outside the original obliterate but in danger of being in the new one9",
280
+ });
281
+ itCorrectlyObliterates({
282
+ title: "recomputes obliterate tiebreak winner",
283
+ action: (helper) => {
284
+ helper.insertText("A", 0, "ABCDEFGHIJKLMNOPQ");
285
+ helper.processAllOps();
286
+ helper.disconnect("B");
287
+ helper.obliterateRange("D", { pos: 0, side: Side.Before }, { pos: 6, side: Side.After }); // ABCDEFG
288
+ helper.obliterateRange("C", { pos: 2, side: Side.Before }, { pos: 8, side: Side.After }); // CDEFGHI
289
+ helper.obliterateRange("B", { pos: 3, side: Side.After }, { pos: 4, side: Side.After }); // D
290
+ // This insertion position by B is critically inside the range that B originally obliterated, but that will no longer be the case
291
+ // later on when B reconnects, since the region B wanted to obliterate will be gone (so there is no way to specify the same obliterate).
292
+ helper.insertText("B", 4, "should go away");
293
+ helper.processAllOps();
294
+ helper.removeRange("C", 0, 1); // J
295
+ helper.insertText("C", 0, "01234567"); // C now sees '01234567KLMNOPQ'
296
+ helper.processAllOps();
297
+ // B now needs to recognize that it no longer necessarily has 'last-write-win' privilege over its insertion
298
+ // in the event that the new insertion position was obliterated concurrently to the op it's about to (re)submit.
299
+ helper.reconnect("B");
300
+ // ... and indeed, in this case A has obliterated a region containing the "should go away" B inserted.
301
+ helper.obliterateRange("A", { pos: 7, side: Side.After }, { pos: 9, side: Side.Before }); // K in '01234567KLMNOPQ', expanding on both ends
302
+ },
303
+ expectedText: "01234567LMNOPQ",
304
+ });
305
+ // This test case demonstrates the need to 'pre-compute' the result of rebasing obliterate endpoints upon reconnection
306
+ // before segment order is normalized.
307
+ // TODO: AB#34898: This seems to demonstrate an issue with segment normalization which should be investigated.
308
+ // Resolving that work item may involve including this as a test case targeted at segment ordering instead, and finding an analogous case
309
+ // where legitimate segment reording could affect obliterate rebasing.
310
+ itCorrectlyObliterates({
311
+ title: "computes obliterate rebases before segment normalization",
312
+ action: (helper) => {
313
+ helper.insertText("A", 0, "0ABCDEFGHIJKLMNOPQRSTUVWXYZ");
314
+ helper.processAllOps();
315
+ helper.disconnect("B");
316
+ helper.obliterateRange("B", { pos: 9, side: Side.Before }, { pos: 26, side: Side.After }); // I through Z inclusive
317
+ helper.insertText("B", 3, "e"); // between "B" and "C"
318
+ helper.obliterateRange("B", { pos: 2, side: Side.After }, { pos: 6, side: Side.After }); // the inserted 'e' through F inclusive
319
+ helper.obliterateRange("A", { pos: 4, side: Side.After }, { pos: 15, side: Side.Before }); // E through N inclusive
320
+ helper.processAllOps();
321
+ // Before reconnecting, B's segment order is:
322
+ // 0ABeCDEFGHIJKLMNOPQRSTUVWXYZ
323
+ // B has issued obliterates for:
324
+ // 0ABeCDEFGHIJKLMNOPQRSTUVWXYZ
325
+ // ( ] [ ]
326
+ // and it sees that A has already obliterated:
327
+ // 0ABeCDEFGHIJKLMNOPQRSTUVWXYZ
328
+ // ( )
329
+ // Segment normalization on B will reorder "eCDEFGHIJKLMNOPQRSTUVWXYZ" to "eCDOPQRSTUVWXYZEFGHIJKLMN"
330
+ // (note that this seems unnecessary and is why AB#34898 is filed)
331
+ helper.reconnect("B");
332
+ },
333
+ expectedText: "0AB",
334
+ });
335
+ });
159
336
  //# sourceMappingURL=obliterate.reconnect.spec.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"obliterate.reconnect.spec.js","sourceRoot":"","sources":["../../src/test/obliterate.reconnect.spec.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { strict as assert } from \"node:assert\";\n\nimport { MergeTree } from \"../mergeTree.js\";\n\nimport { ReconnectTestHelper } from \"./reconnectHelper.js\";\nimport { useStrictPartialLengthChecks } from \"./testUtils.js\";\n\nfor (const incremental of [true, false]) {\n\tdescribe(`obliterate partial lengths incremental = ${incremental}`, () => {\n\t\tuseStrictPartialLengthChecks();\n\n\t\tbeforeEach(() => {\n\t\t\tMergeTree.options.incrementalUpdate = incremental;\n\t\t});\n\n\t\tafterEach(() => {\n\t\t\tMergeTree.options.incrementalUpdate = true;\n\t\t});\n\n\t\tit(\"obliterate does not expand during rebase\", () => {\n\t\t\tconst helper = new ReconnectTestHelper();\n\n\t\t\thelper.insertText(\"B\", 0, \"ABCD\");\n\t\t\thelper.processAllOps();\n\t\t\thelper.removeRange(\"B\", 0, 3);\n\t\t\thelper.disconnect([\"C\"]);\n\t\t\tconst cOp = helper.obliterateRangeLocal(\"C\", 0, 1);\n\t\t\thelper.reconnect([\"C\"]);\n\t\t\thelper.submitDisconnectedOp(\"C\", cOp);\n\t\t\thelper.processAllOps();\n\n\t\t\tassert.equal(helper.clients.A.getText(), \"D\");\n\n\t\t\thelper.logger.validate();\n\t\t});\n\n\t\tit(\"does delete reconnected insert into obliterate range if insert is rebased\", () => {\n\t\t\tconst helper = new ReconnectTestHelper();\n\n\t\t\thelper.insertText(\"B\", 0, \"ABCD\");\n\t\t\thelper.processAllOps();\n\t\t\thelper.obliterateRange(\"B\", 0, 3);\n\t\t\thelper.disconnect([\"C\"]);\n\t\t\tconst cOp = helper.insertTextLocal(\"C\", 2, \"aaa\");\n\t\t\thelper.reconnect([\"C\"]);\n\t\t\thelper.submitDisconnectedOp(\"C\", cOp);\n\t\t\thelper.processAllOps();\n\n\t\t\tassert.equal(helper.clients.A.getText(), \"D\");\n\t\t\tassert.equal(helper.clients.C.getText(), \"D\");\n\n\t\t\thelper.logger.validate();\n\t\t});\n\n\t\tit(\"deletes reconnected insert into obliterate range when entire string deleted if rebased\", () => {\n\t\t\tconst helper = new ReconnectTestHelper();\n\n\t\t\thelper.insertText(\"B\", 0, \"ABCD\");\n\t\t\thelper.processAllOps();\n\t\t\thelper.obliterateRange(\"B\", 0, 4);\n\t\t\thelper.disconnect([\"C\"]);\n\t\t\tconst cOp = helper.insertTextLocal(\"C\", 2, \"aaa\");\n\t\t\thelper.reconnect([\"C\"]);\n\t\t\thelper.submitDisconnectedOp(\"C\", cOp);\n\t\t\thelper.processAllOps();\n\n\t\t\tassert.equal(helper.clients.A.getText(), \"\");\n\t\t\tassert.equal(helper.clients.C.getText(), \"\");\n\n\t\t\thelper.logger.validate();\n\t\t});\n\n\t\tit(\"obliterates local segment while disconnected\", () => {\n\t\t\tconst helper = new ReconnectTestHelper();\n\n\t\t\t// [C]-D-(E)-F-H-G-B-A\n\n\t\t\thelper.insertText(\"B\", 0, \"A\");\n\n\t\t\thelper.disconnect([\"C\"]);\n\t\t\tconst op0 = helper.insertTextLocal(\"C\", 0, \"B\");\n\t\t\tconst op1 = helper.insertTextLocal(\"C\", 0, \"CDEFG\");\n\t\t\tconst op2 = helper.removeRangeLocal(\"C\", 0, 1);\n\t\t\tconst op3 = helper.obliterateRangeLocal(\"C\", 1, 2);\n\t\t\tconst op4 = helper.insertTextLocal(\"C\", 2, \"H\");\n\n\t\t\thelper.reconnect([\"C\"]);\n\t\t\thelper.submitDisconnectedOp(\"C\", op0);\n\t\t\thelper.submitDisconnectedOp(\"C\", op1);\n\t\t\thelper.submitDisconnectedOp(\"C\", op2);\n\t\t\thelper.submitDisconnectedOp(\"C\", op3);\n\t\t\thelper.submitDisconnectedOp(\"C\", op4);\n\n\t\t\thelper.processAllOps();\n\n\t\t\tassert.equal(helper.clients.A.getText(), \"DFHGBA\");\n\n\t\t\thelper.logger.validate();\n\t\t});\n\n\t\tit(\"deletes concurrently inserted segment between separated group ops\", () => {\n\t\t\tconst helper = new ReconnectTestHelper();\n\n\t\t\t// B-A\n\t\t\t// (B-C-A)\n\n\t\t\thelper.insertText(\"A\", 0, \"A\");\n\t\t\thelper.insertText(\"A\", 0, \"B\");\n\t\t\thelper.processAllOps();\n\t\t\thelper.logger.validate();\n\t\t\thelper.insertText(\"A\", 1, \"C\");\n\n\t\t\thelper.disconnect([\"B\"]);\n\t\t\tconst op = helper.obliterateRangeLocal(\"B\", 0, 2);\n\t\t\thelper.reconnect([\"B\"]);\n\t\t\thelper.submitDisconnectedOp(\"B\", op);\n\n\t\t\thelper.processAllOps();\n\n\t\t\tassert.equal(helper.clients.A.getText(), \"\");\n\n\t\t\thelper.logger.validate();\n\t\t});\n\n\t\tit(\"removes correct number of pending segments\", () => {\n\t\t\tconst helper = new ReconnectTestHelper();\n\n\t\t\t// (BC)-[A]\n\n\t\t\tconst op0 = helper.insertTextLocal(\"A\", 0, \"A\");\n\t\t\tconst op1 = helper.insertTextLocal(\"A\", 1, \"BC\");\n\t\t\tconst op2 = helper.obliterateRangeLocal(\"A\", 0, 2);\n\n\t\t\thelper.submitDisconnectedOp(\"A\", op0);\n\t\t\thelper.submitDisconnectedOp(\"A\", op1);\n\t\t\thelper.submitDisconnectedOp(\"A\", op2);\n\n\t\t\thelper.removeRange(\"A\", 0, 1);\n\n\t\t\thelper.processAllOps();\n\n\t\t\tassert.equal(helper.clients.A.getText(), \"\");\n\n\t\t\thelper.logger.validate();\n\t\t});\n\n\t\tit(\"doesn't do obliterate ack traversal when starting segment has been acked\", () => {\n\t\t\tconst helper = new ReconnectTestHelper();\n\n\t\t\t// AB\n\t\t\t// (E)-[F]-(G-D-(C-A)-B)\n\n\t\t\thelper.insertText(\"B\", 0, \"AB\");\n\t\t\thelper.processAllOps();\n\t\t\thelper.logger.validate();\n\n\t\t\tconst op0 = helper.insertTextLocal(\"A\", 0, \"C\");\n\t\t\tconst op1 = helper.obliterateRangeLocal(\"A\", 0, 2);\n\t\t\thelper.submitDisconnectedOp(\"A\", op0);\n\t\t\thelper.submitDisconnectedOp(\"A\", op1);\n\n\t\t\thelper.insertText(\"B\", 0, \"D\");\n\t\t\thelper.insertText(\"A\", 0, \"EFG\");\n\t\t\thelper.obliterateRange(\"A\", 0, 1);\n\t\t\thelper.removeRange(\"A\", 0, 1);\n\t\t\thelper.obliterateRange(\"A\", 0, 2);\n\t\t\thelper.processAllOps();\n\n\t\t\tassert.equal(helper.clients.A.getText(), \"\");\n\n\t\t\thelper.logger.validate();\n\t\t});\n\n\t\tit(\"does not delete reconnected insert at start of obliterate range if rebased\", () => {\n\t\t\tconst helper = new ReconnectTestHelper();\n\n\t\t\thelper.insertText(\"B\", 0, \"ABCD\");\n\t\t\thelper.processAllOps();\n\t\t\thelper.obliterateRange(\"B\", 0, 3);\n\t\t\thelper.disconnect([\"C\"]);\n\t\t\tconst cOp = helper.insertTextLocal(\"C\", 0, \"aaa\");\n\t\t\thelper.reconnect([\"C\"]);\n\t\t\thelper.submitDisconnectedOp(\"C\", cOp);\n\t\t\thelper.processAllOps();\n\n\t\t\tassert.equal(helper.clients.A.getText(), \"aaaD\");\n\t\t\tassert.equal(helper.clients.C.getText(), \"aaaD\");\n\n\t\t\thelper.logger.validate();\n\t\t});\n\n\t\tit(\"does not delete reconnected insert at end of obliterate range\", () => {\n\t\t\tconst helper = new ReconnectTestHelper();\n\n\t\t\thelper.insertText(\"B\", 0, \"ABCD\");\n\t\t\thelper.processAllOps();\n\t\t\thelper.obliterateRange(\"B\", 0, 3);\n\t\t\thelper.disconnect([\"C\"]);\n\t\t\tconst cOp = helper.insertTextLocal(\"C\", 3, \"aaa\");\n\t\t\thelper.reconnect([\"C\"]);\n\t\t\thelper.submitDisconnectedOp(\"C\", cOp);\n\t\t\thelper.processAllOps();\n\n\t\t\tassert.equal(helper.clients.A.getText(), \"aaaD\");\n\n\t\t\thelper.logger.validate();\n\t\t});\n\t});\n}\n"]}
1
+ {"version":3,"file":"obliterate.reconnect.spec.js","sourceRoot":"","sources":["../../src/test/obliterate.reconnect.spec.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { strict as assert } from \"node:assert\";\n\nimport { generatePairwiseOptions } from \"@fluid-private/test-pairwise-generator\";\n\nimport { MergeTree } from \"../mergeTree.js\";\nimport { Side } from \"../sequencePlace.js\";\n\nimport { ClientTestHelper } from \"./clientTestHelper.js\";\nimport { itCorrectlyObliterates, useStrictPartialLengthChecks } from \"./testUtils.js\";\n\nfor (const { incremental, mergeTreeEnableSidedObliterate } of generatePairwiseOptions({\n\tincremental: [true, false],\n\tmergeTreeEnableSidedObliterate: [\n\t\tfalse,\n\t\t// TODO:AB#31001: Enable this once sided obliterate supports reconnect.\n\t\t// true,\n\t],\n})) {\n\tdescribe(`obliterate partial lengths incremental = ${incremental} enableSidedObliterate = ${mergeTreeEnableSidedObliterate}`, () => {\n\t\tuseStrictPartialLengthChecks();\n\n\t\tbeforeEach(() => {\n\t\t\tMergeTree.options.incrementalUpdate = incremental;\n\t\t});\n\n\t\tafterEach(() => {\n\t\t\tMergeTree.options.incrementalUpdate = true;\n\t\t});\n\n\t\tit(\"obliterate does not expand during rebase\", () => {\n\t\t\tconst helper = new ClientTestHelper({ mergeTreeEnableSidedObliterate });\n\n\t\t\thelper.insertText(\"B\", 0, \"ABCD\");\n\t\t\thelper.processAllOps();\n\t\t\thelper.removeRange(\"B\", 0, 3);\n\t\t\thelper.disconnect(\"C\");\n\t\t\thelper.obliterateRange(\"C\", 0, 1);\n\t\t\thelper.reconnect(\"C\");\n\t\t\thelper.processAllOps();\n\n\t\t\tassert.equal(helper.clients.A.getText(), \"D\");\n\n\t\t\thelper.logger.validate();\n\t\t});\n\n\t\tit(\"does delete reconnected insert into obliterate range if insert is rebased\", () => {\n\t\t\tconst helper = new ClientTestHelper({ mergeTreeEnableSidedObliterate });\n\n\t\t\thelper.insertText(\"B\", 0, \"ABCD\");\n\t\t\thelper.processAllOps();\n\t\t\thelper.obliterateRange(\"B\", 0, 3);\n\t\t\thelper.disconnect(\"C\");\n\t\t\thelper.insertText(\"C\", 2, \"aaa\");\n\t\t\thelper.reconnect(\"C\");\n\t\t\thelper.processAllOps();\n\n\t\t\tassert.equal(helper.clients.A.getText(), \"D\");\n\t\t\tassert.equal(helper.clients.C.getText(), \"D\");\n\n\t\t\thelper.logger.validate();\n\t\t});\n\n\t\tit(\"deletes reconnected insert into obliterate range when entire string deleted if rebased\", () => {\n\t\t\tconst helper = new ClientTestHelper({ mergeTreeEnableSidedObliterate });\n\n\t\t\thelper.insertText(\"B\", 0, \"ABCD\");\n\t\t\thelper.processAllOps();\n\t\t\thelper.obliterateRange(\"B\", 0, 4);\n\t\t\thelper.disconnect(\"C\");\n\t\t\thelper.insertText(\"C\", 2, \"aaa\");\n\t\t\thelper.reconnect(\"C\");\n\t\t\thelper.processAllOps();\n\n\t\t\tassert.equal(helper.clients.A.getText(), \"\");\n\t\t\tassert.equal(helper.clients.C.getText(), \"\");\n\n\t\t\thelper.logger.validate();\n\t\t});\n\n\t\tit(\"obliterates local segment while disconnected\", () => {\n\t\t\tconst helper = new ClientTestHelper({ mergeTreeEnableSidedObliterate });\n\n\t\t\t// [C]-D-(E)-F-H-G-B-A\n\n\t\t\thelper.insertText(\"B\", 0, \"A\");\n\n\t\t\thelper.disconnect(\"C\");\n\t\t\thelper.insertText(\"C\", 0, \"B\");\n\t\t\thelper.insertText(\"C\", 0, \"CDEFG\");\n\t\t\thelper.removeRange(\"C\", 0, 1);\n\t\t\thelper.obliterateRange(\"C\", 1, 2);\n\t\t\thelper.insertText(\"C\", 2, \"H\");\n\n\t\t\thelper.reconnect(\"C\");\n\n\t\t\thelper.processAllOps();\n\n\t\t\tassert.equal(helper.clients.A.getText(), \"DFHGBA\");\n\n\t\t\thelper.logger.validate();\n\t\t});\n\n\t\tit(\"deletes concurrently inserted segment between separated group ops\", () => {\n\t\t\tconst helper = new ClientTestHelper({ mergeTreeEnableSidedObliterate });\n\n\t\t\t// B-A\n\t\t\t// (B-C-A)\n\n\t\t\thelper.insertText(\"A\", 0, \"A\");\n\t\t\thelper.insertText(\"A\", 0, \"B\");\n\t\t\thelper.processAllOps();\n\t\t\thelper.logger.validate();\n\t\t\thelper.insertText(\"A\", 1, \"C\");\n\n\t\t\thelper.disconnect(\"B\");\n\t\t\thelper.obliterateRange(\"B\", 0, 2);\n\t\t\thelper.reconnect(\"B\");\n\n\t\t\thelper.processAllOps();\n\n\t\t\tassert.equal(helper.clients.A.getText(), \"\");\n\n\t\t\thelper.logger.validate();\n\t\t});\n\n\t\tit(\"removes correct number of pending segments\", () => {\n\t\t\tconst helper = new ClientTestHelper({ mergeTreeEnableSidedObliterate });\n\n\t\t\t// (BC)-[A]\n\n\t\t\thelper.disconnect(\"A\");\n\t\t\thelper.insertText(\"A\", 0, \"A\");\n\t\t\thelper.insertText(\"A\", 1, \"BC\");\n\t\t\thelper.obliterateRange(\"A\", 0, 2);\n\t\t\thelper.reconnect(\"A\");\n\n\t\t\thelper.removeRange(\"A\", 0, 1);\n\n\t\t\thelper.processAllOps();\n\n\t\t\tassert.equal(helper.clients.A.getText(), \"\");\n\n\t\t\thelper.logger.validate();\n\t\t});\n\n\t\tit(\"doesn't do obliterate ack traversal when starting segment has been acked\", () => {\n\t\t\tconst helper = new ClientTestHelper({ mergeTreeEnableSidedObliterate });\n\n\t\t\t// AB\n\t\t\t// (E)-[F]-(G-D-(C-A)-B)\n\n\t\t\thelper.insertText(\"B\", 0, \"AB\");\n\t\t\thelper.processAllOps();\n\t\t\thelper.logger.validate();\n\n\t\t\thelper.disconnect(\"A\");\n\t\t\thelper.insertText(\"A\", 0, \"C\");\n\t\t\thelper.obliterateRange(\"A\", 0, 2);\n\t\t\thelper.reconnect(\"A\");\n\n\t\t\thelper.insertText(\"B\", 0, \"D\");\n\t\t\thelper.insertText(\"A\", 0, \"EFG\");\n\t\t\thelper.obliterateRange(\"A\", 0, 1);\n\t\t\thelper.removeRange(\"A\", 0, 1);\n\t\t\thelper.obliterateRange(\"A\", 0, 2);\n\t\t\thelper.processAllOps();\n\n\t\t\tassert.equal(helper.clients.A.getText(), \"\");\n\n\t\t\thelper.logger.validate();\n\t\t});\n\n\t\tit(\"does not delete reconnected insert at start of obliterate range if rebased\", () => {\n\t\t\tconst helper = new ClientTestHelper({ mergeTreeEnableSidedObliterate });\n\n\t\t\thelper.insertText(\"B\", 0, \"ABCD\");\n\t\t\thelper.processAllOps();\n\t\t\thelper.obliterateRange(\"B\", 0, 3);\n\t\t\thelper.disconnect(\"C\");\n\t\t\thelper.insertText(\"C\", 0, \"aaa\");\n\t\t\thelper.reconnect(\"C\");\n\n\t\t\thelper.processAllOps();\n\n\t\t\tassert.equal(helper.clients.A.getText(), \"aaaD\");\n\t\t\tassert.equal(helper.clients.C.getText(), \"aaaD\");\n\n\t\t\thelper.logger.validate();\n\t\t});\n\n\t\tit(\"does not delete reconnected insert at end of obliterate range\", () => {\n\t\t\tconst helper = new ClientTestHelper({ mergeTreeEnableSidedObliterate });\n\n\t\t\thelper.insertText(\"B\", 0, \"ABCD\");\n\t\t\thelper.processAllOps();\n\t\t\thelper.obliterateRange(\"B\", 0, 3);\n\t\t\thelper.disconnect(\"C\");\n\t\t\thelper.insertText(\"C\", 3, \"aaa\");\n\t\t\thelper.reconnect(\"C\");\n\t\t\thelper.processAllOps();\n\n\t\t\tassert.equal(helper.clients.A.getText(), \"aaaD\");\n\n\t\t\thelper.logger.validate();\n\t\t});\n\t});\n}\n\ndescribe(\"sided obliterate reconnect\", () => {\n\titCorrectlyObliterates({\n\t\ttitle: \"add text, disconnect, obliterate, reconnect, insert adjacent to obliterated range\",\n\t\taction: (helper) => {\n\t\t\thelper.insertText(\"A\", 0, \"hello world\");\n\t\t\thelper.processAllOps();\n\t\t\thelper.disconnect(\"C\");\n\t\t\thelper.obliterateRange(\"C\", { pos: 1, side: Side.After }, { pos: 4, side: Side.After });\n\t\t\t// inserting adjacent to the obliterated range start\n\t\t\thelper.reconnect(\"C\");\n\t\t\thelper.insertText(\"A\", 2, \"123\");\n\t\t},\n\t\texpectedText: \"he world\",\n\t});\n\titCorrectlyObliterates({\n\t\ttitle: \"add text, disconnect, obliterate, insert adjacent to obliterated range, reconnect\",\n\t\taction: (helper) => {\n\t\t\thelper.insertText(\"A\", 0, \"hello world\");\n\t\t\thelper.processAllOps();\n\t\t\thelper.disconnect(\"C\");\n\t\t\thelper.obliterateRange(\"C\", { pos: 1, side: Side.After }, { pos: 4, side: Side.After });\n\t\t\t// inserting adjacent to the obliterated range start\n\t\t\thelper.insertText(\"A\", 2, \"123\");\n\t\t\thelper.reconnect(\"C\");\n\t\t},\n\t\texpectedText: \"he world\",\n\t});\n\n\tdescribe(\"obliterate rebasing over\", () => {\n\t\tfor (const { removalType, getRemoveMethod } of [\n\t\t\t{\n\t\t\t\tremovalType: \"remove\",\n\t\t\t\tgetRemoveMethod: (helper: ClientTestHelper): ClientTestHelper[\"removeRange\"] =>\n\t\t\t\t\thelper.removeRange.bind(helper),\n\t\t\t},\n\t\t\t{\n\t\t\t\tremovalType: \"obliterate\",\n\t\t\t\tgetRemoveMethod: (helper: ClientTestHelper): ClientTestHelper[\"removeRange\"] =>\n\t\t\t\t\thelper.obliterateRange.bind(helper),\n\t\t\t},\n\t\t]) {\n\t\t\titCorrectlyObliterates({\n\t\t\t\ttitle: `${removalType} overlapping obliterate start`,\n\t\t\t\taction: (helper) => {\n\t\t\t\t\thelper.insertText(\"A\", 0, \"0123456789\");\n\t\t\t\t\thelper.processAllOps();\n\t\t\t\t\thelper.disconnect(\"B\");\n\t\t\t\t\thelper.obliterateRange(\n\t\t\t\t\t\t\"B\",\n\t\t\t\t\t\t{ pos: 1, side: Side.Before },\n\t\t\t\t\t\t{ pos: 8, side: Side.After },\n\t\t\t\t\t);\n\t\t\t\t\thelper.insertText(\"A\", 5, \"should be obliterated\");\n\t\t\t\t\tgetRemoveMethod(helper)(\"A\", 0, 2);\n\t\t\t\t\thelper.reconnect(\"B\");\n\t\t\t\t},\n\t\t\t\texpectedText: \"9\",\n\t\t\t});\n\n\t\t\titCorrectlyObliterates({\n\t\t\t\ttitle: `${removalType} overlapping obliterate middle`,\n\t\t\t\taction: (helper) => {\n\t\t\t\t\thelper.insertText(\"A\", 0, \"0123456789\");\n\t\t\t\t\thelper.processAllOps();\n\t\t\t\t\thelper.disconnect(\"B\");\n\t\t\t\t\thelper.obliterateRange(\n\t\t\t\t\t\t\"B\",\n\t\t\t\t\t\t{ pos: 1, side: Side.Before },\n\t\t\t\t\t\t{ pos: 8, side: Side.After },\n\t\t\t\t\t);\n\t\t\t\t\tgetRemoveMethod(helper)(\"A\", 3, 4);\n\t\t\t\t\thelper.insertText(\"A\", 5, \"should be obliterated\");\n\t\t\t\t\thelper.reconnect(\"B\");\n\t\t\t\t},\n\t\t\t\texpectedText: \"09\",\n\t\t\t});\n\n\t\t\titCorrectlyObliterates({\n\t\t\t\ttitle: `${removalType} overlapping obliterate end`,\n\t\t\t\taction: (helper) => {\n\t\t\t\t\thelper.insertText(\"A\", 0, \"0123456789\");\n\t\t\t\t\thelper.processAllOps();\n\t\t\t\t\thelper.disconnect(\"B\");\n\t\t\t\t\thelper.obliterateRange(\n\t\t\t\t\t\t\"B\",\n\t\t\t\t\t\t{ pos: 1, side: Side.Before },\n\t\t\t\t\t\t{ pos: 8, side: Side.After },\n\t\t\t\t\t);\n\t\t\t\t\tgetRemoveMethod(helper)(\"A\", 8, 10);\n\t\t\t\t\thelper.insertText(\"A\", 5, \"should be obliterated\");\n\t\t\t\t\thelper.reconnect(\"B\");\n\t\t\t\t},\n\t\t\t\texpectedText: \"0\",\n\t\t\t});\n\t\t}\n\t});\n\n\t// This test and the analogous endpoint test below provide some rationale for the policy of tending to shrink obliterates\n\t// inward upon reconnect in the case that regions near their endpoints were removed between the original submission and resubmission.\n\titCorrectlyObliterates({\n\t\ttitle: \"obliterate shrinks start point to best possible option\",\n\t\taction: (helper) => {\n\t\t\thelper.insertText(\"A\", 0, \"0123456789\");\n\t\t\thelper.processAllOps();\n\t\t\thelper.disconnect(\"B\");\n\t\t\thelper.obliterateRange(\"B\", { pos: 3, side: Side.After }, { pos: 8, side: Side.Before }); // 4 through 7\n\t\t\thelper.insertText(\"A\", 7, \"inside the original obliterate\");\n\t\t\thelper.removeRange(\"C\", 1, 5); // 1234\n\t\t\thelper.advanceClients(\"B\");\n\t\t\t// B recognizes an observer client at this seq will have '056inside the original obliterate789' and B's obliterate should additionally\n\t\t\t// remove '5inside the original obliterate67'.\n\t\t\t// It reconnects at this point and has a choice: it can either specify the startpoint of the obliterate as 'after the 0 character'\n\t\t\t// (preserving the original side) or 'before the 5 character' (which will make its start endpoint no longer \"sticky\" like\n\t\t\t// the original one). Seeing as the original startpoint has actually already been removed, we should choose the latter.\n\t\t\thelper.reconnect(\"B\");\n\t\t\t// The possibility of another client doing something like this is additional justification for this policy:\n\t\t\t// (point being that B will see this segment as clearly after the \"0\" character since it was inserted before the 3 in the original string)\n\t\t\thelper.insertText(\n\t\t\t\t\"A\",\n\t\t\t\t3,\n\t\t\t\t\"outside the original obliterate but in danger of being in the new one\",\n\t\t\t);\n\t\t},\n\t\texpectedText: \"0outside the original obliterate but in danger of being in the new one89\",\n\t});\n\n\titCorrectlyObliterates({\n\t\ttitle: \"obliterate shrinks end point to best possible option\",\n\t\taction: (helper) => {\n\t\t\thelper.insertText(\"A\", 0, \"0123456789\");\n\t\t\thelper.processAllOps();\n\t\t\thelper.disconnect(\"B\");\n\t\t\thelper.obliterateRange(\"B\", { pos: 3, side: Side.After }, { pos: 8, side: Side.Before }); // 4 through 7\n\t\t\thelper.insertText(\"A\", 7, \"inside the original obliterate\");\n\t\t\thelper.removeRange(\"C\", 6, 9); // 678\n\t\t\thelper.advanceClients(\"B\");\n\t\t\t// B recognizes an observer client at this seq will have '056inside the original obliterate789' and B's obliterate should additionally\n\t\t\t// remove '5inside the original obliterate67'.\n\t\t\t// It reconnects at this point and has a choice: it can either specify the startpoint of the obliterate as 'after the 0 character'\n\t\t\t// (preserving the original side) or 'before the 5 character' (which will make its start endpoint no longer \"sticky\" like\n\t\t\t// the original one). Seeing as the original startpoint has actually already been removed, we should choose the latter.\n\t\t\thelper.reconnect(\"B\");\n\t\t\t// The possibility of another client doing something like this is additional justification for this policy:\n\t\t\t// (point being that B will see this segment as clearly after the \"0\" character since it was inserted before the 3 in the original string)\n\t\t\thelper.insertText(\n\t\t\t\t\"A\",\n\t\t\t\t9 + \"inside the original obliterate\".length,\n\t\t\t\t\"outside the original obliterate but in danger of being in the new one\",\n\t\t\t);\n\t\t},\n\t\texpectedText: \"0123outside the original obliterate but in danger of being in the new one9\",\n\t});\n\n\titCorrectlyObliterates({\n\t\ttitle: \"recomputes obliterate tiebreak winner\",\n\t\taction: (helper) => {\n\t\t\thelper.insertText(\"A\", 0, \"ABCDEFGHIJKLMNOPQ\");\n\t\t\thelper.processAllOps();\n\t\t\thelper.disconnect(\"B\");\n\t\t\thelper.obliterateRange(\"D\", { pos: 0, side: Side.Before }, { pos: 6, side: Side.After }); // ABCDEFG\n\t\t\thelper.obliterateRange(\"C\", { pos: 2, side: Side.Before }, { pos: 8, side: Side.After }); // CDEFGHI\n\t\t\thelper.obliterateRange(\"B\", { pos: 3, side: Side.After }, { pos: 4, side: Side.After }); // D\n\t\t\t// This insertion position by B is critically inside the range that B originally obliterated, but that will no longer be the case\n\t\t\t// later on when B reconnects, since the region B wanted to obliterate will be gone (so there is no way to specify the same obliterate).\n\t\t\thelper.insertText(\"B\", 4, \"should go away\");\n\t\t\thelper.processAllOps();\n\t\t\thelper.removeRange(\"C\", 0, 1); // J\n\t\t\thelper.insertText(\"C\", 0, \"01234567\"); // C now sees '01234567KLMNOPQ'\n\t\t\thelper.processAllOps();\n\t\t\t// B now needs to recognize that it no longer necessarily has 'last-write-win' privilege over its insertion\n\t\t\t// in the event that the new insertion position was obliterated concurrently to the op it's about to (re)submit.\n\t\t\thelper.reconnect(\"B\");\n\t\t\t// ... and indeed, in this case A has obliterated a region containing the \"should go away\" B inserted.\n\t\t\thelper.obliterateRange(\"A\", { pos: 7, side: Side.After }, { pos: 9, side: Side.Before }); // K in '01234567KLMNOPQ', expanding on both ends\n\t\t},\n\t\texpectedText: \"01234567LMNOPQ\",\n\t});\n\n\t// This test case demonstrates the need to 'pre-compute' the result of rebasing obliterate endpoints upon reconnection\n\t// before segment order is normalized.\n\t// TODO: AB#34898: This seems to demonstrate an issue with segment normalization which should be investigated.\n\t// Resolving that work item may involve including this as a test case targeted at segment ordering instead, and finding an analogous case\n\t// where legitimate segment reording could affect obliterate rebasing.\n\titCorrectlyObliterates({\n\t\ttitle: \"computes obliterate rebases before segment normalization\",\n\t\taction: (helper) => {\n\t\t\thelper.insertText(\"A\", 0, \"0ABCDEFGHIJKLMNOPQRSTUVWXYZ\");\n\t\t\thelper.processAllOps();\n\t\t\thelper.disconnect(\"B\");\n\t\t\thelper.obliterateRange(\n\t\t\t\t\"B\",\n\t\t\t\t{ pos: 9, side: Side.Before },\n\t\t\t\t{ pos: 26, side: Side.After },\n\t\t\t); // I through Z inclusive\n\t\t\thelper.insertText(\"B\", 3, \"e\"); // between \"B\" and \"C\"\n\t\t\thelper.obliterateRange(\"B\", { pos: 2, side: Side.After }, { pos: 6, side: Side.After }); // the inserted 'e' through F inclusive\n\t\t\thelper.obliterateRange(\n\t\t\t\t\"A\",\n\t\t\t\t{ pos: 4, side: Side.After },\n\t\t\t\t{ pos: 15, side: Side.Before },\n\t\t\t); // E through N inclusive\n\t\t\thelper.processAllOps();\n\t\t\t// Before reconnecting, B's segment order is:\n\t\t\t// 0ABeCDEFGHIJKLMNOPQRSTUVWXYZ\n\t\t\t// B has issued obliterates for:\n\t\t\t// 0ABeCDEFGHIJKLMNOPQRSTUVWXYZ\n\t\t\t// ( ] [ ]\n\t\t\t// and it sees that A has already obliterated:\n\t\t\t// 0ABeCDEFGHIJKLMNOPQRSTUVWXYZ\n\t\t\t// ( )\n\n\t\t\t// Segment normalization on B will reorder \"eCDEFGHIJKLMNOPQRSTUVWXYZ\" to \"eCDOPQRSTUVWXYZEFGHIJKLMN\"\n\t\t\t// (note that this seems unnecessary and is why AB#34898 is filed)\n\t\t\thelper.reconnect(\"B\");\n\t\t},\n\t\texpectedText: \"0AB\",\n\t});\n});\n"]}
@@ -218,7 +218,7 @@ export class TestClient extends Client {
218
218
  return posAccumulated <= pos;
219
219
  });
220
220
  assert(segment !== undefined, "No segment found");
221
- const segoff = getSlideToSegoff({ segment, offset }) ?? segment;
221
+ const segoff = getSlideToSegoff({ segment, offset }, undefined, perspective) ?? segment;
222
222
  if (segoff.segment === undefined || segoff.offset === undefined) {
223
223
  return DetachedReferencePosition;
224
224
  }