@fluidframework/sequence 2.0.0-rc.1.0.4 → 2.0.0-rc.2.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.
Files changed (379) hide show
  1. package/{.eslintrc.js → .eslintrc.cjs} +4 -1
  2. package/{.mocharc.js → .mocharc.cjs} +1 -1
  3. package/CHANGELOG.md +8 -0
  4. package/README.md +2 -2
  5. package/{api-extractor-esm.json → api-extractor-cjs.json} +5 -1
  6. package/api-extractor.json +1 -1
  7. package/api-report/sequence.api.md +15 -27
  8. package/dist/defaultMap.d.ts +10 -16
  9. package/dist/defaultMap.d.ts.map +1 -1
  10. package/dist/defaultMap.js +73 -70
  11. package/dist/defaultMap.js.map +1 -1
  12. package/dist/defaultMapInterfaces.d.ts +1 -8
  13. package/dist/defaultMapInterfaces.d.ts.map +1 -1
  14. package/dist/defaultMapInterfaces.js.map +1 -1
  15. package/dist/index.d.ts +11 -11
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +42 -42
  18. package/dist/index.js.map +1 -1
  19. package/dist/intervalCollection.d.ts +21 -15
  20. package/dist/intervalCollection.d.ts.map +1 -1
  21. package/dist/intervalCollection.js +59 -110
  22. package/dist/intervalCollection.js.map +1 -1
  23. package/dist/intervalIndex/endpointInRangeIndex.d.ts +3 -3
  24. package/dist/intervalIndex/endpointInRangeIndex.d.ts.map +1 -1
  25. package/dist/intervalIndex/endpointInRangeIndex.js +8 -8
  26. package/dist/intervalIndex/endpointInRangeIndex.js.map +1 -1
  27. package/dist/intervalIndex/endpointIndex.d.ts +3 -3
  28. package/dist/intervalIndex/endpointIndex.d.ts.map +1 -1
  29. package/dist/intervalIndex/endpointIndex.js +4 -4
  30. package/dist/intervalIndex/endpointIndex.js.map +1 -1
  31. package/dist/intervalIndex/idIntervalIndex.d.ts +2 -2
  32. package/dist/intervalIndex/idIntervalIndex.d.ts.map +1 -1
  33. package/dist/intervalIndex/idIntervalIndex.js +2 -2
  34. package/dist/intervalIndex/idIntervalIndex.js.map +1 -1
  35. package/dist/intervalIndex/index.d.ts +8 -8
  36. package/dist/intervalIndex/index.d.ts.map +1 -1
  37. package/dist/intervalIndex/index.js +16 -16
  38. package/dist/intervalIndex/index.js.map +1 -1
  39. package/dist/intervalIndex/intervalIndex.d.ts +1 -1
  40. package/dist/intervalIndex/intervalIndex.d.ts.map +1 -1
  41. package/dist/intervalIndex/intervalIndex.js.map +1 -1
  42. package/dist/intervalIndex/overlappingIntervalsIndex.d.ts +7 -10
  43. package/dist/intervalIndex/overlappingIntervalsIndex.d.ts.map +1 -1
  44. package/dist/intervalIndex/overlappingIntervalsIndex.js +12 -13
  45. package/dist/intervalIndex/overlappingIntervalsIndex.js.map +1 -1
  46. package/dist/intervalIndex/overlappingSequenceIntervalsIndex.d.ts +2 -2
  47. package/dist/intervalIndex/overlappingSequenceIntervalsIndex.d.ts.map +1 -1
  48. package/dist/intervalIndex/overlappingSequenceIntervalsIndex.js +7 -7
  49. package/dist/intervalIndex/overlappingSequenceIntervalsIndex.js.map +1 -1
  50. package/dist/intervalIndex/sequenceIntervalIndexes.d.ts +2 -2
  51. package/dist/intervalIndex/sequenceIntervalIndexes.d.ts.map +1 -1
  52. package/dist/intervalIndex/sequenceIntervalIndexes.js.map +1 -1
  53. package/dist/intervalIndex/startpointInRangeIndex.d.ts +3 -3
  54. package/dist/intervalIndex/startpointInRangeIndex.d.ts.map +1 -1
  55. package/dist/intervalIndex/startpointInRangeIndex.js +8 -8
  56. package/dist/intervalIndex/startpointInRangeIndex.js.map +1 -1
  57. package/dist/intervalTree.d.ts +1 -1
  58. package/dist/intervalTree.d.ts.map +1 -1
  59. package/dist/intervalTree.js.map +1 -1
  60. package/dist/intervals/index.d.ts +3 -3
  61. package/dist/intervals/index.d.ts.map +1 -1
  62. package/dist/intervals/index.js +16 -16
  63. package/dist/intervals/index.js.map +1 -1
  64. package/dist/intervals/interval.d.ts +2 -2
  65. package/dist/intervals/interval.d.ts.map +1 -1
  66. package/dist/intervals/interval.js +2 -2
  67. package/dist/intervals/interval.js.map +1 -1
  68. package/dist/intervals/intervalUtils.d.ts +3 -3
  69. package/dist/intervals/intervalUtils.d.ts.map +1 -1
  70. package/dist/intervals/intervalUtils.js +1 -1
  71. package/dist/intervals/intervalUtils.js.map +1 -1
  72. package/dist/intervals/sequenceInterval.d.ts +2 -2
  73. package/dist/intervals/sequenceInterval.d.ts.map +1 -1
  74. package/dist/intervals/sequenceInterval.js +22 -23
  75. package/dist/intervals/sequenceInterval.js.map +1 -1
  76. package/dist/localValues.d.ts +2 -2
  77. package/dist/localValues.d.ts.map +1 -1
  78. package/dist/localValues.js.map +1 -1
  79. package/dist/package.json +3 -0
  80. package/dist/packageVersion.d.ts +1 -1
  81. package/dist/packageVersion.js +1 -1
  82. package/dist/packageVersion.js.map +1 -1
  83. package/dist/revertibles.d.ts +12 -12
  84. package/dist/revertibles.d.ts.map +1 -1
  85. package/dist/revertibles.js +26 -26
  86. package/dist/revertibles.js.map +1 -1
  87. package/dist/sequence-alpha.d.ts +172 -13
  88. package/dist/sequence-beta.d.ts +2 -0
  89. package/dist/sequence-public.d.ts +2 -0
  90. package/dist/sequence-untrimmed.d.ts +73 -14
  91. package/dist/sequence.d.ts +48 -5
  92. package/dist/sequence.d.ts.map +1 -1
  93. package/dist/sequence.js +103 -29
  94. package/dist/sequence.js.map +1 -1
  95. package/dist/sequenceFactory.d.ts +4 -1
  96. package/dist/sequenceFactory.d.ts.map +1 -1
  97. package/dist/sequenceFactory.js +8 -5
  98. package/dist/sequenceFactory.js.map +1 -1
  99. package/dist/sharedIntervalCollection.d.ts +2 -2
  100. package/dist/sharedIntervalCollection.d.ts.map +1 -1
  101. package/dist/sharedIntervalCollection.js +5 -5
  102. package/dist/sharedIntervalCollection.js.map +1 -1
  103. package/dist/sharedSequence.d.ts +1 -1
  104. package/dist/sharedSequence.d.ts.map +1 -1
  105. package/dist/sharedSequence.js +2 -2
  106. package/dist/sharedSequence.js.map +1 -1
  107. package/dist/sharedString.d.ts +2 -2
  108. package/dist/sharedString.d.ts.map +1 -1
  109. package/dist/sharedString.js +6 -6
  110. package/dist/sharedString.js.map +1 -1
  111. package/dist/tsdoc-metadata.json +1 -1
  112. package/lib/{defaultMap.d.mts → defaultMap.d.ts} +11 -17
  113. package/lib/defaultMap.d.ts.map +1 -0
  114. package/lib/{defaultMap.mjs → defaultMap.js} +72 -69
  115. package/lib/defaultMap.js.map +1 -0
  116. package/lib/{defaultMapInterfaces.d.mts → defaultMapInterfaces.d.ts} +2 -9
  117. package/lib/defaultMapInterfaces.d.ts.map +1 -0
  118. package/lib/{defaultMapInterfaces.mjs → defaultMapInterfaces.js} +1 -1
  119. package/lib/defaultMapInterfaces.js.map +1 -0
  120. package/lib/{index.d.mts → index.d.ts} +24 -12
  121. package/lib/index.d.ts.map +1 -0
  122. package/lib/{index.mjs → index.js} +11 -11
  123. package/lib/index.js.map +1 -0
  124. package/lib/{intervalCollection.d.mts → intervalCollection.d.ts} +22 -16
  125. package/lib/intervalCollection.d.ts.map +1 -0
  126. package/lib/{intervalCollection.mjs → intervalCollection.js} +23 -74
  127. package/lib/intervalCollection.js.map +1 -0
  128. package/lib/intervalIndex/{endpointInRangeIndex.d.mts → endpointInRangeIndex.d.ts} +4 -4
  129. package/lib/intervalIndex/endpointInRangeIndex.d.ts.map +1 -0
  130. package/lib/intervalIndex/{endpointInRangeIndex.mjs → endpointInRangeIndex.js} +3 -3
  131. package/lib/intervalIndex/endpointInRangeIndex.js.map +1 -0
  132. package/lib/intervalIndex/{endpointIndex.d.mts → endpointIndex.d.ts} +4 -4
  133. package/lib/intervalIndex/endpointIndex.d.ts.map +1 -0
  134. package/lib/intervalIndex/{endpointIndex.mjs → endpointIndex.js} +2 -2
  135. package/lib/intervalIndex/endpointIndex.js.map +1 -0
  136. package/lib/intervalIndex/{idIntervalIndex.d.mts → idIntervalIndex.d.ts} +3 -3
  137. package/lib/intervalIndex/idIntervalIndex.d.ts.map +1 -0
  138. package/lib/intervalIndex/{idIntervalIndex.mjs → idIntervalIndex.js} +2 -2
  139. package/lib/intervalIndex/idIntervalIndex.js.map +1 -0
  140. package/lib/intervalIndex/{index.d.mts → index.d.ts} +9 -9
  141. package/lib/intervalIndex/index.d.ts.map +1 -0
  142. package/lib/intervalIndex/{index.mjs → index.js} +7 -7
  143. package/lib/intervalIndex/index.js.map +1 -0
  144. package/lib/intervalIndex/{intervalIndex.d.mts → intervalIndex.d.ts} +2 -2
  145. package/lib/intervalIndex/intervalIndex.d.ts.map +1 -0
  146. package/lib/intervalIndex/{intervalIndex.mjs → intervalIndex.js} +1 -1
  147. package/lib/intervalIndex/intervalIndex.js.map +1 -0
  148. package/lib/intervalIndex/{intervalIndexUtils.d.mts → intervalIndexUtils.d.ts} +1 -1
  149. package/lib/intervalIndex/intervalIndexUtils.d.ts.map +1 -0
  150. package/lib/intervalIndex/{intervalIndexUtils.mjs → intervalIndexUtils.js} +1 -1
  151. package/lib/intervalIndex/intervalIndexUtils.js.map +1 -0
  152. package/lib/intervalIndex/{overlappingIntervalsIndex.d.mts → overlappingIntervalsIndex.d.ts} +8 -11
  153. package/lib/intervalIndex/overlappingIntervalsIndex.d.ts.map +1 -0
  154. package/lib/intervalIndex/{overlappingIntervalsIndex.mjs → overlappingIntervalsIndex.js} +8 -9
  155. package/lib/intervalIndex/overlappingIntervalsIndex.js.map +1 -0
  156. package/lib/intervalIndex/{overlappingSequenceIntervalsIndex.d.mts → overlappingSequenceIntervalsIndex.d.ts} +3 -3
  157. package/lib/intervalIndex/overlappingSequenceIntervalsIndex.d.ts.map +1 -0
  158. package/lib/intervalIndex/{overlappingSequenceIntervalsIndex.mjs → overlappingSequenceIntervalsIndex.js} +3 -3
  159. package/lib/intervalIndex/overlappingSequenceIntervalsIndex.js.map +1 -0
  160. package/lib/intervalIndex/{sequenceIntervalIndexes.d.mts → sequenceIntervalIndexes.d.ts} +3 -3
  161. package/lib/intervalIndex/sequenceIntervalIndexes.d.ts.map +1 -0
  162. package/lib/intervalIndex/{sequenceIntervalIndexes.mjs → sequenceIntervalIndexes.js} +1 -1
  163. package/lib/intervalIndex/sequenceIntervalIndexes.js.map +1 -0
  164. package/lib/intervalIndex/{startpointInRangeIndex.d.mts → startpointInRangeIndex.d.ts} +4 -4
  165. package/lib/intervalIndex/startpointInRangeIndex.d.ts.map +1 -0
  166. package/lib/intervalIndex/{startpointInRangeIndex.mjs → startpointInRangeIndex.js} +3 -3
  167. package/lib/intervalIndex/startpointInRangeIndex.js.map +1 -0
  168. package/lib/{intervalTree.d.mts → intervalTree.d.ts} +2 -2
  169. package/lib/intervalTree.d.ts.map +1 -0
  170. package/lib/{intervalTree.mjs → intervalTree.js} +1 -1
  171. package/lib/intervalTree.js.map +1 -0
  172. package/lib/intervals/{index.d.mts → index.d.ts} +4 -4
  173. package/lib/intervals/index.d.ts.map +1 -0
  174. package/lib/intervals/{index.mjs → index.js} +4 -4
  175. package/lib/intervals/index.js.map +1 -0
  176. package/lib/intervals/{interval.d.mts → interval.d.ts} +3 -3
  177. package/lib/intervals/{interval.d.mts.map → interval.d.ts.map} +1 -1
  178. package/lib/intervals/{interval.mjs → interval.js} +2 -2
  179. package/lib/intervals/interval.js.map +1 -0
  180. package/lib/intervals/{intervalUtils.d.mts → intervalUtils.d.ts} +4 -4
  181. package/lib/intervals/intervalUtils.d.ts.map +1 -0
  182. package/lib/intervals/{intervalUtils.mjs → intervalUtils.js} +3 -2
  183. package/lib/intervals/intervalUtils.js.map +1 -0
  184. package/lib/intervals/{sequenceInterval.d.mts → sequenceInterval.d.ts} +3 -3
  185. package/lib/intervals/sequenceInterval.d.ts.map +1 -0
  186. package/lib/intervals/{sequenceInterval.mjs → sequenceInterval.js} +5 -4
  187. package/lib/intervals/{sequenceInterval.mjs.map → sequenceInterval.js.map} +1 -1
  188. package/lib/{localValues.d.mts → localValues.d.ts} +3 -3
  189. package/lib/{localValues.d.mts.map → localValues.d.ts.map} +1 -1
  190. package/lib/{localValues.mjs → localValues.js} +1 -1
  191. package/lib/localValues.js.map +1 -0
  192. package/lib/{packageVersion.d.mts → packageVersion.d.ts} +2 -2
  193. package/lib/packageVersion.d.ts.map +1 -0
  194. package/lib/{packageVersion.mjs → packageVersion.js} +2 -2
  195. package/lib/packageVersion.js.map +1 -0
  196. package/lib/{revertibles.d.mts → revertibles.d.ts} +13 -13
  197. package/lib/revertibles.d.ts.map +1 -0
  198. package/lib/{revertibles.mjs → revertibles.js} +11 -10
  199. package/lib/revertibles.js.map +1 -0
  200. package/lib/{sequence-alpha.d.mts → sequence-alpha.d.ts} +185 -13
  201. package/lib/{sequence-public.d.mts → sequence-beta.d.ts} +15 -0
  202. package/lib/{sequence-beta.d.mts → sequence-public.d.ts} +15 -0
  203. package/lib/{sequence-untrimmed.d.mts → sequence-untrimmed.d.ts} +86 -14
  204. package/lib/{sequence.d.mts → sequence.d.ts} +49 -6
  205. package/lib/sequence.d.ts.map +1 -0
  206. package/lib/{sequence.mjs → sequence.js} +100 -29
  207. package/lib/sequence.js.map +1 -0
  208. package/lib/{sequenceDeltaEvent.d.mts → sequenceDeltaEvent.d.ts} +1 -1
  209. package/lib/sequenceDeltaEvent.d.ts.map +1 -0
  210. package/lib/{sequenceDeltaEvent.mjs → sequenceDeltaEvent.js} +1 -1
  211. package/lib/sequenceDeltaEvent.js.map +1 -0
  212. package/lib/{sequenceFactory.d.mts → sequenceFactory.d.ts} +5 -2
  213. package/lib/sequenceFactory.d.ts.map +1 -0
  214. package/lib/{sequenceFactory.mjs → sequenceFactory.js} +6 -3
  215. package/lib/sequenceFactory.js.map +1 -0
  216. package/lib/{sharedIntervalCollection.d.mts → sharedIntervalCollection.d.ts} +3 -3
  217. package/lib/sharedIntervalCollection.d.ts.map +1 -0
  218. package/lib/{sharedIntervalCollection.mjs → sharedIntervalCollection.js} +4 -4
  219. package/lib/sharedIntervalCollection.js.map +1 -0
  220. package/lib/{sharedSequence.d.mts → sharedSequence.d.ts} +2 -2
  221. package/lib/sharedSequence.d.ts.map +1 -0
  222. package/lib/{sharedSequence.mjs → sharedSequence.js} +2 -2
  223. package/lib/sharedSequence.js.map +1 -0
  224. package/lib/{sharedString.d.mts → sharedString.d.ts} +3 -3
  225. package/lib/sharedString.d.ts.map +1 -0
  226. package/lib/{sharedString.mjs → sharedString.js} +3 -3
  227. package/lib/sharedString.js.map +1 -0
  228. package/lib/test/collections.intervalTree.js +73 -0
  229. package/lib/test/collections.intervalTree.js.map +1 -0
  230. package/lib/test/createSnapshotFiles.js +15 -0
  231. package/lib/test/createSnapshotFiles.js.map +1 -0
  232. package/lib/test/dirname.cjs +16 -0
  233. package/lib/test/dirname.cjs.map +1 -0
  234. package/lib/test/endpointInRangeIndex.spec.js +182 -0
  235. package/lib/test/endpointInRangeIndex.spec.js.map +1 -0
  236. package/lib/test/fuzz/fuzzUtils.js +250 -0
  237. package/lib/test/fuzz/fuzzUtils.js.map +1 -0
  238. package/lib/test/fuzz/intervalCollection.fuzz.spec.js +200 -0
  239. package/lib/test/fuzz/intervalCollection.fuzz.spec.js.map +1 -0
  240. package/lib/test/fuzz/intervalRevertibles.fuzz.spec.js +129 -0
  241. package/lib/test/fuzz/intervalRevertibles.fuzz.spec.js.map +1 -0
  242. package/lib/test/fuzz/sharedString.fuzz.spec.js +91 -0
  243. package/lib/test/fuzz/sharedString.fuzz.spec.js.map +1 -0
  244. package/lib/test/generateSharedStrings.js +138 -0
  245. package/lib/test/generateSharedStrings.js.map +1 -0
  246. package/lib/test/intervalCollection.detached.spec.js +126 -0
  247. package/lib/test/intervalCollection.detached.spec.js.map +1 -0
  248. package/lib/test/intervalCollection.events.spec.js +491 -0
  249. package/lib/test/intervalCollection.events.spec.js.map +1 -0
  250. package/lib/test/intervalCollection.perf.spec.js +88 -0
  251. package/lib/test/intervalCollection.perf.spec.js.map +1 -0
  252. package/lib/test/intervalCollection.snapshot.spec.js +171 -0
  253. package/lib/test/intervalCollection.snapshot.spec.js.map +1 -0
  254. package/lib/test/intervalCollection.spec.js +1660 -0
  255. package/lib/test/intervalCollection.spec.js.map +1 -0
  256. package/lib/test/intervalIndexTestUtils.js +49 -0
  257. package/lib/test/intervalIndexTestUtils.js.map +1 -0
  258. package/lib/test/intervalRebasing.spec.js +589 -0
  259. package/lib/test/intervalRebasing.spec.js.map +1 -0
  260. package/lib/test/intervalStashedOps.spec.js +142 -0
  261. package/lib/test/intervalStashedOps.spec.js.map +1 -0
  262. package/lib/test/intervalTestUtils.js +81 -0
  263. package/lib/test/intervalTestUtils.js.map +1 -0
  264. package/lib/test/marshalling.spec.js +55 -0
  265. package/lib/test/marshalling.spec.js.map +1 -0
  266. package/lib/test/memory/sharedSequence.spec.js +82 -0
  267. package/lib/test/memory/sharedSequence.spec.js.map +1 -0
  268. package/lib/test/memory/sharedString.spec.js +134 -0
  269. package/lib/test/memory/sharedString.spec.js.map +1 -0
  270. package/lib/test/overlappingSequenceIntervalsIndex.spec.js +348 -0
  271. package/lib/test/overlappingSequenceIntervalsIndex.spec.js.map +1 -0
  272. package/lib/test/partialLoad.spec.js +211 -0
  273. package/lib/test/partialLoad.spec.js.map +1 -0
  274. package/lib/test/rebasing.spec.js +81 -0
  275. package/lib/test/rebasing.spec.js.map +1 -0
  276. package/lib/test/reentrancy.spec.js +174 -0
  277. package/lib/test/reentrancy.spec.js.map +1 -0
  278. package/lib/test/revertibles.spec.js +971 -0
  279. package/lib/test/revertibles.spec.js.map +1 -0
  280. package/lib/test/sequenceDeltaEvent.spec.js +2144 -0
  281. package/lib/test/sequenceDeltaEvent.spec.js.map +1 -0
  282. package/lib/test/sharedIntervalCollection.spec.js +159 -0
  283. package/lib/test/sharedIntervalCollection.spec.js.map +1 -0
  284. package/lib/test/sharedString.spec.js +532 -0
  285. package/lib/test/sharedString.spec.js.map +1 -0
  286. package/lib/test/snapshotEmptyProps.spec.js +45 -0
  287. package/lib/test/snapshotEmptyProps.spec.js.map +1 -0
  288. package/lib/test/snapshotVersion.spec.js +149 -0
  289. package/lib/test/snapshotVersion.spec.js.map +1 -0
  290. package/lib/test/startpointInRangeIndex.spec.js +182 -0
  291. package/lib/test/startpointInRangeIndex.spec.js.map +1 -0
  292. package/lib/test/subSequence.spec.js +92 -0
  293. package/lib/test/subSequence.spec.js.map +1 -0
  294. package/lib/test/types/validateSequencePrevious.generated.js +162 -0
  295. package/lib/test/types/validateSequencePrevious.generated.js.map +1 -0
  296. package/lib/test/v1IntervalCollectionHelpers.js +93 -0
  297. package/lib/test/v1IntervalCollectionHelpers.js.map +1 -0
  298. package/package.json +66 -60
  299. package/src/defaultMap.ts +97 -123
  300. package/src/defaultMapInterfaces.ts +1 -9
  301. package/src/index.ts +15 -11
  302. package/src/intervalCollection.ts +57 -84
  303. package/src/intervalIndex/endpointInRangeIndex.ts +4 -4
  304. package/src/intervalIndex/endpointIndex.ts +3 -3
  305. package/src/intervalIndex/idIntervalIndex.ts +3 -4
  306. package/src/intervalIndex/index.ts +8 -8
  307. package/src/intervalIndex/intervalIndex.ts +1 -1
  308. package/src/intervalIndex/overlappingIntervalsIndex.ts +10 -11
  309. package/src/intervalIndex/overlappingSequenceIntervalsIndex.ts +4 -4
  310. package/src/intervalIndex/sequenceIntervalIndexes.ts +2 -2
  311. package/src/intervalIndex/startpointInRangeIndex.ts +4 -4
  312. package/src/intervalTree.ts +1 -1
  313. package/src/intervals/index.ts +3 -3
  314. package/src/intervals/interval.ts +2 -4
  315. package/src/intervals/intervalUtils.ts +3 -3
  316. package/src/intervals/sequenceInterval.ts +3 -4
  317. package/src/localValues.ts +2 -2
  318. package/src/packageVersion.ts +1 -1
  319. package/src/revertibles.ts +13 -13
  320. package/src/sequence.ts +126 -44
  321. package/src/sequenceFactory.ts +5 -2
  322. package/src/sharedIntervalCollection.ts +5 -5
  323. package/src/sharedSequence.ts +1 -1
  324. package/src/sharedString.ts +2 -2
  325. package/tsconfig.cjs.json +7 -0
  326. package/tsconfig.json +2 -5
  327. package/lib/defaultMap.d.mts.map +0 -1
  328. package/lib/defaultMap.mjs.map +0 -1
  329. package/lib/defaultMapInterfaces.d.mts.map +0 -1
  330. package/lib/defaultMapInterfaces.mjs.map +0 -1
  331. package/lib/index.d.mts.map +0 -1
  332. package/lib/index.mjs.map +0 -1
  333. package/lib/intervalCollection.d.mts.map +0 -1
  334. package/lib/intervalCollection.mjs.map +0 -1
  335. package/lib/intervalIndex/endpointInRangeIndex.d.mts.map +0 -1
  336. package/lib/intervalIndex/endpointInRangeIndex.mjs.map +0 -1
  337. package/lib/intervalIndex/endpointIndex.d.mts.map +0 -1
  338. package/lib/intervalIndex/endpointIndex.mjs.map +0 -1
  339. package/lib/intervalIndex/idIntervalIndex.d.mts.map +0 -1
  340. package/lib/intervalIndex/idIntervalIndex.mjs.map +0 -1
  341. package/lib/intervalIndex/index.d.mts.map +0 -1
  342. package/lib/intervalIndex/index.mjs.map +0 -1
  343. package/lib/intervalIndex/intervalIndex.d.mts.map +0 -1
  344. package/lib/intervalIndex/intervalIndex.mjs.map +0 -1
  345. package/lib/intervalIndex/intervalIndexUtils.d.mts.map +0 -1
  346. package/lib/intervalIndex/intervalIndexUtils.mjs.map +0 -1
  347. package/lib/intervalIndex/overlappingIntervalsIndex.d.mts.map +0 -1
  348. package/lib/intervalIndex/overlappingIntervalsIndex.mjs.map +0 -1
  349. package/lib/intervalIndex/overlappingSequenceIntervalsIndex.d.mts.map +0 -1
  350. package/lib/intervalIndex/overlappingSequenceIntervalsIndex.mjs.map +0 -1
  351. package/lib/intervalIndex/sequenceIntervalIndexes.d.mts.map +0 -1
  352. package/lib/intervalIndex/sequenceIntervalIndexes.mjs.map +0 -1
  353. package/lib/intervalIndex/startpointInRangeIndex.d.mts.map +0 -1
  354. package/lib/intervalIndex/startpointInRangeIndex.mjs.map +0 -1
  355. package/lib/intervalTree.d.mts.map +0 -1
  356. package/lib/intervalTree.mjs.map +0 -1
  357. package/lib/intervals/index.d.mts.map +0 -1
  358. package/lib/intervals/index.mjs.map +0 -1
  359. package/lib/intervals/interval.mjs.map +0 -1
  360. package/lib/intervals/intervalUtils.d.mts.map +0 -1
  361. package/lib/intervals/intervalUtils.mjs.map +0 -1
  362. package/lib/intervals/sequenceInterval.d.mts.map +0 -1
  363. package/lib/localValues.mjs.map +0 -1
  364. package/lib/packageVersion.d.mts.map +0 -1
  365. package/lib/packageVersion.mjs.map +0 -1
  366. package/lib/revertibles.d.mts.map +0 -1
  367. package/lib/revertibles.mjs.map +0 -1
  368. package/lib/sequence.d.mts.map +0 -1
  369. package/lib/sequence.mjs.map +0 -1
  370. package/lib/sequenceDeltaEvent.d.mts.map +0 -1
  371. package/lib/sequenceDeltaEvent.mjs.map +0 -1
  372. package/lib/sequenceFactory.d.mts.map +0 -1
  373. package/lib/sequenceFactory.mjs.map +0 -1
  374. package/lib/sharedIntervalCollection.d.mts.map +0 -1
  375. package/lib/sharedIntervalCollection.mjs.map +0 -1
  376. package/lib/sharedSequence.d.mts.map +0 -1
  377. package/lib/sharedSequence.mjs.map +0 -1
  378. package/lib/sharedString.d.mts.map +0 -1
  379. package/lib/sharedString.mjs.map +0 -1
