@fluidframework/merge-tree 2.0.0-internal.8.0.0 → 2.0.0-rc.1.0.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.
- package/CHANGELOG.md +38 -0
- package/README.md +0 -6
- package/api-extractor-esm.json +17 -0
- package/api-extractor-lint.json +1 -10
- package/api-extractor.json +0 -4
- package/api-report/merge-tree.api.md +4 -9
- package/dist/client.d.ts +0 -7
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +0 -7
- package/dist/client.js.map +1 -1
- package/dist/merge-tree-alpha.d.ts +27 -12
- package/dist/merge-tree-beta.d.ts +0 -16
- package/dist/merge-tree-public.d.ts +0 -16
- package/dist/merge-tree-untrimmed.d.ts +5 -29
- package/dist/mergeTree.d.ts +0 -17
- package/dist/mergeTree.d.ts.map +1 -1
- package/dist/mergeTree.js +0 -130
- package/dist/mergeTree.js.map +1 -1
- package/dist/ops.d.ts +1 -1
- package/dist/ops.js +1 -1
- package/dist/ops.js.map +1 -1
- package/dist/revertibles.d.ts +4 -4
- package/dist/revertibles.js +3 -3
- package/dist/revertibles.js.map +1 -1
- package/dist/test/Insertion.perf.spec.d.ts +6 -0
- package/dist/test/Insertion.perf.spec.d.ts.map +1 -0
- package/dist/test/Insertion.perf.spec.js +113 -0
- package/dist/test/Insertion.perf.spec.js.map +1 -0
- package/dist/test/PartialLengths.perf.spec.d.ts +6 -0
- package/dist/test/PartialLengths.perf.spec.d.ts.map +1 -0
- package/dist/test/PartialLengths.perf.spec.js +67 -0
- package/dist/test/PartialLengths.perf.spec.js.map +1 -0
- package/dist/test/Removal.perf.spec.d.ts +6 -0
- package/dist/test/Removal.perf.spec.d.ts.map +1 -0
- package/dist/test/Removal.perf.spec.js +166 -0
- package/dist/test/Removal.perf.spec.js.map +1 -0
- package/dist/test/Snapshot.perf.spec.d.ts +6 -0
- package/dist/test/Snapshot.perf.spec.d.ts.map +1 -0
- package/dist/test/Snapshot.perf.spec.js +33 -0
- package/dist/test/Snapshot.perf.spec.js.map +1 -0
- package/dist/test/attributionCollection.perf.spec.d.ts +6 -0
- package/dist/test/attributionCollection.perf.spec.d.ts.map +1 -0
- package/dist/test/attributionCollection.perf.spec.js +231 -0
- package/dist/test/attributionCollection.perf.spec.js.map +1 -0
- package/dist/test/attributionCollection.spec.d.ts +6 -0
- package/dist/test/attributionCollection.spec.d.ts.map +1 -0
- package/dist/test/attributionCollection.spec.js +486 -0
- package/dist/test/attributionCollection.spec.js.map +1 -0
- package/dist/test/attributionPolicy.spec.d.ts +6 -0
- package/dist/test/attributionPolicy.spec.d.ts.map +1 -0
- package/dist/test/attributionPolicy.spec.js +189 -0
- package/dist/test/attributionPolicy.spec.js.map +1 -0
- package/dist/test/beastTest.d.ts +54 -0
- package/dist/test/beastTest.d.ts.map +1 -0
- package/dist/test/beastTest.js +1333 -0
- package/dist/test/beastTest.js.map +1 -0
- package/dist/test/client.annotateMarker.spec.d.ts +6 -0
- package/dist/test/client.annotateMarker.spec.d.ts.map +1 -0
- package/dist/test/client.annotateMarker.spec.js +45 -0
- package/dist/test/client.annotateMarker.spec.js.map +1 -0
- package/dist/test/client.apis.d.ts +7 -0
- package/dist/test/client.apis.d.ts.map +1 -0
- package/dist/test/client.apis.js +72 -0
- package/dist/test/client.apis.js.map +1 -0
- package/dist/test/client.applyMsg.spec.d.ts +6 -0
- package/dist/test/client.applyMsg.spec.d.ts.map +1 -0
- package/dist/test/client.applyMsg.spec.js +500 -0
- package/dist/test/client.applyMsg.spec.js.map +1 -0
- package/dist/test/client.applyStashedOpFarm.spec.d.ts +12 -0
- package/dist/test/client.applyStashedOpFarm.spec.d.ts.map +1 -0
- package/dist/test/client.applyStashedOpFarm.spec.js +144 -0
- package/dist/test/client.applyStashedOpFarm.spec.js.map +1 -0
- package/dist/test/client.attributionFarm.spec.d.ts +7 -0
- package/dist/test/client.attributionFarm.spec.d.ts.map +1 -0
- package/dist/test/client.attributionFarm.spec.js +96 -0
- package/dist/test/client.attributionFarm.spec.js.map +1 -0
- package/dist/test/client.conflictFarm.spec.d.ts +15 -0
- package/dist/test/client.conflictFarm.spec.d.ts.map +1 -0
- package/dist/test/client.conflictFarm.spec.js +88 -0
- package/dist/test/client.conflictFarm.spec.js.map +1 -0
- package/dist/test/client.getPosition.spec.d.ts +6 -0
- package/dist/test/client.getPosition.spec.d.ts.map +1 -0
- package/dist/test/client.getPosition.spec.js +54 -0
- package/dist/test/client.getPosition.spec.js.map +1 -0
- package/dist/test/client.localReference.spec.d.ts +6 -0
- package/dist/test/client.localReference.spec.d.ts.map +1 -0
- package/dist/test/client.localReference.spec.js +439 -0
- package/dist/test/client.localReference.spec.js.map +1 -0
- package/dist/test/client.localReferenceFarm.spec.d.ts +6 -0
- package/dist/test/client.localReferenceFarm.spec.d.ts.map +1 -0
- package/dist/test/client.localReferenceFarm.spec.js +88 -0
- package/dist/test/client.localReferenceFarm.spec.js.map +1 -0
- package/dist/test/client.rebasePosition.spec.d.ts +6 -0
- package/dist/test/client.rebasePosition.spec.d.ts.map +1 -0
- package/dist/test/client.rebasePosition.spec.js +102 -0
- package/dist/test/client.rebasePosition.spec.js.map +1 -0
- package/dist/test/client.reconnectFarm.spec.d.ts +12 -0
- package/dist/test/client.reconnectFarm.spec.d.ts.map +1 -0
- package/dist/test/client.reconnectFarm.spec.js +91 -0
- package/dist/test/client.reconnectFarm.spec.js.map +1 -0
- package/dist/test/client.replay.spec.d.ts +6 -0
- package/dist/test/client.replay.spec.d.ts.map +1 -0
- package/dist/test/client.replay.spec.js +85 -0
- package/dist/test/client.replay.spec.js.map +1 -0
- package/dist/test/client.rollback.spec.d.ts +6 -0
- package/dist/test/client.rollback.spec.d.ts.map +1 -0
- package/dist/test/client.rollback.spec.js +453 -0
- package/dist/test/client.rollback.spec.js.map +1 -0
- package/dist/test/client.rollbackFarm.spec.d.ts +6 -0
- package/dist/test/client.rollbackFarm.spec.d.ts.map +1 -0
- package/dist/test/client.rollbackFarm.spec.js +48 -0
- package/dist/test/client.rollbackFarm.spec.js.map +1 -0
- package/dist/test/client.searchForMarker.spec.d.ts +6 -0
- package/dist/test/client.searchForMarker.spec.d.ts.map +1 -0
- package/dist/test/client.searchForMarker.spec.js +446 -0
- package/dist/test/client.searchForMarker.spec.js.map +1 -0
- package/dist/test/client.walkSegments.spec.d.ts +6 -0
- package/dist/test/client.walkSegments.spec.d.ts.map +1 -0
- package/dist/test/client.walkSegments.spec.js +54 -0
- package/dist/test/client.walkSegments.spec.js.map +1 -0
- package/dist/test/collections.list.spec.d.ts +6 -0
- package/dist/test/collections.list.spec.d.ts.map +1 -0
- package/dist/test/collections.list.spec.js +84 -0
- package/dist/test/collections.list.spec.js.map +1 -0
- package/dist/test/createInsertOnlyAttributionPolicy.spec.d.ts +6 -0
- package/dist/test/createInsertOnlyAttributionPolicy.spec.d.ts.map +1 -0
- package/dist/test/createInsertOnlyAttributionPolicy.spec.js +35 -0
- package/dist/test/createInsertOnlyAttributionPolicy.spec.js.map +1 -0
- package/dist/test/index.d.ts +13 -0
- package/dist/test/index.d.ts.map +1 -0
- package/dist/test/index.js +88 -0
- package/dist/test/index.js.map +1 -0
- package/dist/test/mergeTree.annotate.deltaCallback.spec.d.ts +6 -0
- package/dist/test/mergeTree.annotate.deltaCallback.spec.d.ts.map +1 -0
- package/dist/test/mergeTree.annotate.deltaCallback.spec.js +142 -0
- package/dist/test/mergeTree.annotate.deltaCallback.spec.js.map +1 -0
- package/dist/test/mergeTree.annotate.spec.d.ts +6 -0
- package/dist/test/mergeTree.annotate.spec.d.ts.map +1 -0
- package/dist/test/mergeTree.annotate.spec.js +448 -0
- package/dist/test/mergeTree.annotate.spec.js.map +1 -0
- package/dist/test/mergeTree.insert.deltaCallback.spec.d.ts +6 -0
- package/dist/test/mergeTree.insert.deltaCallback.spec.d.ts.map +1 -0
- package/dist/test/mergeTree.insert.deltaCallback.spec.js +126 -0
- package/dist/test/mergeTree.insert.deltaCallback.spec.js.map +1 -0
- package/dist/test/mergeTree.insertingWalk.spec.d.ts +6 -0
- package/dist/test/mergeTree.insertingWalk.spec.d.ts.map +1 -0
- package/dist/test/mergeTree.insertingWalk.spec.js +279 -0
- package/dist/test/mergeTree.insertingWalk.spec.js.map +1 -0
- package/dist/test/mergeTree.markRangeRemoved.deltaCallback.spec.d.ts +6 -0
- package/dist/test/mergeTree.markRangeRemoved.deltaCallback.spec.d.ts.map +1 -0
- package/dist/test/mergeTree.markRangeRemoved.deltaCallback.spec.js +178 -0
- package/dist/test/mergeTree.markRangeRemoved.deltaCallback.spec.js.map +1 -0
- package/dist/test/mergeTree.markRangeRemoved.spec.d.ts +6 -0
- package/dist/test/mergeTree.markRangeRemoved.spec.d.ts.map +1 -0
- package/dist/test/mergeTree.markRangeRemoved.spec.js +130 -0
- package/dist/test/mergeTree.markRangeRemoved.spec.js.map +1 -0
- package/dist/test/mergeTree.walk.spec.d.ts +6 -0
- package/dist/test/mergeTree.walk.spec.d.ts.map +1 -0
- package/dist/test/mergeTree.walk.spec.js +63 -0
- package/dist/test/mergeTree.walk.spec.js.map +1 -0
- package/dist/test/mergeTree.zamboni.spec.d.ts +6 -0
- package/dist/test/mergeTree.zamboni.spec.d.ts.map +1 -0
- package/dist/test/mergeTree.zamboni.spec.js +52 -0
- package/dist/test/mergeTree.zamboni.spec.js.map +1 -0
- package/dist/test/mergeTreeOperationRunner.d.ts +63 -0
- package/dist/test/mergeTreeOperationRunner.d.ts.map +1 -0
- package/dist/test/mergeTreeOperationRunner.js +245 -0
- package/dist/test/mergeTreeOperationRunner.js.map +1 -0
- package/dist/test/mergeTreeOperationRunner.spec.d.ts +6 -0
- package/dist/test/mergeTreeOperationRunner.spec.d.ts.map +1 -0
- package/dist/test/mergeTreeOperationRunner.spec.js +156 -0
- package/dist/test/mergeTreeOperationRunner.spec.js.map +1 -0
- package/dist/test/obliterate.concurrent.spec.d.ts +6 -0
- package/dist/test/obliterate.concurrent.spec.d.ts.map +1 -0
- package/dist/test/obliterate.concurrent.spec.js +1446 -0
- package/dist/test/obliterate.concurrent.spec.js.map +1 -0
- package/dist/test/obliterate.partialLength.spec.d.ts +6 -0
- package/dist/test/obliterate.partialLength.spec.d.ts.map +1 -0
- package/dist/test/obliterate.partialLength.spec.js +279 -0
- package/dist/test/obliterate.partialLength.spec.js.map +1 -0
- package/dist/test/obliterate.reconnect.spec.d.ts +6 -0
- package/dist/test/obliterate.reconnect.spec.d.ts.map +1 -0
- package/dist/test/obliterate.reconnect.spec.js +164 -0
- package/dist/test/obliterate.reconnect.spec.js.map +1 -0
- package/dist/test/obliterate.spec.d.ts +6 -0
- package/dist/test/obliterate.spec.d.ts.map +1 -0
- package/dist/test/obliterate.spec.js +162 -0
- package/dist/test/obliterate.spec.js.map +1 -0
- package/dist/test/ordinal.spec.d.ts +2 -0
- package/dist/test/ordinal.spec.d.ts.map +1 -0
- package/dist/test/ordinal.spec.js +43 -0
- package/dist/test/ordinal.spec.js.map +1 -0
- package/dist/test/partialLength.spec.d.ts +6 -0
- package/dist/test/partialLength.spec.d.ts.map +1 -0
- package/dist/test/partialLength.spec.js +282 -0
- package/dist/test/partialLength.spec.js.map +1 -0
- package/dist/test/properties.spec.d.ts +6 -0
- package/dist/test/properties.spec.d.ts.map +1 -0
- package/dist/test/properties.spec.js +55 -0
- package/dist/test/properties.spec.js.map +1 -0
- package/dist/test/reconnectHelper.d.ts +48 -0
- package/dist/test/reconnectHelper.d.ts.map +1 -0
- package/dist/test/reconnectHelper.js +86 -0
- package/dist/test/reconnectHelper.js.map +1 -0
- package/dist/test/resetPendingSegmentsToOp.spec.d.ts +6 -0
- package/dist/test/resetPendingSegmentsToOp.spec.d.ts.map +1 -0
- package/dist/test/resetPendingSegmentsToOp.spec.js +218 -0
- package/dist/test/resetPendingSegmentsToOp.spec.js.map +1 -0
- package/dist/test/revertibleFarm.spec.d.ts +6 -0
- package/dist/test/revertibleFarm.spec.d.ts.map +1 -0
- package/dist/test/revertibleFarm.spec.js +124 -0
- package/dist/test/revertibleFarm.spec.js.map +1 -0
- package/dist/test/revertibles.spec.d.ts +17 -0
- package/dist/test/revertibles.spec.d.ts.map +1 -0
- package/dist/test/revertibles.spec.js +385 -0
- package/dist/test/revertibles.spec.js.map +1 -0
- package/dist/test/segmentGroupCollection.spec.d.ts +6 -0
- package/dist/test/segmentGroupCollection.spec.d.ts.map +1 -0
- package/dist/test/segmentGroupCollection.spec.js +60 -0
- package/dist/test/segmentGroupCollection.spec.js.map +1 -0
- package/dist/test/snapshot.spec.d.ts +6 -0
- package/dist/test/snapshot.spec.d.ts.map +1 -0
- package/dist/test/snapshot.spec.js +178 -0
- package/dist/test/snapshot.spec.js.map +1 -0
- package/dist/test/snapshot.utils.d.ts +33 -0
- package/dist/test/snapshot.utils.d.ts.map +1 -0
- package/dist/test/snapshot.utils.js +109 -0
- package/dist/test/snapshot.utils.js.map +1 -0
- package/dist/test/snapshotlegacy.spec.d.ts +6 -0
- package/dist/test/snapshotlegacy.spec.d.ts.map +1 -0
- package/dist/test/snapshotlegacy.spec.js +139 -0
- package/dist/test/snapshotlegacy.spec.js.map +1 -0
- package/dist/test/sortedSegmentSet.spec.d.ts +6 -0
- package/dist/test/sortedSegmentSet.spec.d.ts.map +1 -0
- package/dist/test/sortedSegmentSet.spec.js +95 -0
- package/dist/test/sortedSegmentSet.spec.js.map +1 -0
- package/dist/test/testClient.d.ts +119 -0
- package/dist/test/testClient.d.ts.map +1 -0
- package/dist/test/testClient.js +439 -0
- package/dist/test/testClient.js.map +1 -0
- package/dist/test/testClientLogger.d.ts +44 -0
- package/dist/test/testClientLogger.d.ts.map +1 -0
- package/dist/test/testClientLogger.js +287 -0
- package/dist/test/testClientLogger.js.map +1 -0
- package/dist/test/testSerializer.d.ts +18 -0
- package/dist/test/testSerializer.d.ts.map +1 -0
- package/dist/test/testSerializer.js +33 -0
- package/dist/test/testSerializer.js.map +1 -0
- package/dist/test/testServer.d.ts +36 -0
- package/dist/test/testServer.d.ts.map +1 -0
- package/dist/test/testServer.js +138 -0
- package/dist/test/testServer.js.map +1 -0
- package/dist/test/testUtils.d.ts +69 -0
- package/dist/test/testUtils.d.ts.map +1 -0
- package/dist/test/testUtils.js +149 -0
- package/dist/test/testUtils.js.map +1 -0
- package/dist/test/text.d.ts +9 -0
- package/dist/test/text.d.ts.map +1 -0
- package/dist/test/text.js +76 -0
- package/dist/test/text.js.map +1 -0
- package/dist/test/tracking.spec.d.ts +6 -0
- package/dist/test/tracking.spec.d.ts.map +1 -0
- package/dist/test/tracking.spec.js +120 -0
- package/dist/test/tracking.spec.js.map +1 -0
- package/dist/test/wordUnitTests.d.ts +6 -0
- package/dist/test/wordUnitTests.d.ts.map +1 -0
- package/dist/test/wordUnitTests.js +172 -0
- package/dist/test/wordUnitTests.js.map +1 -0
- package/lib/{MergeTreeTextHelper.d.ts → MergeTreeTextHelper.d.mts} +3 -3
- package/lib/MergeTreeTextHelper.d.mts.map +1 -0
- package/lib/{MergeTreeTextHelper.js → MergeTreeTextHelper.mjs} +5 -10
- package/lib/MergeTreeTextHelper.mjs.map +1 -0
- package/lib/{attributionCollection.d.ts → attributionCollection.d.mts} +2 -2
- package/lib/attributionCollection.d.mts.map +1 -0
- package/lib/{attributionCollection.js → attributionCollection.mjs} +9 -14
- package/lib/attributionCollection.mjs.map +1 -0
- package/lib/{attributionPolicy.d.ts → attributionPolicy.d.mts} +2 -2
- package/lib/attributionPolicy.d.mts.map +1 -0
- package/lib/{attributionPolicy.js → attributionPolicy.mjs} +21 -27
- package/lib/attributionPolicy.mjs.map +1 -0
- package/lib/{client.d.ts → client.d.mts} +9 -16
- package/lib/client.d.mts.map +1 -0
- package/lib/{client.js → client.mjs} +101 -110
- package/lib/client.mjs.map +1 -0
- package/lib/collections/{index.d.ts → index.d.mts} +3 -3
- package/lib/collections/index.d.mts.map +1 -0
- package/lib/collections/index.mjs +7 -0
- package/lib/collections/index.mjs.map +1 -0
- package/lib/collections/{list.d.ts → list.d.mts} +1 -1
- package/lib/collections/list.d.mts.map +1 -0
- package/lib/collections/{list.js → list.mjs} +6 -11
- package/lib/collections/list.mjs.map +1 -0
- package/lib/collections/{rbTree.d.ts → rbTree.d.mts} +1 -1
- package/lib/collections/rbTree.d.mts.map +1 -0
- package/lib/collections/{rbTree.js → rbTree.mjs} +16 -20
- package/lib/collections/rbTree.mjs.map +1 -0
- package/lib/{constants.d.ts → constants.d.mts} +1 -1
- package/lib/constants.d.mts.map +1 -0
- package/lib/constants.mjs +32 -0
- package/lib/constants.mjs.map +1 -0
- package/lib/{endOfTreeSegment.d.ts → endOfTreeSegment.d.mts} +4 -4
- package/lib/endOfTreeSegment.d.mts.map +1 -0
- package/lib/{endOfTreeSegment.js → endOfTreeSegment.mjs} +13 -18
- package/lib/endOfTreeSegment.mjs.map +1 -0
- package/lib/{index.d.ts → index.d.mts} +21 -21
- package/lib/index.d.mts.map +1 -0
- package/lib/index.mjs +24 -0
- package/lib/index.mjs.map +1 -0
- package/lib/{localReference.d.ts → localReference.d.mts} +7 -7
- package/lib/localReference.d.mts.map +1 -0
- package/lib/{localReference.js → localReference.mjs} +38 -47
- package/lib/localReference.mjs.map +1 -0
- package/lib/{merge-tree-alpha.d.ts → merge-tree-alpha.d.mts} +27 -12
- package/lib/{merge-tree-beta.d.ts → merge-tree-beta.d.mts} +0 -16
- package/lib/{merge-tree-public.d.ts → merge-tree-public.d.mts} +0 -16
- package/lib/{merge-tree-untrimmed.d.ts → merge-tree-untrimmed.d.mts} +5 -29
- package/lib/{mergeTree.d.ts → mergeTree.d.mts} +12 -29
- package/lib/mergeTree.d.mts.map +1 -0
- package/lib/{mergeTree.js → mergeTree.mjs} +203 -340
- package/lib/mergeTree.mjs.map +1 -0
- package/lib/{mergeTreeDeltaCallback.d.ts → mergeTreeDeltaCallback.d.mts} +4 -8
- package/lib/mergeTreeDeltaCallback.d.mts.map +1 -0
- package/lib/{mergeTreeDeltaCallback.js → mergeTreeDeltaCallback.mjs} +2 -5
- package/lib/mergeTreeDeltaCallback.mjs.map +1 -0
- package/lib/{mergeTreeNodeWalk.d.ts → mergeTreeNodeWalk.d.mts} +2 -2
- package/lib/mergeTreeNodeWalk.d.mts.map +1 -0
- package/lib/{mergeTreeNodeWalk.js → mergeTreeNodeWalk.mjs} +14 -21
- package/lib/mergeTreeNodeWalk.mjs.map +1 -0
- package/lib/{mergeTreeNodes.d.ts → mergeTreeNodes.d.mts} +12 -12
- package/lib/mergeTreeNodes.d.mts.map +1 -0
- package/lib/{mergeTreeNodes.js → mergeTreeNodes.mjs} +60 -76
- package/lib/mergeTreeNodes.mjs.map +1 -0
- package/lib/{mergeTreeTracking.d.ts → mergeTreeTracking.d.mts} +3 -3
- package/lib/mergeTreeTracking.d.mts.map +1 -0
- package/lib/{mergeTreeTracking.js → mergeTreeTracking.mjs} +6 -13
- package/lib/mergeTreeTracking.mjs.map +1 -0
- package/lib/{opBuilder.d.ts → opBuilder.d.mts} +4 -4
- package/lib/opBuilder.d.mts.map +1 -0
- package/lib/{opBuilder.js → opBuilder.mjs} +15 -25
- package/lib/opBuilder.mjs.map +1 -0
- package/lib/{ops.d.ts → ops.d.mts} +2 -2
- package/lib/ops.d.mts.map +1 -0
- package/lib/{ops.js → ops.mjs} +5 -8
- package/lib/ops.mjs.map +1 -0
- package/lib/{ordinal.d.ts → ordinal.d.mts} +1 -1
- package/lib/ordinal.d.mts.map +1 -0
- package/lib/{ordinal.js → ordinal.mjs} +4 -9
- package/lib/ordinal.mjs.map +1 -0
- package/lib/{partialLengths.d.ts → partialLengths.d.mts} +4 -4
- package/lib/partialLengths.d.mts.map +1 -0
- package/lib/{partialLengths.js → partialLengths.mjs} +38 -46
- package/lib/partialLengths.mjs.map +1 -0
- package/lib/{properties.d.ts → properties.d.mts} +1 -1
- package/lib/properties.d.mts.map +1 -0
- package/lib/{properties.js → properties.mjs} +7 -16
- package/lib/properties.mjs.map +1 -0
- package/lib/{referencePositions.d.ts → referencePositions.d.mts} +5 -5
- package/lib/referencePositions.d.mts.map +1 -0
- package/lib/referencePositions.mjs +70 -0
- package/lib/referencePositions.mjs.map +1 -0
- package/lib/{revertibles.d.ts → revertibles.d.mts} +12 -12
- package/lib/revertibles.d.mts.map +1 -0
- package/lib/{revertibles.js → revertibles.mjs} +60 -67
- package/lib/revertibles.mjs.map +1 -0
- package/lib/{segmentGroupCollection.d.ts → segmentGroupCollection.d.mts} +2 -2
- package/lib/segmentGroupCollection.d.mts.map +1 -0
- package/lib/{segmentGroupCollection.js → segmentGroupCollection.mjs} +5 -9
- package/lib/segmentGroupCollection.mjs.map +1 -0
- package/lib/{segmentPropertiesManager.d.ts → segmentPropertiesManager.d.mts} +3 -3
- package/lib/segmentPropertiesManager.d.mts.map +1 -0
- package/lib/{segmentPropertiesManager.js → segmentPropertiesManager.mjs} +14 -20
- package/lib/{segmentPropertiesManager.js.map → segmentPropertiesManager.mjs.map} +1 -1
- package/lib/{snapshotChunks.d.ts → snapshotChunks.d.mts} +4 -4
- package/lib/snapshotChunks.d.mts.map +1 -0
- package/lib/{snapshotChunks.js → snapshotChunks.mjs} +10 -17
- package/lib/snapshotChunks.mjs.map +1 -0
- package/lib/{snapshotLoader.d.ts → snapshotLoader.d.mts} +3 -3
- package/lib/snapshotLoader.d.mts.map +1 -0
- package/lib/{snapshotLoader.js → snapshotLoader.mjs} +33 -38
- package/lib/snapshotLoader.mjs.map +1 -0
- package/lib/{snapshotV1.d.ts → snapshotV1.d.mts} +4 -4
- package/lib/snapshotV1.d.mts.map +1 -0
- package/lib/{snapshotV1.js → snapshotV1.mjs} +28 -32
- package/lib/snapshotV1.mjs.map +1 -0
- package/lib/{snapshotlegacy.d.ts → snapshotlegacy.d.mts} +3 -3
- package/lib/snapshotlegacy.d.mts.map +1 -0
- package/lib/{snapshotlegacy.js → snapshotlegacy.mjs} +21 -26
- package/lib/snapshotlegacy.mjs.map +1 -0
- package/lib/{sortedSegmentSet.d.ts → sortedSegmentSet.d.mts} +4 -4
- package/lib/sortedSegmentSet.d.mts.map +1 -0
- package/lib/{sortedSegmentSet.js → sortedSegmentSet.mjs} +3 -8
- package/lib/sortedSegmentSet.mjs.map +1 -0
- package/lib/{sortedSet.d.ts → sortedSet.d.mts} +1 -1
- package/lib/sortedSet.d.mts.map +1 -0
- package/lib/{sortedSet.js → sortedSet.mjs} +2 -6
- package/lib/sortedSet.mjs.map +1 -0
- package/lib/{textSegment.d.ts → textSegment.d.mts} +4 -4
- package/lib/textSegment.d.mts.map +1 -0
- package/lib/{textSegment.js → textSegment.mjs} +8 -12
- package/lib/textSegment.mjs.map +1 -0
- package/lib/{zamboni.d.ts → zamboni.d.mts} +3 -3
- package/lib/zamboni.d.mts.map +1 -0
- package/lib/{zamboni.js → zamboni.mjs} +22 -28
- package/lib/zamboni.mjs.map +1 -0
- package/package.json +95 -164
- package/src/client.ts +0 -8
- package/src/mergeTree.ts +0 -226
- package/src/ops.ts +1 -1
- package/src/revertibles.ts +4 -4
- package/lib/MergeTreeTextHelper.d.ts.map +0 -1
- package/lib/MergeTreeTextHelper.js.map +0 -1
- package/lib/attributionCollection.d.ts.map +0 -1
- package/lib/attributionCollection.js.map +0 -1
- package/lib/attributionPolicy.d.ts.map +0 -1
- package/lib/attributionPolicy.js.map +0 -1
- package/lib/client.d.ts.map +0 -1
- package/lib/client.js.map +0 -1
- package/lib/collections/index.d.ts.map +0 -1
- package/lib/collections/index.js +0 -14
- package/lib/collections/index.js.map +0 -1
- package/lib/collections/list.d.ts.map +0 -1
- package/lib/collections/list.js.map +0 -1
- package/lib/collections/rbTree.d.ts.map +0 -1
- package/lib/collections/rbTree.js.map +0 -1
- package/lib/constants.d.ts.map +0 -1
- package/lib/constants.js +0 -35
- package/lib/constants.js.map +0 -1
- package/lib/endOfTreeSegment.d.ts.map +0 -1
- package/lib/endOfTreeSegment.js.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js +0 -80
- package/lib/index.js.map +0 -1
- package/lib/localReference.d.ts.map +0 -1
- package/lib/localReference.js.map +0 -1
- package/lib/mergeTree.d.ts.map +0 -1
- package/lib/mergeTree.js.map +0 -1
- package/lib/mergeTreeDeltaCallback.d.ts.map +0 -1
- package/lib/mergeTreeDeltaCallback.js.map +0 -1
- package/lib/mergeTreeNodeWalk.d.ts.map +0 -1
- package/lib/mergeTreeNodeWalk.js.map +0 -1
- package/lib/mergeTreeNodes.d.ts.map +0 -1
- package/lib/mergeTreeNodes.js.map +0 -1
- package/lib/mergeTreeTracking.d.ts.map +0 -1
- package/lib/mergeTreeTracking.js.map +0 -1
- package/lib/opBuilder.d.ts.map +0 -1
- package/lib/opBuilder.js.map +0 -1
- package/lib/ops.d.ts.map +0 -1
- package/lib/ops.js.map +0 -1
- package/lib/ordinal.d.ts.map +0 -1
- package/lib/ordinal.js.map +0 -1
- package/lib/partialLengths.d.ts.map +0 -1
- package/lib/partialLengths.js.map +0 -1
- package/lib/properties.d.ts.map +0 -1
- package/lib/properties.js.map +0 -1
- package/lib/referencePositions.d.ts.map +0 -1
- package/lib/referencePositions.js +0 -80
- package/lib/referencePositions.js.map +0 -1
- package/lib/revertibles.d.ts.map +0 -1
- package/lib/revertibles.js.map +0 -1
- package/lib/segmentGroupCollection.d.ts.map +0 -1
- package/lib/segmentGroupCollection.js.map +0 -1
- package/lib/segmentPropertiesManager.d.ts.map +0 -1
- package/lib/snapshotChunks.d.ts.map +0 -1
- package/lib/snapshotChunks.js.map +0 -1
- package/lib/snapshotLoader.d.ts.map +0 -1
- package/lib/snapshotLoader.js.map +0 -1
- package/lib/snapshotV1.d.ts.map +0 -1
- package/lib/snapshotV1.js.map +0 -1
- package/lib/snapshotlegacy.d.ts.map +0 -1
- package/lib/snapshotlegacy.js.map +0 -1
- package/lib/sortedSegmentSet.d.ts.map +0 -1
- package/lib/sortedSegmentSet.js.map +0 -1
- package/lib/sortedSet.d.ts.map +0 -1
- package/lib/sortedSet.js.map +0 -1
- package/lib/textSegment.d.ts.map +0 -1
- package/lib/textSegment.js.map +0 -1
- package/lib/zamboni.d.ts.map +0 -1
- package/lib/zamboni.js.map +0 -1
- package/merge-tree.test-files.tar +0 -0
- package/src/mergeTreeExample1.pdf +0 -0
- package/tsconfig.esnext.json +0 -6
|
@@ -0,0 +1,1446 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*!
|
|
3
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
4
|
+
* Licensed under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const assert_1 = require("assert");
|
|
8
|
+
const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
|
|
9
|
+
const partialLengths_1 = require("../partialLengths");
|
|
10
|
+
const mergeTree_1 = require("../mergeTree");
|
|
11
|
+
const reconnectHelper_1 = require("./reconnectHelper");
|
|
12
|
+
/**
|
|
13
|
+
* Some tests contain ASCII diagrams of the trees to make it easier to reason about
|
|
14
|
+
* structure. The format of these diagrams is this:
|
|
15
|
+
* - segments are separated by `-`.
|
|
16
|
+
* - removed ranges are surrounded by `[` ... `]`
|
|
17
|
+
* - obliterated ranges are surrounded by `(` ... `)`
|
|
18
|
+
*
|
|
19
|
+
* In cases where these ranges are ambiguous, there are additional diagrams
|
|
20
|
+
* containing `v` and `-`, with the `v` denoting the start and end of the range
|
|
21
|
+
*
|
|
22
|
+
* E.g.
|
|
23
|
+
*
|
|
24
|
+
* ```
|
|
25
|
+
* v---v
|
|
26
|
+
* [ABC]
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* This diagram describes a single segment, `ABC`, that has been removed.
|
|
30
|
+
*
|
|
31
|
+
*
|
|
32
|
+
* ```
|
|
33
|
+
* v-----v
|
|
34
|
+
* v---v
|
|
35
|
+
* [[ABC]]
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* This diagram describes a single segment, `ABC`, that has been concurrently
|
|
39
|
+
* removed by two clients.
|
|
40
|
+
*/
|
|
41
|
+
for (const incremental of [true, false]) {
|
|
42
|
+
describe(`obliterate partial lengths incremental = ${incremental}`, () => {
|
|
43
|
+
beforeEach(() => {
|
|
44
|
+
partialLengths_1.PartialSequenceLengths.options.verifier = partialLengths_1.verify;
|
|
45
|
+
partialLengths_1.PartialSequenceLengths.options.verifyExpected = partialLengths_1.verifyExpected;
|
|
46
|
+
mergeTree_1.MergeTree.options.incrementalUpdate = incremental;
|
|
47
|
+
});
|
|
48
|
+
afterEach(() => {
|
|
49
|
+
partialLengths_1.PartialSequenceLengths.options.verifier = undefined;
|
|
50
|
+
partialLengths_1.PartialSequenceLengths.options.verifyExpected = undefined;
|
|
51
|
+
mergeTree_1.MergeTree.options.incrementalUpdate = true;
|
|
52
|
+
});
|
|
53
|
+
it("length of children does not differ from parent when overlapping remove+obliterate", () => {
|
|
54
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
55
|
+
// ABCDEFGH
|
|
56
|
+
// I-[J]-(KLM-[ABC]-D-123456-E-[FG]-H)
|
|
57
|
+
helper.insertText("A", 0, "ABCDEFGH");
|
|
58
|
+
helper.processAllOps();
|
|
59
|
+
helper.removeRange("C", 0, 3);
|
|
60
|
+
helper.insertText("C", 1, "123456");
|
|
61
|
+
helper.removeRange("A", 5, 7);
|
|
62
|
+
helper.insertText("A", 0, "IJKLM");
|
|
63
|
+
helper.obliterateRange("A", 2, 11);
|
|
64
|
+
helper.removeRange("A", 1, 2);
|
|
65
|
+
helper.processAllOps();
|
|
66
|
+
assert_1.strict.equal(helper.clients.A.getText(), "I");
|
|
67
|
+
helper.logger.validate();
|
|
68
|
+
});
|
|
69
|
+
it("deletes concurrent insert that occurs after obliterate", () => {
|
|
70
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
71
|
+
helper.insertText("B", 0, "ABCD");
|
|
72
|
+
helper.processAllOps();
|
|
73
|
+
helper.obliterateRange("B", 0, 4);
|
|
74
|
+
helper.insertText("C", 2, "X");
|
|
75
|
+
helper.processAllOps();
|
|
76
|
+
assert_1.strict.equal(helper.clients.A.getText(), "");
|
|
77
|
+
assert_1.strict.equal(helper.clients.C.getText(), "");
|
|
78
|
+
helper.logger.validate();
|
|
79
|
+
});
|
|
80
|
+
it("deletes concurrent insert that occurs before obliterate", () => {
|
|
81
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
82
|
+
helper.insertText("B", 0, "ABCD");
|
|
83
|
+
helper.processAllOps();
|
|
84
|
+
helper.insertText("C", 2, "X");
|
|
85
|
+
helper.obliterateRange("B", 0, 4);
|
|
86
|
+
helper.processAllOps();
|
|
87
|
+
assert_1.strict.equal(helper.clients.A.getText(), "");
|
|
88
|
+
assert_1.strict.equal(helper.clients.C.getText(), "");
|
|
89
|
+
helper.logger.validate();
|
|
90
|
+
});
|
|
91
|
+
it("does not delete unacked segment at start of string", () => {
|
|
92
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
93
|
+
helper.insertText("C", 0, "ABC");
|
|
94
|
+
helper.obliterateRange("C", 2, 3);
|
|
95
|
+
helper.insertText("B", 0, "X");
|
|
96
|
+
helper.processAllOps();
|
|
97
|
+
assert_1.strict.equal(helper.clients.A.getText(), "XAB");
|
|
98
|
+
assert_1.strict.equal(helper.clients.B.getText(), "XAB");
|
|
99
|
+
assert_1.strict.equal(helper.clients.C.getText(), "XAB");
|
|
100
|
+
helper.logger.validate();
|
|
101
|
+
});
|
|
102
|
+
it("throws when local obliterate has range end outside length of local string", () => {
|
|
103
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
104
|
+
helper.insertText("B", 0, "A");
|
|
105
|
+
helper.insertText("C", 0, "B");
|
|
106
|
+
try {
|
|
107
|
+
helper.obliterateRange("C", 0, 2);
|
|
108
|
+
assert_1.strict.fail("should not be possible to obliterate outside local range");
|
|
109
|
+
}
|
|
110
|
+
catch (e) {
|
|
111
|
+
(0, assert_1.strict)(e instanceof telemetry_utils_1.LoggingError);
|
|
112
|
+
assert_1.strict.equal(e.message, "RangeOutOfBounds");
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
it("does not delete when obliterate immediately after insert", () => {
|
|
116
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
117
|
+
helper.insertText("C", 0, "A");
|
|
118
|
+
helper.obliterateRange("C", 0, 1);
|
|
119
|
+
helper.insertText("B", 0, "W");
|
|
120
|
+
helper.insertText("C", 0, "D");
|
|
121
|
+
helper.obliterateRange("C", 0, 1);
|
|
122
|
+
helper.processAllOps();
|
|
123
|
+
assert_1.strict.equal(helper.clients.A.getText(), "W");
|
|
124
|
+
assert_1.strict.equal(helper.clients.B.getText(), "W");
|
|
125
|
+
assert_1.strict.equal(helper.clients.C.getText(), "W");
|
|
126
|
+
helper.logger.validate();
|
|
127
|
+
});
|
|
128
|
+
it("does not delete remote insert when between local insert+obliterate", () => {
|
|
129
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
130
|
+
helper.insertText("C", 0, "A");
|
|
131
|
+
helper.insertText("B", 0, "X");
|
|
132
|
+
helper.obliterateRange("C", 0, 1);
|
|
133
|
+
helper.insertText("C", 0, "B");
|
|
134
|
+
helper.obliterateRange("C", 0, 1);
|
|
135
|
+
helper.processAllOps();
|
|
136
|
+
assert_1.strict.equal(helper.clients.A.getText(), "X");
|
|
137
|
+
assert_1.strict.equal(helper.clients.B.getText(), "X");
|
|
138
|
+
assert_1.strict.equal(helper.clients.C.getText(), "X");
|
|
139
|
+
helper.logger.validate();
|
|
140
|
+
});
|
|
141
|
+
it("does not delete remote insert when between local insert+obliterate", () => {
|
|
142
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
143
|
+
helper.insertText("C", 0, "A");
|
|
144
|
+
helper.obliterateRange("C", 0, 1);
|
|
145
|
+
helper.insertText("B", 0, "B");
|
|
146
|
+
helper.insertText("C", 0, "X");
|
|
147
|
+
helper.obliterateRange("B", 0, 1);
|
|
148
|
+
helper.processAllOps();
|
|
149
|
+
assert_1.strict.equal(helper.clients.A.getText(), "X");
|
|
150
|
+
assert_1.strict.equal(helper.clients.B.getText(), "X");
|
|
151
|
+
assert_1.strict.equal(helper.clients.C.getText(), "X");
|
|
152
|
+
helper.logger.validate();
|
|
153
|
+
});
|
|
154
|
+
it("does not delete remote insert when in middle of segment", () => {
|
|
155
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
156
|
+
helper.insertText("C", 0, "ABC");
|
|
157
|
+
helper.obliterateRange("C", 2, 3);
|
|
158
|
+
helper.obliterateRange("C", 0, 1);
|
|
159
|
+
helper.insertText("B", 0, "X");
|
|
160
|
+
helper.processAllOps();
|
|
161
|
+
assert_1.strict.equal(helper.clients.A.getText(), "XB");
|
|
162
|
+
assert_1.strict.equal(helper.clients.B.getText(), "XB");
|
|
163
|
+
assert_1.strict.equal(helper.clients.C.getText(), "XB");
|
|
164
|
+
helper.logger.validate();
|
|
165
|
+
});
|
|
166
|
+
it("deletes segment inserted into locally obliterated segment", () => {
|
|
167
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
168
|
+
helper.insertText("C", 0, "A");
|
|
169
|
+
helper.insertText("B", 0, "X");
|
|
170
|
+
helper.insertText("C", 0, "B");
|
|
171
|
+
helper.obliterateRange("C", 0, 2);
|
|
172
|
+
helper.processAllOps();
|
|
173
|
+
assert_1.strict.equal(helper.clients.A.getText(), "");
|
|
174
|
+
assert_1.strict.equal(helper.clients.B.getText(), "");
|
|
175
|
+
assert_1.strict.equal(helper.clients.C.getText(), "");
|
|
176
|
+
helper.logger.validate();
|
|
177
|
+
});
|
|
178
|
+
it("updates lengths after obliterated insertion", () => {
|
|
179
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
180
|
+
helper.insertText("C", 0, "A");
|
|
181
|
+
helper.insertText("B", 0, "X");
|
|
182
|
+
helper.insertText("C", 0, "N");
|
|
183
|
+
helper.obliterateRange("C", 0, 2);
|
|
184
|
+
helper.insertText("B", 1, "B");
|
|
185
|
+
helper.processAllOps();
|
|
186
|
+
assert_1.strict.equal(helper.clients.A.getText(), "");
|
|
187
|
+
assert_1.strict.equal(helper.clients.B.getText(), "");
|
|
188
|
+
assert_1.strict.equal(helper.clients.C.getText(), "");
|
|
189
|
+
assert_1.strict.equal(helper.clients.A.getLength(), 0);
|
|
190
|
+
assert_1.strict.equal(helper.clients.B.getLength(), 0);
|
|
191
|
+
assert_1.strict.equal(helper.clients.C.getLength(), 0);
|
|
192
|
+
helper.logger.validate();
|
|
193
|
+
});
|
|
194
|
+
it("updates lengths when insertion causes tree to split", () => {
|
|
195
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
196
|
+
helper.insertText("A", 0, "0");
|
|
197
|
+
helper.insertText("C", 0, "123");
|
|
198
|
+
helper.insertText("B", 0, "BB");
|
|
199
|
+
helper.insertText("C", 0, "GGG");
|
|
200
|
+
helper.obliterateRange("C", 2, 5);
|
|
201
|
+
helper.insertText("B", 1, "A");
|
|
202
|
+
helper.processAllOps();
|
|
203
|
+
assert_1.strict.equal(helper.clients.A.getText().length, helper.clients.A.getLength());
|
|
204
|
+
assert_1.strict.equal(helper.clients.B.getText().length, helper.clients.B.getLength());
|
|
205
|
+
assert_1.strict.equal(helper.clients.C.getText().length, helper.clients.C.getLength());
|
|
206
|
+
assert_1.strict.equal(helper.clients.A.getText(), "GG30");
|
|
207
|
+
helper.logger.validate();
|
|
208
|
+
});
|
|
209
|
+
it("length of node split by insertion does not count remotely obliterated segments", () => {
|
|
210
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
211
|
+
helper.insertText("A", 0, "1");
|
|
212
|
+
helper.insertText("A", 0, "2");
|
|
213
|
+
helper.insertText("C", 0, "XXXX");
|
|
214
|
+
helper.insertText("B", 0, "ABC");
|
|
215
|
+
helper.insertText("C", 0, "GGG");
|
|
216
|
+
helper.obliterateRange("C", 2, 6);
|
|
217
|
+
helper.insertText("C", 1, "D");
|
|
218
|
+
helper.processAllOps();
|
|
219
|
+
assert_1.strict.equal(helper.clients.A.getText(), "GDGX21");
|
|
220
|
+
assert_1.strict.equal(helper.clients.C.getText(), "GDGX21");
|
|
221
|
+
helper.logger.validate();
|
|
222
|
+
});
|
|
223
|
+
it("length of node split by obliterate does not count remotely obliterated segments", () => {
|
|
224
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
225
|
+
helper.insertText("A", 0, "1");
|
|
226
|
+
helper.insertText("A", 0, "2");
|
|
227
|
+
helper.insertText("C", 0, "XXXX");
|
|
228
|
+
helper.insertText("B", 0, "A");
|
|
229
|
+
helper.insertText("C", 0, "GGG");
|
|
230
|
+
helper.obliterateRange("C", 2, 6);
|
|
231
|
+
helper.insertText("C", 1, "C");
|
|
232
|
+
helper.insertText("B", 1, "D");
|
|
233
|
+
helper.processAllOps();
|
|
234
|
+
assert_1.strict.equal(helper.clients.A.getText(), "GCGX21");
|
|
235
|
+
assert_1.strict.equal(helper.clients.B.getText(), "GCGX21");
|
|
236
|
+
helper.logger.validate();
|
|
237
|
+
});
|
|
238
|
+
it("counts remotely but not concurrently inserted segments for length when tree is split", () => {
|
|
239
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
240
|
+
// a-b-c-d-e-123
|
|
241
|
+
// (a-b)-c-d-e-1-[2]-3
|
|
242
|
+
helper.insertText("B", 0, "123");
|
|
243
|
+
helper.insertText("C", 0, "e");
|
|
244
|
+
helper.insertText("C", 0, "d");
|
|
245
|
+
helper.insertText("C", 0, "c");
|
|
246
|
+
helper.insertText("C", 0, "b");
|
|
247
|
+
helper.insertText("C", 0, "a");
|
|
248
|
+
helper.processAllOps();
|
|
249
|
+
helper.obliterateRange("B", 0, 2);
|
|
250
|
+
helper.removeRange("B", 4, 5);
|
|
251
|
+
helper.processAllOps();
|
|
252
|
+
assert_1.strict.equal(helper.clients.A.getText(), "cde13");
|
|
253
|
+
assert_1.strict.equal(helper.clients.C.getText(), "cde13");
|
|
254
|
+
helper.logger.validate();
|
|
255
|
+
});
|
|
256
|
+
it("does obliterate X for all clients", () => {
|
|
257
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
258
|
+
helper.insertText("B", 0, "DE");
|
|
259
|
+
helper.obliterateRange("B", 0, 1);
|
|
260
|
+
helper.insertText("A", 0, "X");
|
|
261
|
+
helper.insertText("B", 0, "ABC");
|
|
262
|
+
helper.obliterateRange("B", 2, 4);
|
|
263
|
+
helper.processAllOps();
|
|
264
|
+
assert_1.strict.equal(helper.clients.A.getText(), "AB");
|
|
265
|
+
assert_1.strict.equal(helper.clients.C.getText(), "AB");
|
|
266
|
+
helper.logger.validate();
|
|
267
|
+
});
|
|
268
|
+
it("does not include remote but unacked segments in partial len calculation", () => {
|
|
269
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
270
|
+
// 89-4567-123-X
|
|
271
|
+
// 8-(9-4-w-567-1)-23-Y-X
|
|
272
|
+
helper.insertText("A", 0, "X");
|
|
273
|
+
helper.insertText("C", 0, "123");
|
|
274
|
+
helper.insertText("C", 0, "4567");
|
|
275
|
+
helper.insertText("B", 0, "89");
|
|
276
|
+
helper.processAllOps();
|
|
277
|
+
helper.obliterateRange("C", 1, 7);
|
|
278
|
+
helper.insertText("A", 3, "w");
|
|
279
|
+
helper.insertText("C", 3, "Y");
|
|
280
|
+
helper.processAllOps();
|
|
281
|
+
assert_1.strict.equal(helper.clients.A.getText(), "823YX");
|
|
282
|
+
assert_1.strict.equal(helper.clients.B.getText(), "823YX");
|
|
283
|
+
helper.logger.validate();
|
|
284
|
+
});
|
|
285
|
+
it("correctly accounts for overlapping obliterate", () => {
|
|
286
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
287
|
+
helper.insertText("B", 0, "AB");
|
|
288
|
+
helper.processAllOps();
|
|
289
|
+
helper.obliterateRange("C", 0, 1);
|
|
290
|
+
helper.obliterateRange("B", 0, 1);
|
|
291
|
+
helper.processAllOps();
|
|
292
|
+
assert_1.strict.equal(helper.clients.A.getText(), "B");
|
|
293
|
+
assert_1.strict.equal(helper.clients.B.getText(), "B");
|
|
294
|
+
assert_1.strict.equal(helper.clients.C.getText(), "B");
|
|
295
|
+
helper.logger.validate();
|
|
296
|
+
});
|
|
297
|
+
it("correctly accounts for overlapping obliterate and remove", () => {
|
|
298
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
299
|
+
helper.insertText("B", 0, "AB");
|
|
300
|
+
helper.processAllOps();
|
|
301
|
+
helper.removeRange("C", 0, 1);
|
|
302
|
+
helper.obliterateRange("B", 0, 1);
|
|
303
|
+
helper.processAllOps();
|
|
304
|
+
assert_1.strict.equal(helper.clients.A.getText(), "B");
|
|
305
|
+
assert_1.strict.equal(helper.clients.B.getText(), "B");
|
|
306
|
+
assert_1.strict.equal(helper.clients.C.getText(), "B");
|
|
307
|
+
helper.logger.validate();
|
|
308
|
+
});
|
|
309
|
+
it("clones movedClientIds array during insert", () => {
|
|
310
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
311
|
+
// the bug found here:
|
|
312
|
+
// the X was skipped over by client `A` because it had already been
|
|
313
|
+
// deleted, so its length at refSeq was 0
|
|
314
|
+
//
|
|
315
|
+
// this was due to the movedClientIds array not being properly cloned
|
|
316
|
+
// when marking obliterated during insert
|
|
317
|
+
helper.insertText("C", 0, "ABCD");
|
|
318
|
+
helper.processAllOps();
|
|
319
|
+
helper.insertText("B", 2, "X");
|
|
320
|
+
helper.obliterateRange("A", 1, 3);
|
|
321
|
+
helper.obliterateRange("B", 1, 4);
|
|
322
|
+
helper.processAllOps();
|
|
323
|
+
assert_1.strict.equal(helper.clients.A.getText(), "AD");
|
|
324
|
+
assert_1.strict.equal(helper.clients.B.getText(), "AD");
|
|
325
|
+
assert_1.strict.equal(helper.clients.C.getText(), "AD");
|
|
326
|
+
helper.logger.validate();
|
|
327
|
+
});
|
|
328
|
+
it("client partial lens consider overlapping obliterates", () => {
|
|
329
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
330
|
+
helper.insertText("A", 0, "123");
|
|
331
|
+
helper.insertText("A", 0, "ABCDEF");
|
|
332
|
+
helper.processAllOps();
|
|
333
|
+
helper.obliterateRange("B", 2, 3);
|
|
334
|
+
helper.obliterateRange("C", 1, 4);
|
|
335
|
+
helper.obliterateRange("C", 4, 5);
|
|
336
|
+
helper.processAllOps();
|
|
337
|
+
assert_1.strict.equal(helper.clients.A.getText(), "AEF13");
|
|
338
|
+
assert_1.strict.equal(helper.clients.B.getText(), "AEF13");
|
|
339
|
+
assert_1.strict.equal(helper.clients.C.getText(), "AEF13");
|
|
340
|
+
helper.logger.validate();
|
|
341
|
+
});
|
|
342
|
+
it("client partial lens consider overlapping obliterates", () => {
|
|
343
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
344
|
+
helper.insertText("C", 0, "X");
|
|
345
|
+
helper.insertText("C", 0, "ABCDEFG");
|
|
346
|
+
helper.processAllOps();
|
|
347
|
+
helper.obliterateRange("B", 2, 3);
|
|
348
|
+
helper.obliterateRange("C", 1, 4);
|
|
349
|
+
helper.obliterateRange("C", 2, 3);
|
|
350
|
+
helper.processAllOps();
|
|
351
|
+
assert_1.strict.equal(helper.clients.A.getText(), "AEGX");
|
|
352
|
+
assert_1.strict.equal(helper.clients.C.getText(), "AEGX");
|
|
353
|
+
helper.logger.validate();
|
|
354
|
+
});
|
|
355
|
+
it("tracks obliterate refSeq when acking op for partial len calculation", () => {
|
|
356
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
357
|
+
// v-----------------------v
|
|
358
|
+
// v--v
|
|
359
|
+
// v--v
|
|
360
|
+
// 6-(345-AB-(CD)-E-(FG)-HI-1)-2
|
|
361
|
+
helper.insertText("A", 0, "12");
|
|
362
|
+
helper.insertText("B", 0, "ABCDEFGHI");
|
|
363
|
+
helper.insertText("A", 0, "345");
|
|
364
|
+
helper.obliterateRange("A", 0, 4);
|
|
365
|
+
helper.obliterateRange("B", 2, 4);
|
|
366
|
+
helper.insertText("A", 0, "6");
|
|
367
|
+
helper.obliterateRange("B", 3, 5);
|
|
368
|
+
helper.processAllOps();
|
|
369
|
+
assert_1.strict.equal(helper.clients.A.getText(), "62");
|
|
370
|
+
assert_1.strict.equal(helper.clients.B.getText(), "62");
|
|
371
|
+
helper.logger.validate();
|
|
372
|
+
});
|
|
373
|
+
it("does not have negative len when segment obliterated before insert", () => {
|
|
374
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
375
|
+
// 1234567-D-C-AB
|
|
376
|
+
// 12-([3-X-45]-67)-D-C-AB
|
|
377
|
+
helper.insertText("A", 0, "AB");
|
|
378
|
+
helper.insertText("A", 0, "C");
|
|
379
|
+
helper.insertText("A", 0, "D");
|
|
380
|
+
helper.insertText("A", 0, "1234567");
|
|
381
|
+
helper.processAllOps();
|
|
382
|
+
helper.logger.validate();
|
|
383
|
+
helper.obliterateRange("A", 2, 7);
|
|
384
|
+
helper.removeRange("A", 2, 5);
|
|
385
|
+
helper.insertText("C", 3, "X");
|
|
386
|
+
helper.processAllOps();
|
|
387
|
+
assert_1.strict.equal(helper.clients.A.getText(), "12B");
|
|
388
|
+
assert_1.strict.equal(helper.clients.B.getText(), "12B");
|
|
389
|
+
assert_1.strict.equal(helper.clients.C.getText(), "12B");
|
|
390
|
+
helper.logger.validate();
|
|
391
|
+
});
|
|
392
|
+
it("does not have negative len when segment obliterated before insert", () => {
|
|
393
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
394
|
+
// ABCDE-1-[2]-3
|
|
395
|
+
// (A-XX-B)-(CD)-E-1-3
|
|
396
|
+
helper.insertText("B", 0, "123");
|
|
397
|
+
helper.insertText("C", 0, "ABCDE");
|
|
398
|
+
helper.removeRange("B", 1, 2);
|
|
399
|
+
helper.processAllOps();
|
|
400
|
+
helper.logger.validate();
|
|
401
|
+
helper.obliterateRange("C", 0, 2);
|
|
402
|
+
helper.obliterateRange("C", 0, 2);
|
|
403
|
+
helper.insertText("B", 1, "XX");
|
|
404
|
+
helper.processAllOps();
|
|
405
|
+
assert_1.strict.equal(helper.clients.A.getText(), "E13");
|
|
406
|
+
assert_1.strict.equal(helper.clients.B.getText(), "E13");
|
|
407
|
+
assert_1.strict.equal(helper.clients.C.getText(), "E13");
|
|
408
|
+
helper.logger.validate();
|
|
409
|
+
});
|
|
410
|
+
it("deletes segments between two obliterates with different seq", () => {
|
|
411
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
412
|
+
// 90-8-1234-(5)-67-D-C-B-A
|
|
413
|
+
// 9-(EFG-[0-8-1234-(5)-67)]-D-C-B-A
|
|
414
|
+
helper.insertText("A", 0, "A");
|
|
415
|
+
helper.insertText("C", 0, "B");
|
|
416
|
+
helper.insertText("C", 0, "C");
|
|
417
|
+
helper.insertText("C", 0, "D");
|
|
418
|
+
helper.insertText("B", 0, "1234567");
|
|
419
|
+
helper.obliterateRange("B", 4, 5);
|
|
420
|
+
helper.insertText("A", 0, "8");
|
|
421
|
+
helper.insertText("A", 0, "90");
|
|
422
|
+
helper.processAllOps();
|
|
423
|
+
helper.removeRange("C", 1, 9);
|
|
424
|
+
helper.insertText("A", 1, "EFG");
|
|
425
|
+
helper.obliterateRange("A", 1, 11);
|
|
426
|
+
helper.processAllOps();
|
|
427
|
+
assert_1.strict.equal(helper.clients.A.getText(), "9DCBA");
|
|
428
|
+
helper.logger.validate();
|
|
429
|
+
});
|
|
430
|
+
it("deletes inserted segment when obliterate of different seq in-between", () => {
|
|
431
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
432
|
+
helper.insertText("A", 0, "AB");
|
|
433
|
+
helper.insertText("B", 0, "E");
|
|
434
|
+
helper.obliterateRange("A", 0, 1);
|
|
435
|
+
helper.insertText("A", 1, "12");
|
|
436
|
+
helper.insertText("A", 0, "CD");
|
|
437
|
+
helper.obliterateRange("A", 1, 4);
|
|
438
|
+
helper.processAllOps();
|
|
439
|
+
assert_1.strict.equal(helper.clients.A.getText(), "C2");
|
|
440
|
+
assert_1.strict.equal(helper.clients.B.getText(), "C2");
|
|
441
|
+
assert_1.strict.equal(helper.clients.C.getText(), "C2");
|
|
442
|
+
helper.logger.validate();
|
|
443
|
+
});
|
|
444
|
+
it("deletes inserted segment when obliterate of different seq in-between", () => {
|
|
445
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
446
|
+
helper.insertText("A", 0, "ABC");
|
|
447
|
+
helper.obliterateRange("A", 1, 2);
|
|
448
|
+
helper.processAllOps();
|
|
449
|
+
helper.insertText("A", 1, "D");
|
|
450
|
+
helper.obliterateRange("C", 0, 2);
|
|
451
|
+
helper.processAllOps();
|
|
452
|
+
assert_1.strict.equal(helper.clients.A.getText(), "");
|
|
453
|
+
assert_1.strict.equal(helper.clients.B.getText(), "");
|
|
454
|
+
assert_1.strict.equal(helper.clients.C.getText(), "");
|
|
455
|
+
helper.logger.validate();
|
|
456
|
+
});
|
|
457
|
+
it("deletes inserted segment when obliterate of different seq in-between", () => {
|
|
458
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
459
|
+
helper.insertText("A", 0, "ABC");
|
|
460
|
+
helper.obliterateRange("A", 1, 2);
|
|
461
|
+
helper.processAllOps();
|
|
462
|
+
helper.insertText("A", 1, "D");
|
|
463
|
+
helper.obliterateRange("C", 0, 2);
|
|
464
|
+
helper.processAllOps();
|
|
465
|
+
assert_1.strict.equal(helper.clients.A.getText(), "");
|
|
466
|
+
assert_1.strict.equal(helper.clients.B.getText(), "");
|
|
467
|
+
assert_1.strict.equal(helper.clients.C.getText(), "");
|
|
468
|
+
helper.logger.validate();
|
|
469
|
+
});
|
|
470
|
+
it("considers obliterated local segments as remotely obliterate", () => {
|
|
471
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
472
|
+
// G-(H-F-I-C)-J-DE-A-(B)
|
|
473
|
+
// G-J-(H-F-I-CD)-E
|
|
474
|
+
helper.insertText("A", 0, "AB");
|
|
475
|
+
helper.obliterateRange("A", 1, 2);
|
|
476
|
+
helper.insertText("C", 0, "CDE");
|
|
477
|
+
helper.insertText("B", 0, "F");
|
|
478
|
+
helper.insertText("C", 0, "GH");
|
|
479
|
+
helper.obliterateRange("C", 1, 3);
|
|
480
|
+
helper.insertText("B", 1, "I");
|
|
481
|
+
helper.insertText("C", 1, "J");
|
|
482
|
+
helper.processAllOps();
|
|
483
|
+
assert_1.strict.equal(helper.clients.A.getText(), "GJDEA");
|
|
484
|
+
assert_1.strict.equal(helper.clients.B.getText(), "GJDEA");
|
|
485
|
+
helper.logger.validate();
|
|
486
|
+
});
|
|
487
|
+
it("traverses hier block in obliterated when len at ref seq is >0 and len at len seq == 0", () => {
|
|
488
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
489
|
+
helper.insertText("A", 0, "AB");
|
|
490
|
+
helper.insertText("A", 2, "CD");
|
|
491
|
+
helper.removeRange("A", 1, 3);
|
|
492
|
+
helper.insertText("C", 0, "12345");
|
|
493
|
+
helper.insertText("B", 0, "EFG");
|
|
494
|
+
helper.insertText("B", 1, "HIJKL");
|
|
495
|
+
helper.processAllOps();
|
|
496
|
+
helper.logger.validate();
|
|
497
|
+
helper.obliterateRange("A", 6, 12);
|
|
498
|
+
helper.removeRange("A", 5, 7);
|
|
499
|
+
helper.obliterateRange("C", 7, 9);
|
|
500
|
+
helper.processAllOps();
|
|
501
|
+
assert_1.strict.equal(helper.clients.A.getText(), helper.clients.D.getText());
|
|
502
|
+
assert_1.strict.equal(helper.clients.B.getText(), "EHIJKAD");
|
|
503
|
+
helper.logger.validate();
|
|
504
|
+
});
|
|
505
|
+
it("traverses hier block in obliterate when len at ref seq is >0 and len at len seq == 0", () => {
|
|
506
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
507
|
+
// [E]-FGH-12-[A]-[B]-CD
|
|
508
|
+
// 3-4-F-[G-(H-1)-2]-CD
|
|
509
|
+
helper.insertText("B", 0, "ABCD");
|
|
510
|
+
helper.removeRange("B", 0, 1);
|
|
511
|
+
helper.insertText("C", 0, "12");
|
|
512
|
+
helper.insertText("A", 0, "EFGH");
|
|
513
|
+
helper.removeRange("B", 1, 2);
|
|
514
|
+
helper.removeRange("A", 0, 1);
|
|
515
|
+
helper.processAllOps();
|
|
516
|
+
helper.logger.validate();
|
|
517
|
+
helper.removeRange("A", 1, 5);
|
|
518
|
+
helper.obliterateRange("B", 2, 4);
|
|
519
|
+
helper.insertText("A", 0, "3");
|
|
520
|
+
helper.insertText("A", 0, "4");
|
|
521
|
+
helper.processAllOps();
|
|
522
|
+
assert_1.strict.equal(helper.clients.A.getText(), "43FBD");
|
|
523
|
+
assert_1.strict.equal(helper.clients.B.getText(), "43FBD");
|
|
524
|
+
helper.logger.validate();
|
|
525
|
+
});
|
|
526
|
+
it("ignores segments where movedSeq < seq for partial len calculations", () => {
|
|
527
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
528
|
+
helper.insertText("B", 0, "ABC");
|
|
529
|
+
helper.insertText("A", 0, "DEF");
|
|
530
|
+
helper.removeRange("A", 1, 2);
|
|
531
|
+
helper.insertText("B", 0, "123456");
|
|
532
|
+
helper.obliterateRange("B", 2, 7);
|
|
533
|
+
helper.insertText("A", 1, "Y");
|
|
534
|
+
helper.processAllOps();
|
|
535
|
+
helper.logger.validate();
|
|
536
|
+
helper.insertText("B", 4, "X");
|
|
537
|
+
helper.processAllOps();
|
|
538
|
+
assert_1.strict.equal(helper.clients.A.getText(), "12BCX");
|
|
539
|
+
assert_1.strict.equal(helper.clients.B.getText(), "12BCX");
|
|
540
|
+
assert_1.strict.equal(helper.clients.C.getText(), "12BCX");
|
|
541
|
+
helper.logger.validate();
|
|
542
|
+
});
|
|
543
|
+
it("accounts for overlapping obliterates from same client", () => {
|
|
544
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
545
|
+
helper.insertText("A", 0, "AB");
|
|
546
|
+
helper.processAllOps();
|
|
547
|
+
helper.logger.validate();
|
|
548
|
+
helper.obliterateRange("B", 0, 1);
|
|
549
|
+
helper.obliterateRange("B", 0, 1);
|
|
550
|
+
helper.removeRange("A", 0, 1);
|
|
551
|
+
helper.processAllOps();
|
|
552
|
+
assert_1.strict.equal(helper.clients.A.getText(), "");
|
|
553
|
+
assert_1.strict.equal(helper.clients.B.getText(), "");
|
|
554
|
+
assert_1.strict.equal(helper.clients.C.getText(), "");
|
|
555
|
+
helper.logger.validate();
|
|
556
|
+
});
|
|
557
|
+
it("accounts for concurrently obliterated segments from the perspective of the inserting client for partial lengths", () => {
|
|
558
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
559
|
+
helper.insertText("B", 0, "A");
|
|
560
|
+
helper.insertText("C", 0, "B");
|
|
561
|
+
helper.insertText("C", 0, "C");
|
|
562
|
+
helper.insertText("A", 0, "1234");
|
|
563
|
+
helper.processAllOps();
|
|
564
|
+
helper.obliterateRange("C", 1, 3);
|
|
565
|
+
helper.insertText("A", 2, "D");
|
|
566
|
+
helper.insertText("A", 4, "E");
|
|
567
|
+
helper.processAllOps();
|
|
568
|
+
assert_1.strict.equal(helper.clients.A.getText(), "1E4CBA");
|
|
569
|
+
assert_1.strict.equal(helper.clients.B.getText(), "1E4CBA");
|
|
570
|
+
helper.logger.validate();
|
|
571
|
+
});
|
|
572
|
+
it("traverses segments when there is a local obliterate", () => {
|
|
573
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
574
|
+
helper.insertText("A", 0, "AB");
|
|
575
|
+
helper.obliterateRange("A", 0, 1);
|
|
576
|
+
helper.insertText("C", 0, "12");
|
|
577
|
+
helper.processAllOps();
|
|
578
|
+
helper.logger.validate();
|
|
579
|
+
helper.insertText("C", 2, "C");
|
|
580
|
+
helper.obliterateRange("A", 0, 3);
|
|
581
|
+
helper.processAllOps();
|
|
582
|
+
assert_1.strict.equal(helper.clients.A.getText(), "");
|
|
583
|
+
helper.logger.validate();
|
|
584
|
+
});
|
|
585
|
+
it("keeps track of all obliterates on a segment", () => {
|
|
586
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
587
|
+
// B-A
|
|
588
|
+
// (B-C-(A))
|
|
589
|
+
helper.insertText("C", 0, "A");
|
|
590
|
+
helper.insertText("B", 0, "B");
|
|
591
|
+
helper.processAllOps();
|
|
592
|
+
helper.logger.validate();
|
|
593
|
+
helper.obliterateRange("B", 1, 2);
|
|
594
|
+
// bug here: because segment A has already been obliterated, we wouldn't
|
|
595
|
+
// mark it obliterated by this op as well, meaning that segments in
|
|
596
|
+
// this range would look to the right and not find a matching move seq
|
|
597
|
+
helper.obliterateRange("A", 0, 2);
|
|
598
|
+
helper.insertText("B", 1, "C");
|
|
599
|
+
helper.processAllOps();
|
|
600
|
+
assert_1.strict.equal(helper.clients.A.getText(), "");
|
|
601
|
+
assert_1.strict.equal(helper.clients.B.getText(), "");
|
|
602
|
+
assert_1.strict.equal(helper.clients.C.getText(), "");
|
|
603
|
+
assert_1.strict.equal(helper.clients.D.getText(), "");
|
|
604
|
+
helper.logger.validate();
|
|
605
|
+
});
|
|
606
|
+
it("many overlapping obliterates", () => {
|
|
607
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
608
|
+
// EF-ABCD
|
|
609
|
+
// (1)-2-((E)-F-A)-B-(C)-D
|
|
610
|
+
helper.insertText("C", 0, "ABCD");
|
|
611
|
+
helper.insertText("B", 0, "EF");
|
|
612
|
+
helper.processAllOps();
|
|
613
|
+
helper.logger.validate();
|
|
614
|
+
helper.obliterateRange("B", 0, 3);
|
|
615
|
+
helper.insertText("A", 0, "12");
|
|
616
|
+
helper.removeRange("C", 0, 1);
|
|
617
|
+
helper.obliterateRange("A", 0, 1);
|
|
618
|
+
helper.obliterateRange("B", 1, 2);
|
|
619
|
+
helper.processAllOps();
|
|
620
|
+
assert_1.strict.equal(helper.clients.A.getText(), "2BD");
|
|
621
|
+
helper.logger.validate();
|
|
622
|
+
});
|
|
623
|
+
it("overlapping obliterates at start", () => {
|
|
624
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
625
|
+
// 12345-B-A
|
|
626
|
+
// ((1-C-2)-3)-4-D-5-B-A
|
|
627
|
+
helper.insertText("C", 0, "A");
|
|
628
|
+
helper.insertText("B", 0, "B");
|
|
629
|
+
helper.insertText("A", 0, "12345");
|
|
630
|
+
helper.processAllOps();
|
|
631
|
+
helper.logger.validate();
|
|
632
|
+
helper.obliterateRange("A", 0, 2);
|
|
633
|
+
helper.insertText("C", 1, "C");
|
|
634
|
+
helper.obliterateRange("C", 0, 4);
|
|
635
|
+
helper.insertText("C", 1, "D");
|
|
636
|
+
helper.processAllOps();
|
|
637
|
+
helper.logger.validate();
|
|
638
|
+
});
|
|
639
|
+
it("partial lengths updated when local insert is acked", () => {
|
|
640
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
641
|
+
// A-BCDEF
|
|
642
|
+
// (A-B-G-C)-D-I-E-H-F
|
|
643
|
+
helper.insertText("A", 0, "A");
|
|
644
|
+
helper.insertText("A", 1, "BCDEF");
|
|
645
|
+
helper.processAllOps();
|
|
646
|
+
helper.logger.validate();
|
|
647
|
+
helper.obliterateRange("C", 0, 3);
|
|
648
|
+
helper.insertText("A", 2, "G");
|
|
649
|
+
helper.insertText("B", 5, "H");
|
|
650
|
+
helper.insertText("C", 1, "I");
|
|
651
|
+
helper.processAllOps();
|
|
652
|
+
assert_1.strict.equal(helper.clients.A.getText(), "DIEHF");
|
|
653
|
+
assert_1.strict.equal(helper.clients.B.getText(), "DIEHF");
|
|
654
|
+
assert_1.strict.equal(helper.clients.C.getText(), "DIEHF");
|
|
655
|
+
assert_1.strict.equal(helper.clients.D.getText(), "DIEHF");
|
|
656
|
+
helper.logger.validate();
|
|
657
|
+
});
|
|
658
|
+
it("two local obliterates get different seq numbers after ack", () => {
|
|
659
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
660
|
+
// C-AB
|
|
661
|
+
// (C-A)-D-(B)
|
|
662
|
+
helper.insertText("C", 0, "AB");
|
|
663
|
+
helper.insertText("A", 0, "C");
|
|
664
|
+
helper.processAllOps();
|
|
665
|
+
helper.logger.validate();
|
|
666
|
+
// bug here: when the op is acked by client C, it would incorrectly give
|
|
667
|
+
// segment B the same movedSeq despite coming from a different op
|
|
668
|
+
helper.obliterateRange("C", 0, 2);
|
|
669
|
+
helper.insertText("B", 2, "D");
|
|
670
|
+
helper.obliterateRange("C", 0, 1);
|
|
671
|
+
helper.processAllOps();
|
|
672
|
+
assert_1.strict.equal(helper.clients.A.getText(), "D");
|
|
673
|
+
assert_1.strict.equal(helper.clients.C.getText(), "D");
|
|
674
|
+
helper.logger.validate();
|
|
675
|
+
});
|
|
676
|
+
it("acks remote segment obliterated by local op", () => {
|
|
677
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
678
|
+
// (D-C-A)-B
|
|
679
|
+
// (D-C-A)-B-E
|
|
680
|
+
helper.insertText("B", 0, "AB");
|
|
681
|
+
helper.insertText("A", 0, "C");
|
|
682
|
+
helper.insertText("B", 0, "D");
|
|
683
|
+
// bug here: when the op is acked by client B, it wouldn't correctly
|
|
684
|
+
// visit the segment "C", leaving the obliterate unacked
|
|
685
|
+
helper.obliterateRange("B", 0, 2);
|
|
686
|
+
helper.processAllOps();
|
|
687
|
+
helper.logger.validate();
|
|
688
|
+
helper.insertText("C", 1, "E");
|
|
689
|
+
helper.processAllOps();
|
|
690
|
+
assert_1.strict.equal(helper.clients.A.getText(), "BE");
|
|
691
|
+
assert_1.strict.equal(helper.clients.B.getText(), "BE");
|
|
692
|
+
helper.logger.validate();
|
|
693
|
+
});
|
|
694
|
+
it("skips segments obliterated before refSeq when traversing for insertion", () => {
|
|
695
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
696
|
+
// CDE-(A)-B
|
|
697
|
+
// C-(DE-F-(A)-B)
|
|
698
|
+
helper.insertText("A", 0, "AB");
|
|
699
|
+
helper.obliterateRange("A", 0, 1);
|
|
700
|
+
helper.insertText("A", 0, "CDE");
|
|
701
|
+
helper.processAllOps();
|
|
702
|
+
helper.logger.validate();
|
|
703
|
+
helper.obliterateRange("A", 1, 4);
|
|
704
|
+
// bug here: when traversing to see if segment should be obliterated after
|
|
705
|
+
// insertion, traversal would stop at segment A because it was obliterated
|
|
706
|
+
// before the refSeq, which made it appear as an un-deleted segment
|
|
707
|
+
helper.insertText("B", 3, "F");
|
|
708
|
+
helper.processAllOps();
|
|
709
|
+
assert_1.strict.equal(helper.clients.A.getText(), "C");
|
|
710
|
+
assert_1.strict.equal(helper.clients.B.getText(), "C");
|
|
711
|
+
assert_1.strict.equal(helper.clients.C.getText(), "C");
|
|
712
|
+
helper.logger.validate();
|
|
713
|
+
});
|
|
714
|
+
it("applies correct movedSeq when right segment has multiple movedSeqs", () => {
|
|
715
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
716
|
+
// AB
|
|
717
|
+
// (A-C-D-(B))
|
|
718
|
+
helper.insertText("B", 0, "AB");
|
|
719
|
+
helper.processAllOps();
|
|
720
|
+
helper.logger.validate();
|
|
721
|
+
helper.obliterateRange("A", 1, 2);
|
|
722
|
+
helper.obliterateRange("B", 0, 2);
|
|
723
|
+
// bug here: for client B, segment B had multiple movedSeqs, and when
|
|
724
|
+
// traversal went to the right and found a matching movedSeq in the movedSeqs
|
|
725
|
+
// array, it selected the lowest seq in the array, which differed from
|
|
726
|
+
// the correct and matching movedSeq
|
|
727
|
+
helper.insertText("A", 1, "C");
|
|
728
|
+
helper.insertText("A", 2, "D");
|
|
729
|
+
helper.processAllOps();
|
|
730
|
+
assert_1.strict.equal(helper.clients.A.getText(), "");
|
|
731
|
+
assert_1.strict.equal(helper.clients.B.getText(), "");
|
|
732
|
+
assert_1.strict.equal(helper.clients.C.getText(), "");
|
|
733
|
+
helper.logger.validate();
|
|
734
|
+
});
|
|
735
|
+
it("takes the correct moved client id when multiple clientIds for right segment", () => {
|
|
736
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
737
|
+
// AB
|
|
738
|
+
// (A-C-D-(B))
|
|
739
|
+
helper.insertText("A", 0, "AB");
|
|
740
|
+
helper.processAllOps();
|
|
741
|
+
helper.logger.validate();
|
|
742
|
+
helper.obliterateRange("A", 1, 2);
|
|
743
|
+
// bug here: we would incorrectly take the client id of the first element
|
|
744
|
+
// in the movedClientIds array because we did not take into account the
|
|
745
|
+
// length of _both_ the local and non-local movedSeqs arrays
|
|
746
|
+
helper.insertText("A", 1, "C");
|
|
747
|
+
helper.obliterateRange("C", 0, 2);
|
|
748
|
+
helper.insertText("A", 2, "D");
|
|
749
|
+
helper.processAllOps();
|
|
750
|
+
assert_1.strict.equal(helper.clients.A.getText(), "");
|
|
751
|
+
assert_1.strict.equal(helper.clients.B.getText(), "");
|
|
752
|
+
assert_1.strict.equal(helper.clients.C.getText(), "");
|
|
753
|
+
helper.logger.validate();
|
|
754
|
+
});
|
|
755
|
+
it("selects clientId if 0", () => {
|
|
756
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
757
|
+
// AB
|
|
758
|
+
// (A-D-E-(C)-B)
|
|
759
|
+
helper.insertText("B", 0, "AB");
|
|
760
|
+
helper.processAllOps();
|
|
761
|
+
helper.logger.validate();
|
|
762
|
+
// bug here: client id was 0, and the check we used was !clientId, rather
|
|
763
|
+
// than clientId !== undefined
|
|
764
|
+
helper.insertText("A", 1, "C");
|
|
765
|
+
helper.obliterateRange("B", 0, 2);
|
|
766
|
+
helper.obliterateRange("A", 1, 2);
|
|
767
|
+
helper.insertText("A", 1, "D");
|
|
768
|
+
helper.insertText("A", 2, "E");
|
|
769
|
+
helper.processAllOps();
|
|
770
|
+
assert_1.strict.equal(helper.clients.A.getText(), "");
|
|
771
|
+
assert_1.strict.equal(helper.clients.B.getText(), "");
|
|
772
|
+
assert_1.strict.equal(helper.clients.C.getText(), "");
|
|
773
|
+
helper.logger.validate();
|
|
774
|
+
});
|
|
775
|
+
it("obliterates unacked segment inside non-leaf-segment", () => {
|
|
776
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
777
|
+
// FGHIJ-E-12345678-D-C-A-K-B
|
|
778
|
+
// FGHIJ-E-12345-(6-[7-L-8-D]-C)-A-K-B
|
|
779
|
+
helper.insertText("A", 0, "AB");
|
|
780
|
+
helper.insertText("A", 0, "C");
|
|
781
|
+
helper.insertText("C", 0, "D");
|
|
782
|
+
helper.insertText("B", 0, "12345678");
|
|
783
|
+
helper.insertText("B", 0, "E");
|
|
784
|
+
helper.insertText("C", 0, "FGHIJ");
|
|
785
|
+
helper.insertText("A", 2, "K");
|
|
786
|
+
helper.processAllOps();
|
|
787
|
+
helper.logger.validate();
|
|
788
|
+
helper.removeRange("A", 12, 15);
|
|
789
|
+
// bug here: when traversing for obliterate, we visit unacked segments
|
|
790
|
+
// within the range, considering their length 0 but still marking them
|
|
791
|
+
// obliterated. if the segment was inside a hiernode whose length was
|
|
792
|
+
// also 0, we would incorrectly skip over the entire hier node, rather
|
|
793
|
+
// than visiting the children segments
|
|
794
|
+
helper.obliterateRange("A", 11, 13);
|
|
795
|
+
helper.insertText("B", 13, "L");
|
|
796
|
+
helper.processAllOps();
|
|
797
|
+
assert_1.strict.equal(helper.clients.A.getText(), "FGHIJE12345AKB");
|
|
798
|
+
assert_1.strict.equal(helper.clients.B.getText(), "FGHIJE12345AKB");
|
|
799
|
+
assert_1.strict.equal(helper.clients.C.getText(), "FGHIJE12345AKB");
|
|
800
|
+
helper.logger.validate();
|
|
801
|
+
});
|
|
802
|
+
it("tracks length at seq of lower move/remove seq when overlapping", () => {
|
|
803
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
804
|
+
// H-FG-A-CDE-B
|
|
805
|
+
// (H-F-[G-A)-C-I-D]-E-B
|
|
806
|
+
helper.insertText("C", 0, "AB");
|
|
807
|
+
helper.insertText("C", 1, "CDE");
|
|
808
|
+
helper.insertText("B", 0, "FG");
|
|
809
|
+
helper.insertText("A", 0, "H");
|
|
810
|
+
helper.processAllOps();
|
|
811
|
+
helper.logger.validate();
|
|
812
|
+
helper.obliterateRange("A", 0, 4);
|
|
813
|
+
helper.removeRange("B", 2, 6);
|
|
814
|
+
// bug here: this insert triggers a new chunk to be created. when the
|
|
815
|
+
// partial lengths of the new chunk were calculated, it incorrectly
|
|
816
|
+
// used the removedSeq instead of the moveSeq, despite the latter having
|
|
817
|
+
// occurred prior to the remove
|
|
818
|
+
helper.insertText("A", 1, "I");
|
|
819
|
+
helper.processAllOps();
|
|
820
|
+
assert_1.strict.equal(helper.clients.A.getText(), "IEB");
|
|
821
|
+
assert_1.strict.equal(helper.clients.B.getText(), "IEB");
|
|
822
|
+
assert_1.strict.equal(helper.clients.C.getText(), "IEB");
|
|
823
|
+
helper.logger.validate();
|
|
824
|
+
});
|
|
825
|
+
it("segment obliterated on insert overlaps with local obliterate", () => {
|
|
826
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
827
|
+
// AB
|
|
828
|
+
// ((A-C)-B)
|
|
829
|
+
helper.insertText("B", 0, "AB");
|
|
830
|
+
helper.processAllOps();
|
|
831
|
+
helper.logger.validate();
|
|
832
|
+
helper.insertText("B", 1, "C");
|
|
833
|
+
helper.obliterateRange("B", 0, 2);
|
|
834
|
+
helper.obliterateRange("A", 0, 2);
|
|
835
|
+
helper.processAllOps();
|
|
836
|
+
assert_1.strict.equal(helper.clients.A.getText(), "");
|
|
837
|
+
helper.logger.validate();
|
|
838
|
+
});
|
|
839
|
+
it("obliterates entire string when concurrent inserts inside range", () => {
|
|
840
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
841
|
+
// GT
|
|
842
|
+
// (G-O-S-Y-T)
|
|
843
|
+
helper.insertText("B", 0, "GT");
|
|
844
|
+
helper.processAllOps();
|
|
845
|
+
helper.logger.validate();
|
|
846
|
+
helper.obliterateRange("B", 0, 2);
|
|
847
|
+
helper.insertText("C", 1, "OY");
|
|
848
|
+
helper.insertText("C", 2, "S");
|
|
849
|
+
helper.processAllOps();
|
|
850
|
+
assert_1.strict.equal(helper.clients.A.getText(), "");
|
|
851
|
+
helper.logger.validate();
|
|
852
|
+
});
|
|
853
|
+
it("obliterate ack traverses over non-obliterated remove", () => {
|
|
854
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
855
|
+
// ABCDEFGH-1
|
|
856
|
+
// ABCDE-(F-[G]-2-H)-1
|
|
857
|
+
// ABCDE-(F-[G]-2-H)-1-3
|
|
858
|
+
helper.insertText("A", 0, "1");
|
|
859
|
+
helper.insertText("C", 0, "ABCDEFGH");
|
|
860
|
+
helper.processAllOps();
|
|
861
|
+
helper.logger.validate();
|
|
862
|
+
helper.removeRange("C", 6, 7);
|
|
863
|
+
helper.insertText("A", 7, "2");
|
|
864
|
+
// obliterate at seq 5 isn't getting acked because it stops traversal
|
|
865
|
+
// at the removed segment, which doesn't have move info
|
|
866
|
+
helper.obliterateRange("C", 5, 7);
|
|
867
|
+
helper.processAllOps();
|
|
868
|
+
helper.logger.validate();
|
|
869
|
+
helper.insertText("A", 6, "3");
|
|
870
|
+
helper.processAllOps();
|
|
871
|
+
assert_1.strict.equal(helper.clients.A.getText(), "ABCDE13");
|
|
872
|
+
assert_1.strict.equal(helper.clients.C.getText(), "ABCDE13");
|
|
873
|
+
helper.logger.validate();
|
|
874
|
+
});
|
|
875
|
+
it("overlapping remove and obliterate when remove happens last", () => {
|
|
876
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
877
|
+
// FGH-E-D-BC-A
|
|
878
|
+
// ([F]-G)-H-E-D-B-I-C-A
|
|
879
|
+
helper.insertText("A", 0, "A");
|
|
880
|
+
helper.insertText("B", 0, "BC");
|
|
881
|
+
helper.insertText("C", 0, "D");
|
|
882
|
+
helper.insertText("B", 0, "E");
|
|
883
|
+
helper.insertText("B", 0, "FGH");
|
|
884
|
+
helper.processAllOps();
|
|
885
|
+
helper.logger.validate();
|
|
886
|
+
helper.obliterateRange("C", 0, 2);
|
|
887
|
+
helper.removeRange("A", 0, 1);
|
|
888
|
+
helper.insertText("A", 5, "I");
|
|
889
|
+
helper.processAllOps();
|
|
890
|
+
assert_1.strict.equal(helper.clients.A.getText(), "HEDBICA");
|
|
891
|
+
assert_1.strict.equal(helper.clients.B.getText(), "HEDBICA");
|
|
892
|
+
assert_1.strict.equal(helper.clients.C.getText(), "HEDBICA");
|
|
893
|
+
assert_1.strict.equal(helper.clients.D.getText(), "HEDBICA");
|
|
894
|
+
helper.logger.validate();
|
|
895
|
+
});
|
|
896
|
+
it("overlapping remove and obliterate when remove happens last _and_ partial length already exists", () => {
|
|
897
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
898
|
+
// FGH-CDE-B-A
|
|
899
|
+
// [F-(GH)-C]-D-Z-E-B-A
|
|
900
|
+
helper.insertText("B", 0, "A");
|
|
901
|
+
helper.insertText("B", 0, "B");
|
|
902
|
+
helper.insertText("B", 0, "CDE");
|
|
903
|
+
helper.insertText("C", 0, "FGH");
|
|
904
|
+
helper.processAllOps();
|
|
905
|
+
helper.logger.validate();
|
|
906
|
+
helper.obliterateRange("C", 1, 2);
|
|
907
|
+
helper.removeRange("A", 0, 4);
|
|
908
|
+
helper.insertText("A", 1, "Z");
|
|
909
|
+
helper.processAllOps();
|
|
910
|
+
assert_1.strict.equal(helper.clients.A.getText(), "DZEBA");
|
|
911
|
+
helper.logger.validate();
|
|
912
|
+
});
|
|
913
|
+
it("overlapping obliterate and remove when obliterate is larger than remove and happened last", () => {
|
|
914
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
915
|
+
// H-CDEFG-B-A
|
|
916
|
+
// (H-C-[D]-E)-F-Z-G-B-A
|
|
917
|
+
helper.insertText("B", 0, "A");
|
|
918
|
+
helper.insertText("C", 0, "B");
|
|
919
|
+
helper.insertText("A", 0, "CDEFG");
|
|
920
|
+
helper.insertText("A", 0, "H");
|
|
921
|
+
helper.processAllOps();
|
|
922
|
+
helper.logger.validate();
|
|
923
|
+
helper.removeRange("A", 2, 3);
|
|
924
|
+
helper.obliterateRange("C", 0, 4);
|
|
925
|
+
helper.insertText("C", 1, "Z");
|
|
926
|
+
helper.processAllOps();
|
|
927
|
+
assert_1.strict.equal(helper.clients.A.getText(), "FZGBA");
|
|
928
|
+
helper.logger.validate();
|
|
929
|
+
});
|
|
930
|
+
it("wasMovedOnInsert remains after leaf node is split", () => {
|
|
931
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
932
|
+
// CD-B-A
|
|
933
|
+
// I-(C-(G)H-E)-F-D-B-A
|
|
934
|
+
helper.insertText("C", 0, "A");
|
|
935
|
+
helper.insertText("B", 0, "B");
|
|
936
|
+
helper.insertText("B", 0, "CD");
|
|
937
|
+
helper.processAllOps();
|
|
938
|
+
helper.logger.validate();
|
|
939
|
+
helper.insertText("B", 1, "EF");
|
|
940
|
+
helper.obliterateRange("B", 0, 2);
|
|
941
|
+
helper.insertText("A", 1, "GH");
|
|
942
|
+
helper.obliterateRange("A", 1, 2);
|
|
943
|
+
helper.insertText("B", 0, "I");
|
|
944
|
+
helper.processAllOps();
|
|
945
|
+
assert_1.strict.equal(helper.clients.A.getText(), "IFDBA");
|
|
946
|
+
helper.logger.validate();
|
|
947
|
+
});
|
|
948
|
+
it("overlapping obliterates, segment is obliterated on insert and by local client", () => {
|
|
949
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
950
|
+
// DEFG-BC-A
|
|
951
|
+
// v-----------v
|
|
952
|
+
// v-----v
|
|
953
|
+
// (D-(E-H-F)-G)-B-I-C-A
|
|
954
|
+
helper.insertText("B", 0, "A");
|
|
955
|
+
helper.insertText("C", 0, "BC");
|
|
956
|
+
helper.insertText("B", 0, "DEFG");
|
|
957
|
+
helper.processAllOps();
|
|
958
|
+
helper.logger.validate();
|
|
959
|
+
helper.obliterateRange("B", 1, 3);
|
|
960
|
+
helper.insertText("A", 2, "H");
|
|
961
|
+
helper.obliterateRange("A", 0, 5);
|
|
962
|
+
helper.insertText("A", 1, "I");
|
|
963
|
+
helper.processAllOps();
|
|
964
|
+
assert_1.strict.equal(helper.clients.A.getText(), "BICA");
|
|
965
|
+
helper.logger.validate();
|
|
966
|
+
});
|
|
967
|
+
it("overlapping obliterates and remove", () => {
|
|
968
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
969
|
+
// FGHIJKL-BCDE-A
|
|
970
|
+
// (FGHI-[JK-(L-B)-C]-D)-E-M-A
|
|
971
|
+
helper.insertText("C", 0, "A");
|
|
972
|
+
helper.insertText("B", 0, "BCDE");
|
|
973
|
+
helper.insertText("A", 0, "FGHIJKL");
|
|
974
|
+
helper.processAllOps();
|
|
975
|
+
helper.logger.validate();
|
|
976
|
+
helper.removeRange("B", 4, 9);
|
|
977
|
+
helper.obliterateRange("A", 6, 8);
|
|
978
|
+
helper.obliterateRange("C", 0, 10);
|
|
979
|
+
helper.insertText("C", 1, "M");
|
|
980
|
+
helper.processAllOps();
|
|
981
|
+
assert_1.strict.equal(helper.clients.A.getText(), "EMA");
|
|
982
|
+
helper.logger.validate();
|
|
983
|
+
});
|
|
984
|
+
it("does not mark obliterated on insert for non-acked obliterates", () => {
|
|
985
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
986
|
+
// CDE-B-A
|
|
987
|
+
// I-((C-F)-G-D)-H-E-B-A
|
|
988
|
+
helper.insertText("B", 0, "A");
|
|
989
|
+
helper.insertText("A", 0, "B");
|
|
990
|
+
helper.insertText("C", 0, "CDE");
|
|
991
|
+
helper.processAllOps();
|
|
992
|
+
helper.logger.validate();
|
|
993
|
+
helper.insertText("A", 1, "FG");
|
|
994
|
+
helper.obliterateRange("A", 0, 2);
|
|
995
|
+
helper.insertText("A", 2, "H");
|
|
996
|
+
helper.obliterateRange("C", 0, 2);
|
|
997
|
+
helper.insertText("C", 0, "I");
|
|
998
|
+
helper.processAllOps();
|
|
999
|
+
assert_1.strict.equal(helper.clients.A.getText(), "IHEBA");
|
|
1000
|
+
helper.logger.validate();
|
|
1001
|
+
});
|
|
1002
|
+
it("partial len isLocal when seq is -1 but moveSeq > -1", () => {
|
|
1003
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
1004
|
+
// CDEFG-AB
|
|
1005
|
+
// C-((D-I-E)-F)-G-A-H-B
|
|
1006
|
+
helper.insertText("A", 0, "AB");
|
|
1007
|
+
helper.insertText("B", 0, "CDEFG");
|
|
1008
|
+
helper.processAllOps();
|
|
1009
|
+
helper.logger.validate();
|
|
1010
|
+
helper.obliterateRange("C", 1, 4);
|
|
1011
|
+
helper.obliterateRange("B", 1, 3);
|
|
1012
|
+
helper.insertText("B", 4, "H");
|
|
1013
|
+
helper.insertText("A", 2, "I");
|
|
1014
|
+
helper.processAllOps();
|
|
1015
|
+
assert_1.strict.equal(helper.clients.A.getText(), "CGAHB");
|
|
1016
|
+
helper.logger.validate();
|
|
1017
|
+
});
|
|
1018
|
+
it("obliterated on insert by overlapping obliterates", () => {
|
|
1019
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
1020
|
+
// B-DEFG-C-A
|
|
1021
|
+
// ((B-D-H-E)-F-I-G-C)-A
|
|
1022
|
+
helper.insertText("C", 0, "A");
|
|
1023
|
+
helper.insertText("A", 0, "BC");
|
|
1024
|
+
helper.insertText("A", 1, "DEFG");
|
|
1025
|
+
helper.processAllOps();
|
|
1026
|
+
helper.logger.validate();
|
|
1027
|
+
helper.obliterateRange("B", 0, 6);
|
|
1028
|
+
helper.obliterateRange("A", 0, 3);
|
|
1029
|
+
helper.insertText("C", 2, "H");
|
|
1030
|
+
helper.insertText("A", 1, "I");
|
|
1031
|
+
helper.processAllOps();
|
|
1032
|
+
assert_1.strict.equal(helper.clients.A.getText(), "A");
|
|
1033
|
+
helper.logger.validate();
|
|
1034
|
+
});
|
|
1035
|
+
it("overlapping obliterates", () => {
|
|
1036
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
1037
|
+
// ABCDEF
|
|
1038
|
+
// v-------------v
|
|
1039
|
+
// v-----------v
|
|
1040
|
+
// v-------v
|
|
1041
|
+
// I-((AB-(C-G)-H-D)-E)-F
|
|
1042
|
+
helper.insertText("A", 0, "ABCDEF");
|
|
1043
|
+
helper.processAllOps();
|
|
1044
|
+
helper.logger.validate();
|
|
1045
|
+
helper.obliterateRange("B", 0, 4);
|
|
1046
|
+
helper.obliterateRange("C", 2, 5);
|
|
1047
|
+
helper.insertText("A", 3, "GH");
|
|
1048
|
+
helper.insertText("C", 0, "I");
|
|
1049
|
+
helper.obliterateRange("A", 0, 4);
|
|
1050
|
+
helper.processAllOps();
|
|
1051
|
+
assert_1.strict.equal(helper.clients.A.getText(), "IF");
|
|
1052
|
+
helper.logger.validate();
|
|
1053
|
+
});
|
|
1054
|
+
it("overlapping obliterates", () => {
|
|
1055
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
1056
|
+
// CDEF-AB
|
|
1057
|
+
// v-------------------v
|
|
1058
|
+
// v---------v
|
|
1059
|
+
// v-v
|
|
1060
|
+
// ((C-(G)-H-D)-E-I-F-A)-B
|
|
1061
|
+
helper.insertText("B", 0, "AB");
|
|
1062
|
+
helper.insertText("C", 0, "CDEF");
|
|
1063
|
+
helper.processAllOps();
|
|
1064
|
+
helper.logger.validate();
|
|
1065
|
+
helper.obliterateRange("B", 0, 5);
|
|
1066
|
+
helper.obliterateRange("C", 0, 2);
|
|
1067
|
+
helper.insertText("A", 1, "GH");
|
|
1068
|
+
helper.insertText("C", 1, "I");
|
|
1069
|
+
helper.obliterateRange("A", 1, 2);
|
|
1070
|
+
helper.processAllOps();
|
|
1071
|
+
assert_1.strict.equal(helper.clients.A.getText(), "B");
|
|
1072
|
+
helper.logger.validate();
|
|
1073
|
+
});
|
|
1074
|
+
it("overlapping remove and obliterate, local obliterate does not have a remote obliterated len", () => {
|
|
1075
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
1076
|
+
// v-v------v-v
|
|
1077
|
+
// G-(H-[F]-E)-D-[A]-B-I-C
|
|
1078
|
+
helper.insertText("A", 0, "ABC");
|
|
1079
|
+
helper.insertText("B", 0, "D");
|
|
1080
|
+
helper.insertText("B", 0, "E");
|
|
1081
|
+
helper.insertText("A", 0, "F");
|
|
1082
|
+
helper.removeRange("A", 0, 2);
|
|
1083
|
+
helper.insertText("B", 0, "GH");
|
|
1084
|
+
helper.insertText("A", 1, "I");
|
|
1085
|
+
helper.obliterateRange("B", 1, 3);
|
|
1086
|
+
helper.processAllOps();
|
|
1087
|
+
assert_1.strict.equal(helper.clients.A.getText(), "GDBIC");
|
|
1088
|
+
helper.logger.validate();
|
|
1089
|
+
});
|
|
1090
|
+
it("triple overlapping obliterate and overlapping remove", () => {
|
|
1091
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
1092
|
+
// I-H-BCDEFG-A
|
|
1093
|
+
// v------------v
|
|
1094
|
+
// v-------v
|
|
1095
|
+
// v-v
|
|
1096
|
+
// v-------------------------v
|
|
1097
|
+
// [[I]-H-BCD-(E-(F-(J)-G))-A]
|
|
1098
|
+
helper.insertText("C", 0, "A");
|
|
1099
|
+
helper.insertText("B", 0, "BCDEFG");
|
|
1100
|
+
helper.insertText("B", 0, "H");
|
|
1101
|
+
helper.insertText("B", 0, "I");
|
|
1102
|
+
helper.processAllOps();
|
|
1103
|
+
helper.logger.validate();
|
|
1104
|
+
helper.obliterateRange("B", 6, 8);
|
|
1105
|
+
helper.obliterateRange("A", 5, 8);
|
|
1106
|
+
helper.removeRange("C", 0, 1);
|
|
1107
|
+
helper.insertText("C", 6, "J");
|
|
1108
|
+
helper.obliterateRange("C", 6, 7);
|
|
1109
|
+
helper.removeRange("A", 0, 6);
|
|
1110
|
+
helper.processAllOps();
|
|
1111
|
+
assert_1.strict.equal(helper.clients.A.getText(), "");
|
|
1112
|
+
assert_1.strict.equal(helper.clients.C.getText(), "");
|
|
1113
|
+
helper.logger.validate();
|
|
1114
|
+
});
|
|
1115
|
+
it("triple overlapping obliterate with one being local", () => {
|
|
1116
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
1117
|
+
// CDEFG-B-A
|
|
1118
|
+
// v--------v
|
|
1119
|
+
// v--------v
|
|
1120
|
+
// v------v
|
|
1121
|
+
// J-(CD-((E-H)-F))-I-G-B-A
|
|
1122
|
+
helper.insertText("C", 0, "A");
|
|
1123
|
+
helper.insertText("B", 0, "B");
|
|
1124
|
+
helper.insertText("C", 0, "CDEFG");
|
|
1125
|
+
helper.processAllOps();
|
|
1126
|
+
helper.logger.validate();
|
|
1127
|
+
helper.obliterateRange("A", 2, 4);
|
|
1128
|
+
helper.insertText("C", 3, "H");
|
|
1129
|
+
helper.obliterateRange("C", 0, 4);
|
|
1130
|
+
helper.insertText("C", 1, "I");
|
|
1131
|
+
helper.insertText("B", 0, "J");
|
|
1132
|
+
helper.obliterateRange("B", 3, 5);
|
|
1133
|
+
helper.processAllOps();
|
|
1134
|
+
assert_1.strict.equal(helper.clients.C.getText(), "JIGBA");
|
|
1135
|
+
helper.logger.validate();
|
|
1136
|
+
});
|
|
1137
|
+
it("obliterate ack traversal is not stopped by moved segment", () => {
|
|
1138
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
1139
|
+
// ABCD
|
|
1140
|
+
// (A-(B)-E-C)-D-F
|
|
1141
|
+
helper.insertText("B", 0, "ABCD");
|
|
1142
|
+
helper.processAllOps();
|
|
1143
|
+
helper.logger.validate();
|
|
1144
|
+
helper.obliterateRange("A", 1, 2);
|
|
1145
|
+
helper.insertText("C", 2, "E");
|
|
1146
|
+
helper.obliterateRange("A", 0, 2);
|
|
1147
|
+
helper.processAllOps();
|
|
1148
|
+
helper.logger.validate();
|
|
1149
|
+
helper.insertText("C", 1, "F");
|
|
1150
|
+
helper.processAllOps();
|
|
1151
|
+
assert_1.strict.equal(helper.clients.A.getText(), "DF");
|
|
1152
|
+
assert_1.strict.equal(helper.clients.C.getText(), "DF");
|
|
1153
|
+
helper.logger.validate();
|
|
1154
|
+
});
|
|
1155
|
+
it("updates partial lengths for segments when doing obliterate ack traversal", () => {
|
|
1156
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
1157
|
+
// O-JKLMN-FGHI-E-CD-AB
|
|
1158
|
+
// v-v---v-----v
|
|
1159
|
+
// (P-O-JKLMN-FG-[H]-Q-[I-E-C]-D-A)-B
|
|
1160
|
+
// (P-O-JKLMN-FG-[H]-Q-[I-E-C]-D-A)-B-R
|
|
1161
|
+
// problematic segment: H-Q-I-E
|
|
1162
|
+
helper.insertText("B", 0, "AB");
|
|
1163
|
+
helper.insertText("A", 0, "CD");
|
|
1164
|
+
helper.insertText("A", 0, "E");
|
|
1165
|
+
helper.insertText("A", 0, "FGHI");
|
|
1166
|
+
helper.insertText("C", 0, "JKLMN");
|
|
1167
|
+
helper.insertText("B", 0, "O");
|
|
1168
|
+
helper.processAllOps();
|
|
1169
|
+
helper.logger.validate();
|
|
1170
|
+
helper.insertText("A", 0, "P");
|
|
1171
|
+
helper.insertText("B", 9, "Q");
|
|
1172
|
+
helper.removeRange("A", 9, 13);
|
|
1173
|
+
helper.obliterateRange("A", 0, 11);
|
|
1174
|
+
helper.processAllOps();
|
|
1175
|
+
helper.logger.validate();
|
|
1176
|
+
helper.insertText("B", 1, "R");
|
|
1177
|
+
helper.processAllOps();
|
|
1178
|
+
assert_1.strict.equal(helper.clients.A.getText(), "BR");
|
|
1179
|
+
helper.logger.validate();
|
|
1180
|
+
});
|
|
1181
|
+
// fails only for incremental
|
|
1182
|
+
it("combines remote obliterated length ", () => {
|
|
1183
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
1184
|
+
// R-XYZ12-STUVW-LMNOP-DEFGHIJK-A-34567890-Q-BC
|
|
1185
|
+
// v---------------v------------v------v--------v------------v
|
|
1186
|
+
// [R-XYZ12-STUVW-L]-abcdefghij-[MNOP-D]-klmnop-[EFGHIJK-A-34]-5-(6-x-7)-890-Q-BC
|
|
1187
|
+
// v--------v
|
|
1188
|
+
// v--------v
|
|
1189
|
+
// a-T-b-S-c-[def]-ghij-k-Q-l-R-mno-[p-5-8-[9]-0-Q-B]-C
|
|
1190
|
+
// v--------v
|
|
1191
|
+
// v--------v
|
|
1192
|
+
// segment is R-mno-[p-5-8-[9]-0-Q-B]-C
|
|
1193
|
+
// v-------------------------------v
|
|
1194
|
+
// v--------v
|
|
1195
|
+
// v------------v
|
|
1196
|
+
// R-mno-[p-[EFGHIJK-A-34]-5-(6-x-7)-8-[9]-0-Q-B]-C
|
|
1197
|
+
helper.insertText("A", 0, "ABC");
|
|
1198
|
+
helper.insertText("C", 0, "DEFGHIJK");
|
|
1199
|
+
helper.insertText("C", 0, "LMNOP");
|
|
1200
|
+
helper.insertText("A", 1, "Q");
|
|
1201
|
+
helper.insertText("B", 0, "RSTUVW");
|
|
1202
|
+
helper.insertText("B", 1, "XYZ12");
|
|
1203
|
+
helper.insertText("A", 1, "34567890");
|
|
1204
|
+
helper.processAllOps();
|
|
1205
|
+
helper.logger.validate();
|
|
1206
|
+
helper.insertText("C", 29, "x");
|
|
1207
|
+
helper.removeRange("B", 0, 27);
|
|
1208
|
+
helper.insertText("A", 12, "abcdefghij");
|
|
1209
|
+
helper.obliterateRange("A", 38, 40);
|
|
1210
|
+
helper.insertText("C", 17, "klmnop");
|
|
1211
|
+
helper.processAllOps();
|
|
1212
|
+
assert_1.strict.equal(helper.clients.A.getText(), "abcdefghijklmnop5890QBC");
|
|
1213
|
+
helper.logger.validate();
|
|
1214
|
+
helper.removeRange("B", 3, 6);
|
|
1215
|
+
helper.insertText("C", 11, "Q");
|
|
1216
|
+
helper.insertText("B", 9, "R");
|
|
1217
|
+
helper.insertText("B", 2, "S");
|
|
1218
|
+
helper.removeRange("C", 19, 23);
|
|
1219
|
+
helper.obliterateRange("B", 14, 18);
|
|
1220
|
+
helper.insertText("B", 1, "T");
|
|
1221
|
+
helper.processAllOps();
|
|
1222
|
+
assert_1.strict.equal(helper.clients.A.getText(), "aTbScghijkQlRmnoC");
|
|
1223
|
+
helper.logger.validate();
|
|
1224
|
+
});
|
|
1225
|
+
it("three obliterates on same segment", () => {
|
|
1226
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
1227
|
+
// A
|
|
1228
|
+
// (C-B-D-((A)))
|
|
1229
|
+
helper.insertText("B", 0, "A");
|
|
1230
|
+
helper.processAllOps();
|
|
1231
|
+
helper.logger.validate();
|
|
1232
|
+
helper.obliterateRange("B", 0, 1);
|
|
1233
|
+
helper.obliterateRange("A", 0, 1);
|
|
1234
|
+
helper.insertText("A", 0, "B");
|
|
1235
|
+
helper.insertText("C", 0, "C");
|
|
1236
|
+
helper.insertText("A", 1, "D");
|
|
1237
|
+
helper.obliterateRange("C", 0, 2);
|
|
1238
|
+
helper.processAllOps();
|
|
1239
|
+
helper.logger.validate();
|
|
1240
|
+
});
|
|
1241
|
+
describe("incremental partial length updates", () => {
|
|
1242
|
+
it("obliterates concurrently inserted segment", () => {
|
|
1243
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
1244
|
+
// (C-B-A)
|
|
1245
|
+
helper.insertText("A", 0, "A");
|
|
1246
|
+
helper.insertText("B", 0, "B");
|
|
1247
|
+
helper.insertText("A", 0, "C");
|
|
1248
|
+
helper.obliterateRange("A", 0, 2);
|
|
1249
|
+
helper.processAllOps();
|
|
1250
|
+
assert_1.strict.equal(helper.clients.A.getText(), "");
|
|
1251
|
+
helper.logger.validate();
|
|
1252
|
+
});
|
|
1253
|
+
it("obliterates 2 concurrently inserted segments", () => {
|
|
1254
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
1255
|
+
// (C-B-D-A)
|
|
1256
|
+
helper.insertText("B", 0, "A");
|
|
1257
|
+
helper.insertText("A", 0, "B");
|
|
1258
|
+
helper.insertText("B", 0, "C");
|
|
1259
|
+
helper.obliterateRange("B", 0, 2);
|
|
1260
|
+
helper.insertText("A", 1, "D");
|
|
1261
|
+
helper.processAllOps();
|
|
1262
|
+
assert_1.strict.equal(helper.clients.A.getText(), "");
|
|
1263
|
+
helper.logger.validate();
|
|
1264
|
+
});
|
|
1265
|
+
it("obliterates 4 concurrently inserted segments", () => {
|
|
1266
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
1267
|
+
// I-F-(G-D-H-E-C-A)-B
|
|
1268
|
+
helper.insertText("B", 0, "AB");
|
|
1269
|
+
helper.insertText("B", 0, "C");
|
|
1270
|
+
helper.insertText("A", 0, "DE");
|
|
1271
|
+
helper.insertText("B", 0, "FG");
|
|
1272
|
+
helper.obliterateRange("B", 1, 4);
|
|
1273
|
+
helper.insertText("A", 1, "H");
|
|
1274
|
+
helper.insertText("B", 0, "I");
|
|
1275
|
+
helper.processAllOps();
|
|
1276
|
+
assert_1.strict.equal(helper.clients.A.getText(), "IFB");
|
|
1277
|
+
helper.logger.validate();
|
|
1278
|
+
});
|
|
1279
|
+
it("obliterates 3 concurrently inserted segments", () => {
|
|
1280
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
1281
|
+
// I-G-(H-D-F-E-B)-C-A
|
|
1282
|
+
helper.insertText("B", 0, "A");
|
|
1283
|
+
helper.insertText("B", 0, "BC");
|
|
1284
|
+
helper.insertText("A", 0, "DE");
|
|
1285
|
+
helper.insertText("A", 1, "F");
|
|
1286
|
+
helper.insertText("B", 0, "GH");
|
|
1287
|
+
helper.obliterateRange("B", 1, 3);
|
|
1288
|
+
helper.insertText("B", 0, "I");
|
|
1289
|
+
helper.processAllOps();
|
|
1290
|
+
assert_1.strict.equal(helper.clients.A.getText(), "IGCA");
|
|
1291
|
+
helper.logger.validate();
|
|
1292
|
+
});
|
|
1293
|
+
it("overlapping remove + obliterate, remove happened first", () => {
|
|
1294
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
1295
|
+
// D-EFG-B-H-C-A
|
|
1296
|
+
// I-D-([E]-F)-G-B-H-C-A
|
|
1297
|
+
helper.insertText("A", 0, "A");
|
|
1298
|
+
helper.insertText("B", 0, "BC");
|
|
1299
|
+
helper.insertText("C", 0, "DEFG");
|
|
1300
|
+
helper.insertText("B", 1, "H");
|
|
1301
|
+
helper.processAllOps();
|
|
1302
|
+
helper.logger.validate();
|
|
1303
|
+
helper.removeRange("B", 1, 2);
|
|
1304
|
+
helper.obliterateRange("A", 1, 3);
|
|
1305
|
+
helper.insertText("A", 0, "I");
|
|
1306
|
+
helper.processAllOps();
|
|
1307
|
+
assert_1.strict.equal(helper.clients.A.getText(), "IDGBHCA");
|
|
1308
|
+
helper.logger.validate();
|
|
1309
|
+
});
|
|
1310
|
+
it("overlapping remove + obliterate, remove happened last", () => {
|
|
1311
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
1312
|
+
// DEFGH-C-B-A
|
|
1313
|
+
// [D]-[E-(F)-G]-H-C-B-A
|
|
1314
|
+
helper.insertText("C", 0, "A");
|
|
1315
|
+
helper.insertText("B", 0, "B");
|
|
1316
|
+
helper.insertText("A", 0, "C");
|
|
1317
|
+
helper.insertText("B", 0, "DEFGH");
|
|
1318
|
+
helper.processAllOps();
|
|
1319
|
+
helper.logger.validate();
|
|
1320
|
+
helper.obliterateRange("C", 2, 3);
|
|
1321
|
+
helper.removeRange("A", 1, 4);
|
|
1322
|
+
helper.removeRange("A", 0, 1);
|
|
1323
|
+
helper.processAllOps();
|
|
1324
|
+
assert_1.strict.equal(helper.clients.A.getText(), "HCBA");
|
|
1325
|
+
helper.logger.validate();
|
|
1326
|
+
});
|
|
1327
|
+
it("segment inside locally obliterated segment group is split", () => {
|
|
1328
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
1329
|
+
// CDEF-AB
|
|
1330
|
+
// [C]-DE-(F-(G)-H-A)-B
|
|
1331
|
+
// [C]-DE-(F-(G)-H-A)-(B)
|
|
1332
|
+
helper.insertText("B", 0, "AB");
|
|
1333
|
+
helper.insertText("B", 0, "CDEF");
|
|
1334
|
+
helper.processAllOps();
|
|
1335
|
+
helper.logger.validate();
|
|
1336
|
+
helper.removeRange("C", 0, 1);
|
|
1337
|
+
helper.insertText("C", 3, "GH");
|
|
1338
|
+
helper.obliterateRange("C", 3, 4);
|
|
1339
|
+
helper.obliterateRange("A", 3, 5);
|
|
1340
|
+
helper.processAllOps();
|
|
1341
|
+
helper.logger.validate();
|
|
1342
|
+
helper.obliterateRange("B", 2, 3);
|
|
1343
|
+
helper.processAllOps();
|
|
1344
|
+
assert_1.strict.equal(helper.clients.A.getText(), "DE");
|
|
1345
|
+
helper.logger.validate();
|
|
1346
|
+
});
|
|
1347
|
+
it("continues traversal past locally removed segment inserted before first obliterate", () => {
|
|
1348
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
1349
|
+
// ABC
|
|
1350
|
+
// (E-D-(A)-[B]-C)
|
|
1351
|
+
helper.insertText("A", 0, "ABC");
|
|
1352
|
+
helper.processAllOps();
|
|
1353
|
+
helper.logger.validate();
|
|
1354
|
+
helper.obliterateRange("C", 0, 1);
|
|
1355
|
+
helper.insertText("B", 0, "D");
|
|
1356
|
+
helper.removeRange("C", 0, 1);
|
|
1357
|
+
helper.insertText("C", 0, "E");
|
|
1358
|
+
helper.obliterateRange("C", 0, 2);
|
|
1359
|
+
helper.processAllOps();
|
|
1360
|
+
assert_1.strict.equal(helper.clients.A.getText(), "");
|
|
1361
|
+
helper.logger.validate();
|
|
1362
|
+
});
|
|
1363
|
+
it("keeps track of remote obliterated length when hier node contains hier nodes", () => {
|
|
1364
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
1365
|
+
// LMNO-HIJ-E-K-FG-D-C-AB
|
|
1366
|
+
// lmnopq-L-[M]-NO-H-ghijk-I-(J-E-K-FG-D-C-A-a)-bc-ef-d-B
|
|
1367
|
+
// v--------------------------------------v
|
|
1368
|
+
// v-------------------v
|
|
1369
|
+
// r-l-x-mn-[opq-L-[M]-NO-H-gh-s]-t-(u-i-[j]-k-I-(J-E-K-FG-D-C-A-a)-b-v-c-e)-f-d-B
|
|
1370
|
+
// bad segment: (I-(J-E-K-FG-D-C-A-a)-b-v-c-e)-f-d-B
|
|
1371
|
+
helper.insertText("B", 0, "AB");
|
|
1372
|
+
helper.insertText("C", 0, "C");
|
|
1373
|
+
helper.insertText("A", 0, "D");
|
|
1374
|
+
helper.insertText("B", 0, "EFG");
|
|
1375
|
+
helper.insertText("C", 0, "HIJ");
|
|
1376
|
+
helper.insertText("B", 1, "K");
|
|
1377
|
+
helper.insertText("B", 0, "LMNO");
|
|
1378
|
+
helper.processAllOps();
|
|
1379
|
+
helper.logger.validate();
|
|
1380
|
+
helper.removeRange("A", 1, 2);
|
|
1381
|
+
helper.insertText("A", 13, "abcd");
|
|
1382
|
+
helper.obliterateRange("A", 5, 14);
|
|
1383
|
+
helper.insertText("A", 7, "ef"); // seq: 11, len: 7
|
|
1384
|
+
helper.insertText("B", 5, "ghijk");
|
|
1385
|
+
helper.insertText("C", 0, "lmnopq"); // seq: 13, len: 7
|
|
1386
|
+
helper.processAllOps();
|
|
1387
|
+
assert_1.strict.equal(helper.clients.A.getText(), "lmnopqLNOHghijkIbcefdB");
|
|
1388
|
+
helper.logger.validate();
|
|
1389
|
+
helper.insertText("A", 0, "r");
|
|
1390
|
+
helper.insertText("B", 12, "stu");
|
|
1391
|
+
helper.insertText("A", 18, "v"); // seq: 16, len: 8
|
|
1392
|
+
helper.obliterateRange("B", 14, 22); // seq: 17, len: 3
|
|
1393
|
+
helper.removeRange("B", 3, 13); // seq: 18, len: 3
|
|
1394
|
+
helper.removeRange("A", 14, 15); // seq: 19, len: 3
|
|
1395
|
+
helper.insertText("B", 1, "x");
|
|
1396
|
+
helper.processAllOps();
|
|
1397
|
+
assert_1.strict.equal(helper.clients.A.getText(), "rlxmntfdB");
|
|
1398
|
+
helper.logger.validate();
|
|
1399
|
+
});
|
|
1400
|
+
it("combines remote obliterated length for parent node of tree with depth >=3", () => {
|
|
1401
|
+
const helper = new reconnectHelper_1.ReconnectTestHelper();
|
|
1402
|
+
// VWXYZ0-(K)-[L]-M-[N]-O-EFGHIJ-[A]-B-P-TU-QRS-CD
|
|
1403
|
+
// v-v------v------------v
|
|
1404
|
+
// v--------------------v
|
|
1405
|
+
// v----v---------v-v-----v-----v------------------------------v-v
|
|
1406
|
+
// V-c-W-[XYZ0]-(K)-[L]-[M]-[N]-[O-EFG]-[H]-a-[b-[IJ-[A]-B-P-T]-U-Q]-[R]-S-C-d-f-e-D
|
|
1407
|
+
// v-v------v-v---v-----------v
|
|
1408
|
+
// v-------------------------v
|
|
1409
|
+
// v------v---------v-v-----v---v---v--v-----------------------------------v-v
|
|
1410
|
+
// v---------v
|
|
1411
|
+
// j-i-(V-c-W-[XY)-Z0]-(K)-[L]-[M]-[N]-[O-E]-h-[FG]-[H]-a-[b-[I]-g-[J-[A]-B-P-T]-U-Q]-[R]-S-C-d-f-e-D
|
|
1412
|
+
// problem segment: j-i-(V-c-W-[XY)-Z0]-(K)-[L]-[M]-[N]-[O-E]-h-[FG]-[H]-a-[b]
|
|
1413
|
+
// specifically: j-(V-c-W
|
|
1414
|
+
helper.insertText("C", 0, "ABCD");
|
|
1415
|
+
helper.removeRange("C", 0, 1);
|
|
1416
|
+
helper.insertText("B", 0, "EFGHIJ");
|
|
1417
|
+
helper.insertText("A", 0, "KLMNO");
|
|
1418
|
+
helper.obliterateRange("A", 0, 1);
|
|
1419
|
+
helper.removeRange("A", 0, 1);
|
|
1420
|
+
helper.insertText("C", 1, "PQRS");
|
|
1421
|
+
helper.insertText("C", 2, "TU");
|
|
1422
|
+
helper.removeRange("A", 1, 2);
|
|
1423
|
+
helper.insertText("A", 0, "VWXYZ0"); // seq: 10, len: 2
|
|
1424
|
+
helper.processAllOps();
|
|
1425
|
+
assert_1.strict.equal(helper.clients.A.getText(), "VWXYZ0MOEFGHIJBPTUQRSCD");
|
|
1426
|
+
helper.logger.validate();
|
|
1427
|
+
helper.insertText("C", 12, "ab");
|
|
1428
|
+
helper.removeRange("B", 11, 17);
|
|
1429
|
+
helper.removeRange("C", 13, 21);
|
|
1430
|
+
helper.insertText("B", 1, "c"); // seq: 14, len: 3
|
|
1431
|
+
helper.insertText("C", 16, "de");
|
|
1432
|
+
helper.insertText("C", 17, "f");
|
|
1433
|
+
helper.removeRange("B", 3, 15);
|
|
1434
|
+
helper.insertText("A", 13, "g");
|
|
1435
|
+
helper.insertText("A", 9, "h");
|
|
1436
|
+
helper.obliterateRange("C", 0, 4); // seq: 20, len: 0
|
|
1437
|
+
helper.insertText("C", 0, "i");
|
|
1438
|
+
helper.insertText("A", 0, "j"); // seq: 22, len: 1
|
|
1439
|
+
helper.processAllOps();
|
|
1440
|
+
assert_1.strict.equal(helper.clients.A.getText(), "jihagSCdfeD");
|
|
1441
|
+
helper.logger.validate();
|
|
1442
|
+
});
|
|
1443
|
+
});
|
|
1444
|
+
});
|
|
1445
|
+
}
|
|
1446
|
+
//# sourceMappingURL=obliterate.concurrent.spec.js.map
|