@@ -0,0 +1,250 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import * as path from "path";
6
+ import { strict as assert } from "assert";
7
+ import { combineReducersAsync as combineReducers, } from "@fluid-private/stochastic-test-utils";
8
+ import { revertSharedStringRevertibles } from "../../revertibles.js";
9
+ import { SharedStringFactory } from "../../sequenceFactory.js";
10
+ import { assertEquivalentSharedStrings } from "../intervalTestUtils.js";
11
+ import { _dirname } from "../dirname.cjs";
12
+ export function isRevertibleSharedString(s) {
13
+ return s.revertibles !== undefined;
14
+ }
15
+ export const defaultSharedStringOperationGenerationConfig = {
16
+ maxStringLength: 1000,
17
+ maxInsertLength: 10,
18
+ weights: {
19
+ addText: 2,
20
+ removeRange: 1,
21
+ obliterateRange: 1,
22
+ },
23
+ };
24
+ export const defaultIntervalOperationGenerationConfig = {
25
+ ...defaultSharedStringOperationGenerationConfig,
26
+ maxIntervals: 100,
27
+ intervalCollectionNamePool: ["comments"],
28
+ propertyNamePool: ["prop1", "prop2", "prop3"],
29
+ validateInterval: 100,
30
+ weights: {
31
+ ...defaultSharedStringOperationGenerationConfig.weights,
32
+ revertWeight: 2,
33
+ addInterval: 2,
34
+ deleteInterval: 2,
35
+ changeInterval: 2,
36
+ obliterateRange: 0,
37
+ },
38
+ };
39
+ function logCurrentState(state, loggingInfo) {
40
+ for (const id of loggingInfo.clientIds) {
41
+ const { channel } = state.clients.find((s) => s.containerRuntime.clientId === id) ?? {};
42
+ assert(channel);
43
+ const labels = channel.getIntervalCollectionLabels();
44
+ const interval = Array.from(labels)
45
+ .map((label) => channel.getIntervalCollection(label).getIntervalById(loggingInfo.intervalId))
46
+ .find((result) => result !== undefined);
47
+ console.log(`Client ${id}:`);
48
+ if (interval !== undefined) {
49
+ const start = channel.localReferencePositionToPosition(interval.start);
50
+ const end = channel.localReferencePositionToPosition(interval.end);
51
+ if (end === start) {
52
+ console.log(`${" ".repeat(start)}x`);
53
+ }
54
+ else {
55
+ console.log(`${" ".repeat(start)}[${" ".repeat(end - start - 1)}]`);
56
+ }
57
+ }
58
+ console.log(channel.getText());
59
+ console.log("\n");
60
+ }
61
+ }
62
+ export function makeReducer(loggingInfo) {
63
+ const withLogging = (baseReducer) => async (state, operation) => {
64
+ if (loggingInfo !== undefined) {
65
+ logCurrentState(state, loggingInfo);
66
+ console.log("-".repeat(20));
67
+ console.log("Next operation:", JSON.stringify(operation, undefined, 4));
68
+ }
69
+ await baseReducer(state, operation);
70
+ };
71
+ const reducer = combineReducers({
72
+ addText: async ({ client }, { index, content }) => {
73
+ client.channel.insertText(index, content);
74
+ },
75
+ removeRange: async ({ client }, { start, end }) => {
76
+ client.channel.removeRange(start, end);
77
+ },
78
+ obliterateRange: async ({ client }, { start, end }) => {
79
+ client.channel.obliterateRange(start, end);
80
+ },
81
+ addInterval: async ({ client }, { start, end, collectionName, id, startSide, endSide }) => {
82
+ const collection = client.channel.getIntervalCollection(collectionName);
83
+ collection.add({
84
+ start: { pos: start, side: startSide },
85
+ end: { pos: end, side: endSide },
86
+ props: { intervalId: id },
87
+ });
88
+ },
89
+ deleteInterval: async ({ client }, { id, collectionName }) => {
90
+ const collection = client.channel.getIntervalCollection(collectionName);
91
+ collection.removeIntervalById(id);
92
+ },
93
+ changeInterval: async ({ client }, { id, start, end, collectionName, startSide, endSide, properties }) => {
94
+ const collection = client.channel.getIntervalCollection(collectionName);
95
+ if (start !== undefined && end !== undefined) {
96
+ collection.change(id, {
97
+ start: { pos: start, side: startSide },
98
+ end: { pos: end, side: endSide },
99
+ props: properties,
100
+ });
101
+ }
102
+ else {
103
+ collection.change(id, { props: properties });
104
+ }
105
+ },
106
+ revertSharedStringRevertibles: async ({ client }, { editsToRevert }) => {
107
+ assert(isRevertibleSharedString(client.channel));
108
+ client.channel.isCurrentRevert = true;
109
+ const few = client.channel.revertibles.splice(-editsToRevert, editsToRevert);
110
+ revertSharedStringRevertibles(client.channel, few);
111
+ client.channel.isCurrentRevert = false;
112
+ },
113
+ });
114
+ return withLogging(reducer);
115
+ }
116
+ export function createSharedStringGeneratorOperations(optionsParam) {
117
+ const options = { ...defaultSharedStringOperationGenerationConfig, ...(optionsParam ?? {}) };
118
+ // All subsequent helper functions are generators; note that they don't actually apply any operations.
119
+ function startPosition({ random, client }) {
120
+ return random.integer(0, Math.max(0, client.channel.getLength() - 1));
121
+ }
122
+ function exclusiveRange(state) {
123
+ const start = startPosition(state);
124
+ const end = state.random.integer(start + 1, state.client.channel.getLength());
125
+ return { start, end };
126
+ }
127
+ function exclusiveRangeLeaveChar(state) {
128
+ const start = state.random.integer(0, state.client.channel.getLength() - 2);
129
+ const end = state.random.integer(start + 1, state.client.channel.getLength() - 1);
130
+ return { start, end };
131
+ }
132
+ async function addText(state) {
133
+ const { random, client } = state;
134
+ return {
135
+ type: "addText",
136
+ index: random.integer(0, client.channel.getLength()),
137
+ content: random.string(random.integer(0, options.maxInsertLength)),
138
+ };
139
+ }
140
+ async function obliterateRange(state) {
141
+ return {
142
+ type: "obliterateRange",
143
+ ...exclusiveRange(state),
144
+ };
145
+ }
146
+ async function removeRange(state) {
147
+ return { type: "removeRange", ...exclusiveRange(state) };
148
+ }
149
+ async function removeRangeLeaveChar(state) {
150
+ return { type: "removeRange", ...exclusiveRangeLeaveChar(state) };
151
+ }
152
+ const lengthSatisfies = (criteria) => ({ client }) => criteria(client.channel.getLength());
153
+ const hasNonzeroLength = lengthSatisfies((length) => length > 0);
154
+ const isShorterThanMaxLength = lengthSatisfies((length) => length < options.maxStringLength);
155
+ return {
156
+ startPosition,
157
+ exclusiveRange,
158
+ exclusiveRangeLeaveChar,
159
+ addText,
160
+ obliterateRange,
161
+ removeRange,
162
+ removeRangeLeaveChar,
163
+ lengthSatisfies,
164
+ hasNonzeroLength,
165
+ isShorterThanMaxLength,
166
+ };
167
+ }
168
+ export class SharedStringFuzzFactory extends SharedStringFactory {
169
+ async load(runtime, id, services, attributes) {
170
+ runtime.options.intervalStickinessEnabled = true;
171
+ runtime.options.mergeTreeEnableObliterate = true;
172
+ return super.load(runtime, id, services, attributes);
173
+ }
174
+ create(document, id) {
175
+ document.options.intervalStickinessEnabled = true;
176
+ document.options.mergeTreeEnableObliterate = true;
177
+ return super.create(document, id);
178
+ }
179
+ }
180
+ export const baseModel = {
181
+ reducer:
182
+ // makeReducer supports a param for logging output which tracks the provided intervalId over time:
183
+ // { intervalId: "00000000-0000-0000-0000-000000000000", clientIds: ["A", "B", "C"] }
184
+ makeReducer(),
185
+ validateConsistency: assertEquivalentSharedStrings,
186
+ factory: new SharedStringFuzzFactory(),
187
+ minimizationTransforms: [
188
+ (op) => {
189
+ if (op.type !== "addText") {
190
+ return;
191
+ }
192
+ op.content = op.content.slice(1);
193
+ },
194
+ (op) => {
195
+ switch (op.type) {
196
+ case "addText":
197
+ if (op.index > 0) {
198
+ op.index -= 1;
199
+ }
200
+ break;
201
+ case "removeRange":
202
+ case "addInterval":
203
+ case "changeInterval":
204
+ if (op.start !== undefined && op.start > 0) {
205
+ op.start -= 1;
206
+ }
207
+ if (op.end !== undefined && op.end > 0) {
208
+ op.end -= 1;
209
+ }
210
+ break;
211
+ default:
212
+ break;
213
+ }
214
+ },
215
+ (op) => {
216
+ if (op.type !== "removeRange" &&
217
+ op.type !== "addInterval" &&
218
+ op.type !== "changeInterval") {
219
+ return;
220
+ }
221
+ if (op.end !== undefined && op.end > 0) {
222
+ op.end -= 1;
223
+ }
224
+ },
225
+ ],
226
+ };
227
+ export const defaultFuzzOptions = {
228
+ validationStrategy: { type: "fixedInterval", interval: 10 },
229
+ reconnectProbability: 0.1,
230
+ numberOfClients: 3,
231
+ clientJoinOptions: {
232
+ maxNumberOfClients: 6,
233
+ clientAddProbability: 0.1,
234
+ },
235
+ defaultTestCount: 100,
236
+ saveFailures: { directory: path.join(_dirname, "../../src/test/fuzz/results") },
237
+ parseOperations: (serialized) => {
238
+ const operations = JSON.parse(serialized);
239
+ // Replace this value with some other interval ID and uncomment to filter replay of the test
240
+ // suite to only include interval operations with this ID.
241
+ // const filterIntervalId = "00000000-0000-0000-0000-000000000000";
242
+ // if (filterIntervalId) {
243
+ // return operations.filter((entry) =>
244
+ // [undefined, filterIntervalId].includes((entry as any).id),
245
+ // );
246
+ // }
247
+ return operations;
248
+ },
249
+ };
250
+ //# sourceMappingURL=fuzzUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fuzzUtils.js","sourceRoot":"","sources":["../../../src/test/fuzz/fuzzUtils.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\nimport * as path from \"path\";\nimport { strict as assert } from \"assert\";\nimport {\n\tAcceptanceCondition,\n\tcombineReducersAsync as combineReducers,\n\tAsyncReducer as Reducer,\n} from \"@fluid-private/stochastic-test-utils\";\nimport { DDSFuzzModel, DDSFuzzSuiteOptions, DDSFuzzTestState } from \"@fluid-private/test-dds-utils\";\nimport {\n\tIChannelAttributes,\n\tIChannelServices,\n\tIFluidDataStoreRuntime,\n} from \"@fluidframework/datastore-definitions\";\nimport { PropertySet } from \"@fluidframework/merge-tree\";\nimport { revertSharedStringRevertibles, SharedStringRevertible } from \"../../revertibles.js\";\nimport { SharedStringFactory } from \"../../sequenceFactory.js\";\nimport { SharedString } from \"../../sharedString.js\";\nimport { Side } from \"../../intervalCollection.js\";\nimport { assertEquivalentSharedStrings } from \"../intervalTestUtils.js\";\nimport { _dirname } from \"../dirname.cjs\";\n\nexport type RevertibleSharedString = SharedString & {\n\trevertibles: SharedStringRevertible[];\n\t// This field prevents change events that are emitted while in the process of a revert from\n\t// being added into the revertibles stack.\n\tisCurrentRevert: boolean;\n};\nexport function isRevertibleSharedString(s: SharedString): s is RevertibleSharedString {\n\treturn (s as RevertibleSharedString).revertibles !== undefined;\n}\n\nexport interface RangeSpec {\n\tstart: number;\n\tend: number;\n}\n\nexport interface IntervalCollectionSpec {\n\tcollectionName: string;\n}\n\nexport interface AddText {\n\ttype: \"addText\";\n\tindex: number;\n\tcontent: string;\n}\n\nexport interface RemoveRange extends RangeSpec {\n\ttype: \"removeRange\";\n}\n\nexport interface ObliterateRange extends RangeSpec {\n\ttype: \"obliterateRange\";\n}\n\n// For non-interval collection fuzzing, annotating text would also be useful.\nexport interface AddInterval extends IntervalCollectionSpec, RangeSpec {\n\ttype: \"addInterval\";\n\t// Normally interval ids get autogenerated, but including it here allows tracking\n\t// what happened to an interval over the course of its lifetime based on the history\n\t// file, which is useful for debugging test failures.\n\tid: string;\n\tstartSide: Side;\n\tendSide: Side;\n}\n\nexport interface ChangeInterval extends IntervalCollectionSpec {\n\ttype: \"changeInterval\";\n\tstart: number | undefined;\n\tend: number | undefined;\n\tid: string;\n\tstartSide: Side;\n\tendSide: Side;\n\tproperties: PropertySet | undefined;\n}\n\nexport interface DeleteInterval extends IntervalCollectionSpec {\n\ttype: \"deleteInterval\";\n\tid: string;\n}\n\nexport interface RevertSharedStringRevertibles {\n\ttype: \"revertSharedStringRevertibles\";\n\teditsToRevert: number;\n}\n\nexport interface RevertibleWeights {\n\trevertWeight: number;\n\taddText: number;\n\tremoveRange: number;\n\tobliterateRange: number;\n\taddInterval: number;\n\tdeleteInterval: number;\n\tchangeInterval: number;\n}\n\nexport type IntervalOperation = AddInterval | ChangeInterval | DeleteInterval;\nexport type OperationWithRevert = IntervalOperation | RevertSharedStringRevertibles;\nexport type TextOperation = AddText | RemoveRange | ObliterateRange;\n\nexport type ClientOperation = IntervalOperation | TextOperation;\n\nexport type RevertOperation = OperationWithRevert | TextOperation;\nexport type Operation = RevertOperation;\n\nexport type FuzzTestState = DDSFuzzTestState<SharedStringFactory>;\n\nexport interface SharedStringOperationGenerationConfig {\n\t/**\n\t * Maximum length of the SharedString (locally) before no further AddText operations are generated.\n\t * Note due to concurrency, during test execution the actual length of the string may exceed this.\n\t */\n\tmaxStringLength?: number;\n\tmaxInsertLength?: number;\n\tweights?: {\n\t\taddText: number;\n\t\tremoveRange: number;\n\t\tobliterateRange: number;\n\t};\n}\n\nexport interface IntervalOperationGenerationConfig extends SharedStringOperationGenerationConfig {\n\t/**\n\t * Maximum number of intervals (locally) before no further AddInterval operations are generated.\n\t * Note due to concurrency, during test execution the actual number of intervals may exceed this.\n\t */\n\tmaxIntervals?: number;\n\tintervalCollectionNamePool?: string[];\n\tpropertyNamePool?: string[];\n\tvalidateInterval?: number;\n\tweights?: RevertibleWeights & SharedStringOperationGenerationConfig[\"weights\"];\n}\n\nexport const defaultSharedStringOperationGenerationConfig: Required<SharedStringOperationGenerationConfig> =\n\t{\n\t\tmaxStringLength: 1000,\n\t\tmaxInsertLength: 10,\n\t\tweights: {\n\t\t\taddText: 2,\n\t\t\tremoveRange: 1,\n\t\t\tobliterateRange: 1,\n\t\t},\n\t};\nexport const defaultIntervalOperationGenerationConfig: Required<IntervalOperationGenerationConfig> =\n\t{\n\t\t...defaultSharedStringOperationGenerationConfig,\n\t\tmaxIntervals: 100,\n\t\tintervalCollectionNamePool: [\"comments\"],\n\t\tpropertyNamePool: [\"prop1\", \"prop2\", \"prop3\"],\n\t\tvalidateInterval: 100,\n\t\tweights: {\n\t\t\t...defaultSharedStringOperationGenerationConfig.weights,\n\t\t\trevertWeight: 2,\n\t\t\taddInterval: 2,\n\t\t\tdeleteInterval: 2,\n\t\t\tchangeInterval: 2,\n\t\t\tobliterateRange: 0,\n\t\t},\n\t};\n\nexport interface LoggingInfo {\n\t/** id of the interval to track over time */\n\tintervalId: string;\n\t/** Clients to print */\n\tclientIds: string[];\n}\n\nfunction logCurrentState(state: FuzzTestState, loggingInfo: LoggingInfo): void {\n\tfor (const id of loggingInfo.clientIds) {\n\t\tconst { channel } = state.clients.find((s) => s.containerRuntime.clientId === id) ?? {};\n\t\tassert(channel);\n\t\tconst labels = channel.getIntervalCollectionLabels();\n\t\tconst interval = Array.from(labels)\n\t\t\t.map((label) =>\n\t\t\t\tchannel.getIntervalCollection(label).getIntervalById(loggingInfo.intervalId),\n\t\t\t)\n\t\t\t.find((result) => result !== undefined);\n\n\t\tconsole.log(`Client ${id}:`);\n\t\tif (interval !== undefined) {\n\t\t\tconst start = channel.localReferencePositionToPosition(interval.start);\n\t\t\tconst end = channel.localReferencePositionToPosition(interval.end);\n\t\t\tif (end === start) {\n\t\t\t\tconsole.log(`${\" \".repeat(start)}x`);\n\t\t\t} else {\n\t\t\t\tconsole.log(`${\" \".repeat(start)}[${\" \".repeat(end - start - 1)}]`);\n\t\t\t}\n\t\t}\n\t\tconsole.log(channel.getText());\n\t\tconsole.log(\"\\n\");\n\t}\n}\n\ntype ClientOpState = FuzzTestState;\n\nexport function makeReducer(\n\tloggingInfo?: LoggingInfo,\n): Reducer<Operation | RevertOperation, ClientOpState> {\n\tconst withLogging =\n\t\t<T>(baseReducer: Reducer<T, ClientOpState>): Reducer<T, ClientOpState> =>\n\t\tasync (state, operation) => {\n\t\t\tif (loggingInfo !== undefined) {\n\t\t\t\tlogCurrentState(state, loggingInfo);\n\t\t\t\tconsole.log(\"-\".repeat(20));\n\t\t\t\tconsole.log(\"Next operation:\", JSON.stringify(operation, undefined, 4));\n\t\t\t}\n\t\t\tawait baseReducer(state, operation);\n\t\t};\n\n\tconst reducer = combineReducers<Operation | RevertOperation, ClientOpState>({\n\t\taddText: async ({ client }, { index, content }) => {\n\t\t\tclient.channel.insertText(index, content);\n\t\t},\n\t\tremoveRange: async ({ client }, { start, end }) => {\n\t\t\tclient.channel.removeRange(start, end);\n\t\t},\n\t\tobliterateRange: async ({ client }, { start, end }) => {\n\t\t\tclient.channel.obliterateRange(start, end);\n\t\t},\n\t\taddInterval: async ({ client }, { start, end, collectionName, id, startSide, endSide }) => {\n\t\t\tconst collection = client.channel.getIntervalCollection(collectionName);\n\t\t\tcollection.add({\n\t\t\t\tstart: { pos: start, side: startSide },\n\t\t\t\tend: { pos: end, side: endSide },\n\t\t\t\tprops: { intervalId: id },\n\t\t\t});\n\t\t},\n\t\tdeleteInterval: async ({ client }, { id, collectionName }) => {\n\t\t\tconst collection = client.channel.getIntervalCollection(collectionName);\n\t\t\tcollection.removeIntervalById(id);\n\t\t},\n\t\tchangeInterval: async (\n\t\t\t{ client },\n\t\t\t{ id, start, end, collectionName, startSide, endSide, properties },\n\t\t) => {\n\t\t\tconst collection = client.channel.getIntervalCollection(collectionName);\n\t\t\tif (start !== undefined && end !== undefined) {\n\t\t\t\tcollection.change(id, {\n\t\t\t\t\tstart: { pos: start, side: startSide },\n\t\t\t\t\tend: { pos: end, side: endSide },\n\t\t\t\t\tprops: properties,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tcollection.change(id, { props: properties });\n\t\t\t}\n\t\t},\n\t\trevertSharedStringRevertibles: async ({ client }, { editsToRevert }) => {\n\t\t\tassert(isRevertibleSharedString(client.channel));\n\t\t\tclient.channel.isCurrentRevert = true;\n\t\t\tconst few = client.channel.revertibles.splice(-editsToRevert, editsToRevert);\n\t\t\trevertSharedStringRevertibles(client.channel, few);\n\t\t\tclient.channel.isCurrentRevert = false;\n\t\t},\n\t});\n\n\treturn withLogging(reducer);\n}\n\nexport function createSharedStringGeneratorOperations(\n\toptionsParam?: SharedStringOperationGenerationConfig,\n) {\n\tconst options = { ...defaultSharedStringOperationGenerationConfig, ...(optionsParam ?? {}) };\n\n\t// All subsequent helper functions are generators; note that they don't actually apply any operations.\n\tfunction startPosition({ random, client }: ClientOpState): number {\n\t\treturn random.integer(0, Math.max(0, client.channel.getLength() - 1));\n\t}\n\n\tfunction exclusiveRange(state: ClientOpState): RangeSpec {\n\t\tconst start = startPosition(state);\n\t\tconst end = state.random.integer(start + 1, state.client.channel.getLength());\n\t\treturn { start, end };\n\t}\n\n\tfunction exclusiveRangeLeaveChar(state: ClientOpState): RangeSpec {\n\t\tconst start = state.random.integer(0, state.client.channel.getLength() - 2);\n\t\tconst end = state.random.integer(start + 1, state.client.channel.getLength() - 1);\n\t\treturn { start, end };\n\t}\n\n\tasync function addText(state: ClientOpState): Promise<AddText> {\n\t\tconst { random, client } = state;\n\t\treturn {\n\t\t\ttype: \"addText\",\n\t\t\tindex: random.integer(0, client.channel.getLength()),\n\t\t\tcontent: random.string(random.integer(0, options.maxInsertLength)),\n\t\t};\n\t}\n\n\tasync function obliterateRange(state: ClientOpState): Promise<ObliterateRange> {\n\t\treturn {\n\t\t\ttype: \"obliterateRange\",\n\t\t\t...exclusiveRange(state),\n\t\t};\n\t}\n\n\tasync function removeRange(state: ClientOpState): Promise<RemoveRange> {\n\t\treturn { type: \"removeRange\", ...exclusiveRange(state) };\n\t}\n\n\tasync function removeRangeLeaveChar(state: ClientOpState): Promise<RemoveRange> {\n\t\treturn { type: \"removeRange\", ...exclusiveRangeLeaveChar(state) };\n\t}\n\n\tconst lengthSatisfies =\n\t\t(criteria: (length: number) => boolean): AcceptanceCondition<ClientOpState> =>\n\t\t({ client }) =>\n\t\t\tcriteria(client.channel.getLength());\n\tconst hasNonzeroLength = lengthSatisfies((length) => length > 0);\n\tconst isShorterThanMaxLength = lengthSatisfies((length) => length < options.maxStringLength);\n\n\treturn {\n\t\tstartPosition,\n\t\texclusiveRange,\n\t\texclusiveRangeLeaveChar,\n\t\taddText,\n\t\tobliterateRange,\n\t\tremoveRange,\n\t\tremoveRangeLeaveChar,\n\t\tlengthSatisfies,\n\t\thasNonzeroLength,\n\t\tisShorterThanMaxLength,\n\t};\n}\n\nexport class SharedStringFuzzFactory extends SharedStringFactory {\n\tpublic async load(\n\t\truntime: IFluidDataStoreRuntime,\n\t\tid: string,\n\t\tservices: IChannelServices,\n\t\tattributes: IChannelAttributes,\n\t): Promise<SharedString> {\n\t\truntime.options.intervalStickinessEnabled = true;\n\t\truntime.options.mergeTreeEnableObliterate = true;\n\t\treturn super.load(runtime, id, services, attributes);\n\t}\n\n\tpublic create(document: IFluidDataStoreRuntime, id: string): SharedString {\n\t\tdocument.options.intervalStickinessEnabled = true;\n\t\tdocument.options.mergeTreeEnableObliterate = true;\n\t\treturn super.create(document, id);\n\t}\n}\n\nexport const baseModel: Omit<\n\tDDSFuzzModel<SharedStringFactory, Operation, FuzzTestState>,\n\t\"workloadName\" | \"generatorFactory\"\n> = {\n\treducer:\n\t\t// makeReducer supports a param for logging output which tracks the provided intervalId over time:\n\t\t// { intervalId: \"00000000-0000-0000-0000-000000000000\", clientIds: [\"A\", \"B\", \"C\"] }\n\t\tmakeReducer(),\n\tvalidateConsistency: assertEquivalentSharedStrings,\n\tfactory: new SharedStringFuzzFactory(),\n\tminimizationTransforms: [\n\t\t(op) => {\n\t\t\tif (op.type !== \"addText\") {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\top.content = op.content.slice(1);\n\t\t},\n\t\t(op) => {\n\t\t\tswitch (op.type) {\n\t\t\t\tcase \"addText\":\n\t\t\t\t\tif (op.index > 0) {\n\t\t\t\t\t\top.index -= 1;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"removeRange\":\n\t\t\t\tcase \"addInterval\":\n\t\t\t\tcase \"changeInterval\":\n\t\t\t\t\tif (op.start !== undefined && op.start > 0) {\n\t\t\t\t\t\top.start -= 1;\n\t\t\t\t\t}\n\t\t\t\t\tif (op.end !== undefined && op.end > 0) {\n\t\t\t\t\t\top.end -= 1;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t},\n\t\t(op) => {\n\t\t\tif (\n\t\t\t\top.type !== \"removeRange\" &&\n\t\t\t\top.type !== \"addInterval\" &&\n\t\t\t\top.type !== \"changeInterval\"\n\t\t\t) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (op.end !== undefined && op.end > 0) {\n\t\t\t\top.end -= 1;\n\t\t\t}\n\t\t},\n\t],\n};\n\nexport const defaultFuzzOptions: Partial<DDSFuzzSuiteOptions> = {\n\tvalidationStrategy: { type: \"fixedInterval\", interval: 10 },\n\treconnectProbability: 0.1,\n\tnumberOfClients: 3,\n\tclientJoinOptions: {\n\t\tmaxNumberOfClients: 6,\n\t\tclientAddProbability: 0.1,\n\t},\n\tdefaultTestCount: 100,\n\tsaveFailures: { directory: path.join(_dirname, \"../../src/test/fuzz/results\") },\n\tparseOperations: (serialized: string) => {\n\t\tconst operations: Operation[] = JSON.parse(serialized);\n\t\t// Replace this value with some other interval ID and uncomment to filter replay of the test\n\t\t// suite to only include interval operations with this ID.\n\t\t// const filterIntervalId = \"00000000-0000-0000-0000-000000000000\";\n\t\t// if (filterIntervalId) {\n\t\t// \treturn operations.filter((entry) =>\n\t\t// \t\t[undefined, filterIntervalId].includes((entry as any).id),\n\t\t// \t);\n\t\t// }\n\t\treturn operations;\n\t},\n};\n"]}
@@ -0,0 +1,200 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import { strict as assert } from "assert";
6
+ import { createWeightedAsyncGenerator as createWeightedGenerator, takeAsync as take, } from "@fluid-private/stochastic-test-utils";
7
+ import { createDDSFuzzSuite } from "@fluid-private/test-dds-utils";
8
+ import { FlushMode } from "@fluidframework/runtime-definitions";
9
+ import { Side } from "../../intervalCollection.js";
10
+ import { defaultIntervalOperationGenerationConfig, createSharedStringGeneratorOperations, baseModel, defaultFuzzOptions, } from "./fuzzUtils.js";
11
+ export function makeOperationGenerator(optionsParam, alwaysLeaveChar = false) {
12
+ const { startPosition, addText, obliterateRange, removeRange, removeRangeLeaveChar, lengthSatisfies, hasNonzeroLength, isShorterThanMaxLength, } = createSharedStringGeneratorOperations(optionsParam);
13
+ const options = { ...defaultIntervalOperationGenerationConfig, ...(optionsParam ?? {}) };
14
+ function isNonEmpty(collection) {
15
+ for (const _ of collection) {
16
+ return true;
17
+ }
18
+ return false;
19
+ }
20
+ function inclusiveRange(state) {
21
+ const start = startPosition(state);
22
+ const end = state.random.integer(start, Math.max(start, state.client.channel.getLength() - 1));
23
+ return { start, end };
24
+ }
25
+ function inclusiveRangeWithUndefined(state) {
26
+ return state.random.bool() ? inclusiveRange(state) : { start: undefined, end: undefined };
27
+ }
28
+ function propertySet(state) {
29
+ const propNamesShuffled = [...options.propertyNamePool];
30
+ state.random.shuffle(propNamesShuffled);
31
+ const propsToChange = propNamesShuffled.slice(0, state.random.integer(1, propNamesShuffled.length));
32
+ const propSet = {};
33
+ for (const name of propsToChange) {
34
+ propSet[name] = state.random.string(5);
35
+ }
36
+ return propSet;
37
+ }
38
+ function propertySetWithUndefined(state) {
39
+ return state.random.bool() ? propertySet(state) : undefined;
40
+ }
41
+ function nonEmptyIntervalCollection({ client, random }) {
42
+ const nonEmptyLabels = Array.from(client.channel.getIntervalCollectionLabels()).filter((label) => {
43
+ const collection = client.channel.getIntervalCollection(label);
44
+ return isNonEmpty(collection);
45
+ });
46
+ return random.pick(nonEmptyLabels);
47
+ }
48
+ function interval(state) {
49
+ const collectionName = nonEmptyIntervalCollection(state);
50
+ const intervals = Array.from(state.client.channel.getIntervalCollection(collectionName));
51
+ const id = state.random.pick(intervals)?.getIntervalId();
52
+ assert(id);
53
+ return {
54
+ id,
55
+ collectionName,
56
+ };
57
+ }
58
+ async function addInterval(state) {
59
+ return {
60
+ type: "addInterval",
61
+ ...inclusiveRange(state),
62
+ collectionName: state.random.pick(options.intervalCollectionNamePool),
63
+ id: state.random.uuid4(),
64
+ startSide: state.random.pick([Side.Before, Side.After]),
65
+ endSide: state.random.pick([Side.Before, Side.After]),
66
+ };
67
+ }
68
+ async function deleteInterval(state) {
69
+ return {
70
+ type: "deleteInterval",
71
+ ...interval(state),
72
+ };
73
+ }
74
+ async function changeInterval(state) {
75
+ const { start, end } = inclusiveRangeWithUndefined(state);
76
+ const properties = propertySetWithUndefined(state);
77
+ return {
78
+ type: "changeInterval",
79
+ start,
80
+ end,
81
+ startSide: state.random.pick([Side.Before, Side.After]),
82
+ endSide: state.random.pick([Side.Before, Side.After]),
83
+ properties,
84
+ ...interval(state),
85
+ };
86
+ }
87
+ const hasAnInterval = ({ client }) => Array.from(client.channel.getIntervalCollectionLabels()).some((label) => {
88
+ const collection = client.channel.getIntervalCollection(label);
89
+ return isNonEmpty(collection);
90
+ });
91
+ const hasNotTooManyIntervals = ({ client }) => {
92
+ let intervalCount = 0;
93
+ for (const label of client.channel.getIntervalCollectionLabels()) {
94
+ for (const _ of client.channel.getIntervalCollection(label)) {
95
+ intervalCount++;
96
+ if (intervalCount >= options.maxIntervals) {
97
+ return false;
98
+ }
99
+ }
100
+ }
101
+ return true;
102
+ };
103
+ const all = (...clauses) => (t) => clauses.reduce((prev, cond) => prev && cond(t), true);
104
+ const usableWeights = optionsParam?.weights ?? defaultIntervalOperationGenerationConfig.weights;
105
+ return createWeightedGenerator([
106
+ [addText, usableWeights.addText, isShorterThanMaxLength],
107
+ [
108
+ alwaysLeaveChar ? removeRangeLeaveChar : removeRange,
109
+ usableWeights.removeRange,
110
+ alwaysLeaveChar
111
+ ? lengthSatisfies((length) => {
112
+ return length > 1;
113
+ })
114
+ : hasNonzeroLength,
115
+ ],
116
+ [obliterateRange, usableWeights.obliterateRange, hasNonzeroLength],
117
+ [addInterval, usableWeights.addInterval, all(hasNotTooManyIntervals, hasNonzeroLength)],
118
+ [deleteInterval, usableWeights.deleteInterval, hasAnInterval],
119
+ [changeInterval, usableWeights.changeInterval, all(hasAnInterval, hasNonzeroLength)],
120
+ ]);
121
+ }
122
+ const baseIntervalModel = {
123
+ ...baseModel,
124
+ generatorFactory: () => take(100, makeOperationGenerator(defaultIntervalOperationGenerationConfig)),
125
+ };
126
+ describe("IntervalCollection fuzz testing", () => {
127
+ const model = {
128
+ ...baseIntervalModel,
129
+ workloadName: "default interval collection",
130
+ };
131
+ createDDSFuzzSuite(model, {
132
+ ...defaultFuzzOptions,
133
+ skip: [68],
134
+ // Note: there are some known eventual consistency issues which the tests don't currently reproduce.
135
+ // Search this package for AB#6552 (or look at that work item) for a skipped test and further details.
136
+ // Uncomment this line to replay a specific seed from its failure file:
137
+ // replay: 0,
138
+ });
139
+ });
140
+ describe("IntervalCollection with stashing", () => {
141
+ const model = {
142
+ ...baseIntervalModel,
143
+ workloadName: "default interval collection with stashing",
144
+ };
145
+ createDDSFuzzSuite(model, {
146
+ ...defaultFuzzOptions,
147
+ clientJoinOptions: {
148
+ clientAddProbability: 0.1,
149
+ maxNumberOfClients: Number.MAX_SAFE_INTEGER,
150
+ stashableClientProbability: 0.2,
151
+ },
152
+ // AB#7220
153
+ skip: [68],
154
+ // Uncomment this line to replay a specific seed from its failure file:
155
+ // replay: 0,
156
+ });
157
+ });
158
+ describe("IntervalCollection no reconnect fuzz testing", () => {
159
+ const noReconnectModel = {
160
+ ...baseIntervalModel,
161
+ workloadName: "interval collection without reconnects",
162
+ };
163
+ const options = {
164
+ ...defaultFuzzOptions,
165
+ skip: [68],
166
+ reconnectProbability: 0.0,
167
+ clientJoinOptions: {
168
+ maxNumberOfClients: 3,
169
+ clientAddProbability: 0.0,
170
+ },
171
+ };
172
+ createDDSFuzzSuite(noReconnectModel, {
173
+ ...options,
174
+ // Uncomment this line to replay a specific seed from its failure file:
175
+ // replay: 0,
176
+ });
177
+ });
178
+ describe("IntervalCollection fuzz testing with rebased batches", () => {
179
+ const noReconnectWithRebaseModel = {
180
+ ...baseIntervalModel,
181
+ workloadName: "interval collection with rebasing",
182
+ };
183
+ createDDSFuzzSuite(noReconnectWithRebaseModel, {
184
+ ...defaultFuzzOptions,
185
+ // todo AB#5603
186
+ skip: [97],
187
+ reconnectProbability: 0.0,
188
+ clientJoinOptions: {
189
+ maxNumberOfClients: 3,
190
+ clientAddProbability: 0.0,
191
+ },
192
+ rebaseProbability: 0.2,
193
+ containerRuntimeOptions: {
194
+ flushMode: FlushMode.TurnBased,
195
+ enableGroupedBatching: true,
196
+ },
197
+ // Uncomment this line to replay a specific seed from its failure file:
198
+ });
199
+ });
200
+ //# sourceMappingURL=intervalCollection.fuzz.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intervalCollection.fuzz.spec.js","sourceRoot":"","sources":["../../../src/test/fuzz/intervalCollection.fuzz.spec.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { strict as assert } from \"assert\";\nimport {\n\tAcceptanceCondition,\n\tcreateWeightedAsyncGenerator as createWeightedGenerator,\n\tAsyncGenerator as Generator,\n\ttakeAsync as take,\n} from \"@fluid-private/stochastic-test-utils\";\nimport { createDDSFuzzSuite } from \"@fluid-private/test-dds-utils\";\nimport { PropertySet } from \"@fluidframework/merge-tree\";\nimport { FlushMode } from \"@fluidframework/runtime-definitions\";\nimport { IIntervalCollection, Side } from \"../../intervalCollection.js\";\nimport { SequenceInterval } from \"../../intervals/index.js\";\nimport {\n\tOperation,\n\tRangeSpec,\n\tAddInterval,\n\tDeleteInterval,\n\tChangeInterval,\n\tFuzzTestState,\n\tIntervalOperationGenerationConfig,\n\tdefaultIntervalOperationGenerationConfig,\n\tcreateSharedStringGeneratorOperations,\n\tbaseModel,\n\tdefaultFuzzOptions,\n} from \"./fuzzUtils.js\";\n\ntype ClientOpState = FuzzTestState;\nexport function makeOperationGenerator(\n\toptionsParam?: IntervalOperationGenerationConfig,\n\talwaysLeaveChar: boolean = false,\n): Generator<Operation, ClientOpState> {\n\tconst {\n\t\tstartPosition,\n\t\taddText,\n\t\tobliterateRange,\n\t\tremoveRange,\n\t\tremoveRangeLeaveChar,\n\t\tlengthSatisfies,\n\t\thasNonzeroLength,\n\t\tisShorterThanMaxLength,\n\t} = createSharedStringGeneratorOperations(optionsParam);\n\n\tconst options = { ...defaultIntervalOperationGenerationConfig, ...(optionsParam ?? {}) };\n\n\tfunction isNonEmpty(collection: IIntervalCollection<SequenceInterval>): boolean {\n\t\tfor (const _ of collection) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tfunction inclusiveRange(state: ClientOpState): RangeSpec {\n\t\tconst start = startPosition(state);\n\t\tconst end = state.random.integer(\n\t\t\tstart,\n\t\t\tMath.max(start, state.client.channel.getLength() - 1),\n\t\t);\n\t\treturn { start, end };\n\t}\n\n\tfunction inclusiveRangeWithUndefined(\n\t\tstate: ClientOpState,\n\t): RangeSpec | { start: undefined; end: undefined } {\n\t\treturn state.random.bool() ? inclusiveRange(state) : { start: undefined, end: undefined };\n\t}\n\n\tfunction propertySet(state: ClientOpState): PropertySet {\n\t\tconst propNamesShuffled = [...options.propertyNamePool];\n\t\tstate.random.shuffle(propNamesShuffled);\n\t\tconst propsToChange = propNamesShuffled.slice(\n\t\t\t0,\n\t\t\tstate.random.integer(1, propNamesShuffled.length),\n\t\t);\n\t\tconst propSet: PropertySet = {};\n\t\tfor (const name of propsToChange) {\n\t\t\tpropSet[name] = state.random.string(5);\n\t\t}\n\t\treturn propSet;\n\t}\n\n\tfunction propertySetWithUndefined(state: ClientOpState): PropertySet | undefined {\n\t\treturn state.random.bool() ? propertySet(state) : undefined;\n\t}\n\n\tfunction nonEmptyIntervalCollection({ client, random }: ClientOpState): string {\n\t\tconst nonEmptyLabels = Array.from(client.channel.getIntervalCollectionLabels()).filter(\n\t\t\t(label) => {\n\t\t\t\tconst collection = client.channel.getIntervalCollection(label);\n\t\t\t\treturn isNonEmpty(collection);\n\t\t\t},\n\t\t);\n\t\treturn random.pick(nonEmptyLabels);\n\t}\n\n\tfunction interval(state: ClientOpState): { collectionName: string; id: string } {\n\t\tconst collectionName = nonEmptyIntervalCollection(state);\n\t\tconst intervals = Array.from(state.client.channel.getIntervalCollection(collectionName));\n\t\tconst id = state.random.pick(intervals)?.getIntervalId();\n\t\tassert(id);\n\n\t\treturn {\n\t\t\tid,\n\t\t\tcollectionName,\n\t\t};\n\t}\n\n\tasync function addInterval(state: ClientOpState): Promise<AddInterval> {\n\t\treturn {\n\t\t\ttype: \"addInterval\",\n\t\t\t...inclusiveRange(state),\n\t\t\tcollectionName: state.random.pick(options.intervalCollectionNamePool),\n\t\t\tid: state.random.uuid4(),\n\t\t\tstartSide: state.random.pick([Side.Before, Side.After]),\n\t\t\tendSide: state.random.pick([Side.Before, Side.After]),\n\t\t};\n\t}\n\n\tasync function deleteInterval(state: ClientOpState): Promise<DeleteInterval> {\n\t\treturn {\n\t\t\ttype: \"deleteInterval\",\n\t\t\t...interval(state),\n\t\t};\n\t}\n\n\tasync function changeInterval(state: ClientOpState): Promise<ChangeInterval> {\n\t\tconst { start, end } = inclusiveRangeWithUndefined(state);\n\t\tconst properties = propertySetWithUndefined(state);\n\t\treturn {\n\t\t\ttype: \"changeInterval\",\n\t\t\tstart,\n\t\t\tend,\n\t\t\tstartSide: state.random.pick([Side.Before, Side.After]),\n\t\t\tendSide: state.random.pick([Side.Before, Side.After]),\n\t\t\tproperties,\n\t\t\t...interval(state),\n\t\t};\n\t}\n\n\tconst hasAnInterval = ({ client }: ClientOpState): boolean =>\n\t\tArray.from(client.channel.getIntervalCollectionLabels()).some((label) => {\n\t\t\tconst collection = client.channel.getIntervalCollection(label);\n\t\t\treturn isNonEmpty(collection);\n\t\t});\n\n\tconst hasNotTooManyIntervals: AcceptanceCondition<ClientOpState> = ({ client }) => {\n\t\tlet intervalCount = 0;\n\t\tfor (const label of client.channel.getIntervalCollectionLabels()) {\n\t\t\tfor (const _ of client.channel.getIntervalCollection(label)) {\n\t\t\t\tintervalCount++;\n\t\t\t\tif (intervalCount >= options.maxIntervals) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t};\n\n\tconst all =\n\t\t<T>(...clauses: AcceptanceCondition<T>[]): AcceptanceCondition<T> =>\n\t\t(t: T) =>\n\t\t\tclauses.reduce<boolean>((prev, cond) => prev && cond(t), true);\n\tconst usableWeights = optionsParam?.weights ?? defaultIntervalOperationGenerationConfig.weights;\n\treturn createWeightedGenerator<Operation, ClientOpState>([\n\t\t[addText, usableWeights.addText, isShorterThanMaxLength],\n\t\t[\n\t\t\talwaysLeaveChar ? removeRangeLeaveChar : removeRange,\n\t\t\tusableWeights.removeRange,\n\t\t\talwaysLeaveChar\n\t\t\t\t? lengthSatisfies((length) => {\n\t\t\t\t\t\treturn length > 1;\n\t\t\t\t })\n\t\t\t\t: hasNonzeroLength,\n\t\t],\n\t\t[obliterateRange, usableWeights.obliterateRange, hasNonzeroLength],\n\t\t[addInterval, usableWeights.addInterval, all(hasNotTooManyIntervals, hasNonzeroLength)],\n\t\t[deleteInterval, usableWeights.deleteInterval, hasAnInterval],\n\t\t[changeInterval, usableWeights.changeInterval, all(hasAnInterval, hasNonzeroLength)],\n\t]);\n}\n\nconst baseIntervalModel = {\n\t...baseModel,\n\tgeneratorFactory: () =>\n\t\ttake(100, makeOperationGenerator(defaultIntervalOperationGenerationConfig)),\n};\n\ndescribe(\"IntervalCollection fuzz testing\", () => {\n\tconst model = {\n\t\t...baseIntervalModel,\n\t\tworkloadName: \"default interval collection\",\n\t};\n\n\tcreateDDSFuzzSuite(model, {\n\t\t...defaultFuzzOptions,\n\t\tskip: [68],\n\t\t// Note: there are some known eventual consistency issues which the tests don't currently reproduce.\n\t\t// Search this package for AB#6552 (or look at that work item) for a skipped test and further details.\n\t\t// Uncomment this line to replay a specific seed from its failure file:\n\t\t// replay: 0,\n\t});\n});\n\ndescribe(\"IntervalCollection with stashing\", () => {\n\tconst model = {\n\t\t...baseIntervalModel,\n\t\tworkloadName: \"default interval collection with stashing\",\n\t};\n\n\tcreateDDSFuzzSuite(model, {\n\t\t...defaultFuzzOptions,\n\t\tclientJoinOptions: {\n\t\t\tclientAddProbability: 0.1,\n\t\t\tmaxNumberOfClients: Number.MAX_SAFE_INTEGER,\n\t\t\tstashableClientProbability: 0.2,\n\t\t},\n\t\t// AB#7220\n\t\tskip: [68],\n\t\t// Uncomment this line to replay a specific seed from its failure file:\n\t\t// replay: 0,\n\t});\n});\n\ndescribe(\"IntervalCollection no reconnect fuzz testing\", () => {\n\tconst noReconnectModel = {\n\t\t...baseIntervalModel,\n\t\tworkloadName: \"interval collection without reconnects\",\n\t};\n\n\tconst options = {\n\t\t...defaultFuzzOptions,\n\t\tskip: [68],\n\t\treconnectProbability: 0.0,\n\t\tclientJoinOptions: {\n\t\t\tmaxNumberOfClients: 3,\n\t\t\tclientAddProbability: 0.0,\n\t\t},\n\t};\n\n\tcreateDDSFuzzSuite(noReconnectModel, {\n\t\t...options,\n\t\t// Uncomment this line to replay a specific seed from its failure file:\n\t\t// replay: 0,\n\t});\n});\n\ndescribe(\"IntervalCollection fuzz testing with rebased batches\", () => {\n\tconst noReconnectWithRebaseModel = {\n\t\t...baseIntervalModel,\n\t\tworkloadName: \"interval collection with rebasing\",\n\t};\n\n\tcreateDDSFuzzSuite(noReconnectWithRebaseModel, {\n\t\t...defaultFuzzOptions,\n\t\t// todo AB#5603\n\t\tskip: [97],\n\t\treconnectProbability: 0.0,\n\t\tclientJoinOptions: {\n\t\t\tmaxNumberOfClients: 3,\n\t\t\tclientAddProbability: 0.0,\n\t\t},\n\t\trebaseProbability: 0.2,\n\t\tcontainerRuntimeOptions: {\n\t\t\tflushMode: FlushMode.TurnBased,\n\t\t\tenableGroupedBatching: true,\n\t\t},\n\t\t// Uncomment this line to replay a specific seed from its failure file:\n\t});\n});\n"]}
@@ -0,0 +1,129 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import { strict as assert } from "assert";
6
+ import { createWeightedAsyncGenerator as createWeightedGenerator, takeAsync as take, } from "@fluid-private/stochastic-test-utils";
7
+ import { createDDSFuzzSuite, } from "@fluid-private/test-dds-utils";
8
+ import { TypedEventEmitter } from "@fluid-internal/client-utils";
9
+ import { FlushMode } from "@fluidframework/runtime-definitions";
10
+ import { appendAddIntervalToRevertibles, appendChangeIntervalToRevertibles, appendDeleteIntervalToRevertibles, appendIntervalPropertyChangedToRevertibles, appendSharedStringDeltaToRevertibles, } from "../../revertibles.js";
11
+ import { isRevertibleSharedString, baseModel, defaultFuzzOptions, } from "./fuzzUtils.js";
12
+ import { makeOperationGenerator } from "./intervalCollection.fuzz.spec.js";
13
+ const emitter = new TypedEventEmitter();
14
+ emitter.on("clientCreate", (client) => {
15
+ const channel = client.channel;
16
+ channel.revertibles = [];
17
+ channel.isCurrentRevert = false;
18
+ channel.on("createIntervalCollection", (label) => {
19
+ const collection = channel.getIntervalCollection(label);
20
+ assert(isRevertibleSharedString(channel));
21
+ collection.on("addInterval", (interval, local, op) => {
22
+ if (local && !channel.isCurrentRevert) {
23
+ appendAddIntervalToRevertibles(interval, channel.revertibles);
24
+ }
25
+ });
26
+ collection.on("deleteInterval", (interval, local, op) => {
27
+ if (local && !channel.isCurrentRevert) {
28
+ appendDeleteIntervalToRevertibles(channel, interval, channel.revertibles);
29
+ }
30
+ });
31
+ collection.on("changeInterval", (interval, previousInterval, local, op, slide) => {
32
+ if (local && !channel.isCurrentRevert && !slide) {
33
+ appendChangeIntervalToRevertibles(channel, interval, previousInterval, channel.revertibles);
34
+ }
35
+ });
36
+ collection.on("propertyChanged", (interval, propertyDeltas, local, op) => {
37
+ if (local && !channel.isCurrentRevert) {
38
+ appendIntervalPropertyChangedToRevertibles(interval, propertyDeltas, channel.revertibles);
39
+ }
40
+ });
41
+ channel.on("sequenceDelta", (op) => {
42
+ if (op.isLocal && !channel.isCurrentRevert) {
43
+ appendSharedStringDeltaToRevertibles(channel, op, channel.revertibles);
44
+ }
45
+ });
46
+ });
47
+ });
48
+ const defaultRevertiblesFuzzOptions = {
49
+ ...defaultFuzzOptions,
50
+ reconnectProbability: 0,
51
+ clientJoinOptions: {
52
+ maxNumberOfClients: 6,
53
+ clientAddProbability: 0,
54
+ },
55
+ };
56
+ const optionsWithEmitter = {
57
+ ...defaultRevertiblesFuzzOptions,
58
+ emitter,
59
+ };
60
+ function operationGenerator(optionsParam) {
61
+ async function revertSharedStringRevertibles(state) {
62
+ assert(isRevertibleSharedString(state.client.channel));
63
+ return {
64
+ type: "revertSharedStringRevertibles",
65
+ // grab a random number of edits to revert
66
+ editsToRevert: state.random.integer(1, state.client.channel.revertibles.length),
67
+ };
68
+ }
69
+ const hasRevertibles = ({ client }) => {
70
+ assert(isRevertibleSharedString(client.channel));
71
+ return client.channel.revertibles.length > 0;
72
+ };
73
+ assert(optionsParam.weights !== undefined);
74
+ const baseGenerator = makeOperationGenerator(optionsParam, true);
75
+ return createWeightedGenerator([
76
+ [revertSharedStringRevertibles, optionsParam.weights.revertWeight, hasRevertibles],
77
+ [baseGenerator, 1],
78
+ ]);
79
+ }
80
+ describe("IntervalCollection fuzz testing", () => {
81
+ const model = {
82
+ ...baseModel,
83
+ workloadName: "interval collection with revertibles",
84
+ generatorFactory: () => take(100,
85
+ // Weights are explicitly defined here while bugs are being resolved. Once resolved,
86
+ // the weights in the defaultOptions parameter will be used.
87
+ operationGenerator({
88
+ weights: {
89
+ revertWeight: 2,
90
+ addText: 2,
91
+ removeRange: 1,
92
+ obliterateRange: 0,
93
+ addInterval: 2,
94
+ deleteInterval: 2,
95
+ changeInterval: 2,
96
+ },
97
+ })),
98
+ };
99
+ createDDSFuzzSuite(model, optionsWithEmitter);
100
+ });
101
+ describe("IntervalCollection fuzz testing with rebasing", () => {
102
+ const model = {
103
+ ...baseModel,
104
+ workloadName: "interval collection with revertibles and rebasing",
105
+ generatorFactory: () => take(100,
106
+ // Weights are explicitly defined here while bugs are being resolved. Once resolved,
107
+ // the weights in the defaultOptions parameter will be used.
108
+ operationGenerator({
109
+ weights: {
110
+ revertWeight: 2,
111
+ addText: 2,
112
+ removeRange: 1,
113
+ obliterateRange: 0,
114
+ addInterval: 2,
115
+ deleteInterval: 2,
116
+ changeInterval: 2,
117
+ },
118
+ })),
119
+ };
120
+ createDDSFuzzSuite(model, {
121
+ ...optionsWithEmitter,
122
+ rebaseProbability: 0.15,
123
+ containerRuntimeOptions: {
124
+ flushMode: FlushMode.TurnBased,
125
+ enableGroupedBatching: true,
126
+ },
127
+ });
128
+ });
129
+ //# sourceMappingURL=intervalRevertibles.fuzz.spec.js.map