@fluid-experimental/tree 0.59.2001 → 0.59.3000
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/.eslintrc.js +2 -0
- package/.vscode/SharedTree.code-workspace +15 -0
- package/.vscode/settings.json +6 -0
- package/dist/ChangeCompression.js +9 -9
- package/dist/ChangeCompression.js.map +1 -1
- package/dist/ChangeTypes.d.ts +1 -6
- package/dist/ChangeTypes.d.ts.map +1 -1
- package/dist/ChangeTypes.js +5 -5
- package/dist/ChangeTypes.js.map +1 -1
- package/dist/Checkout.js +14 -14
- package/dist/Checkout.js.map +1 -1
- package/dist/Common.d.ts +21 -3
- package/dist/Common.d.ts.map +1 -1
- package/dist/Common.js +29 -4
- package/dist/Common.js.map +1 -1
- package/dist/EditLog.js +26 -25
- package/dist/EditLog.js.map +1 -1
- package/dist/EditUtilities.js +17 -17
- package/dist/EditUtilities.js.map +1 -1
- package/dist/Forest.js +31 -31
- package/dist/Forest.js.map +1 -1
- package/dist/HistoryEditFactory.js +9 -9
- package/dist/HistoryEditFactory.js.map +1 -1
- package/dist/IdConversion.js +9 -9
- package/dist/IdConversion.js.map +1 -1
- package/dist/Identifiers.d.ts +4 -0
- package/dist/Identifiers.d.ts.map +1 -1
- package/dist/Identifiers.js.map +1 -1
- package/dist/LogViewer.d.ts +1 -5
- package/dist/LogViewer.d.ts.map +1 -1
- package/dist/LogViewer.js +11 -19
- package/dist/LogViewer.js.map +1 -1
- package/dist/MergeHealth.js +2 -2
- package/dist/MergeHealth.js.map +1 -1
- package/dist/NodeIdUtilities.js +2 -2
- package/dist/NodeIdUtilities.js.map +1 -1
- package/dist/PayloadUtilities.js +1 -1
- package/dist/PayloadUtilities.js.map +1 -1
- package/dist/RevisionValueCache.d.ts +13 -10
- package/dist/RevisionValueCache.d.ts.map +1 -1
- package/dist/RevisionValueCache.js +14 -11
- package/dist/RevisionValueCache.js.map +1 -1
- package/dist/RevisionView.js +4 -4
- package/dist/RevisionView.js.map +1 -1
- package/dist/SerializationUtilities.js +4 -4
- package/dist/SerializationUtilities.js.map +1 -1
- package/dist/SharedTree.d.ts +93 -31
- package/dist/SharedTree.d.ts.map +1 -1
- package/dist/SharedTree.js +160 -131
- package/dist/SharedTree.js.map +1 -1
- package/dist/SharedTreeEncoder.d.ts +3 -3
- package/dist/SharedTreeEncoder.d.ts.map +1 -1
- package/dist/SharedTreeEncoder.js +36 -36
- package/dist/SharedTreeEncoder.js.map +1 -1
- package/dist/StringInterner.js +1 -1
- package/dist/StringInterner.js.map +1 -1
- package/dist/Summary.js +1 -1
- package/dist/Summary.js.map +1 -1
- package/dist/SummaryBackCompatibility.js +8 -8
- package/dist/SummaryBackCompatibility.js.map +1 -1
- package/dist/Transaction.js +1 -1
- package/dist/Transaction.js.map +1 -1
- package/dist/TransactionInternal.js +17 -17
- package/dist/TransactionInternal.js.map +1 -1
- package/dist/TreeCompressor.d.ts.map +1 -1
- package/dist/TreeCompressor.js +6 -8
- package/dist/TreeCompressor.js.map +1 -1
- package/dist/TreeNodeHandle.js +4 -4
- package/dist/TreeNodeHandle.js.map +1 -1
- package/dist/TreeView.js +7 -7
- package/dist/TreeView.js.map +1 -1
- package/dist/TreeViewUtilities.js +2 -2
- package/dist/TreeViewUtilities.js.map +1 -1
- package/dist/UndoRedoHandler.js +1 -1
- package/dist/UndoRedoHandler.js.map +1 -1
- package/dist/UuidUtilities.d.ts +30 -0
- package/dist/UuidUtilities.d.ts.map +1 -0
- package/dist/UuidUtilities.js +106 -0
- package/dist/UuidUtilities.js.map +1 -0
- package/dist/id-compressor/AppendOnlySortedMap.d.ts +52 -28
- package/dist/id-compressor/AppendOnlySortedMap.d.ts.map +1 -1
- package/dist/id-compressor/AppendOnlySortedMap.js +167 -90
- package/dist/id-compressor/AppendOnlySortedMap.js.map +1 -1
- package/dist/id-compressor/IdCompressor.d.ts +43 -42
- package/dist/id-compressor/IdCompressor.d.ts.map +1 -1
- package/dist/id-compressor/IdCompressor.js +179 -177
- package/dist/id-compressor/IdCompressor.js.map +1 -1
- package/dist/id-compressor/IdRange.js +1 -1
- package/dist/id-compressor/IdRange.js.map +1 -1
- package/dist/id-compressor/NumericUuid.d.ts +6 -14
- package/dist/id-compressor/NumericUuid.d.ts.map +1 -1
- package/dist/id-compressor/NumericUuid.js +15 -76
- package/dist/id-compressor/NumericUuid.js.map +1 -1
- package/dist/id-compressor/SessionIdNormalizer.d.ts +122 -0
- package/dist/id-compressor/SessionIdNormalizer.d.ts.map +1 -0
- package/dist/id-compressor/SessionIdNormalizer.js +418 -0
- package/dist/id-compressor/SessionIdNormalizer.js.map +1 -0
- package/dist/id-compressor/persisted-types/0.0.1.d.ts +6 -13
- package/dist/id-compressor/persisted-types/0.0.1.d.ts.map +1 -1
- package/dist/id-compressor/persisted-types/0.0.1.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/persisted-types/0.1.1.d.ts +1 -6
- package/dist/persisted-types/0.1.1.d.ts.map +1 -1
- package/dist/persisted-types/0.1.1.js +3 -3
- package/dist/persisted-types/0.1.1.js.map +1 -1
- package/lib/ChangeTypes.d.ts +1 -6
- package/lib/ChangeTypes.d.ts.map +1 -1
- package/lib/Checkout.js.map +1 -1
- package/lib/Common.d.ts +21 -3
- package/lib/Common.d.ts.map +1 -1
- package/lib/Common.js +25 -3
- package/lib/Common.js.map +1 -1
- package/lib/EditLog.js +2 -1
- package/lib/EditLog.js.map +1 -1
- package/lib/EditUtilities.js.map +1 -1
- package/lib/Forest.js.map +1 -1
- package/lib/HistoryEditFactory.js.map +1 -1
- package/lib/Identifiers.d.ts +4 -0
- package/lib/Identifiers.d.ts.map +1 -1
- package/lib/Identifiers.js.map +1 -1
- package/lib/LogViewer.d.ts +1 -5
- package/lib/LogViewer.d.ts.map +1 -1
- package/lib/LogViewer.js +5 -13
- package/lib/LogViewer.js.map +1 -1
- package/lib/MergeHealth.js.map +1 -1
- package/lib/NodeIdUtilities.js.map +1 -1
- package/lib/RevisionValueCache.d.ts +13 -10
- package/lib/RevisionValueCache.d.ts.map +1 -1
- package/lib/RevisionValueCache.js +10 -7
- package/lib/RevisionValueCache.js.map +1 -1
- package/lib/RevisionView.js.map +1 -1
- package/lib/SharedTree.d.ts +93 -31
- package/lib/SharedTree.d.ts.map +1 -1
- package/lib/SharedTree.js +107 -78
- package/lib/SharedTree.js.map +1 -1
- package/lib/SharedTreeEncoder.d.ts +3 -3
- package/lib/SharedTreeEncoder.d.ts.map +1 -1
- package/lib/SharedTreeEncoder.js +4 -4
- package/lib/SharedTreeEncoder.js.map +1 -1
- package/lib/StringInterner.js.map +1 -1
- package/lib/Summary.js.map +1 -1
- package/lib/TreeCompressor.d.ts.map +1 -1
- package/lib/TreeCompressor.js +1 -3
- package/lib/TreeCompressor.js.map +1 -1
- package/lib/TreeNodeHandle.js.map +1 -1
- package/lib/TreeView.js.map +1 -1
- package/lib/TreeViewUtilities.js.map +1 -1
- package/lib/UuidUtilities.d.ts +30 -0
- package/lib/UuidUtilities.d.ts.map +1 -0
- package/lib/UuidUtilities.js +98 -0
- package/lib/UuidUtilities.js.map +1 -0
- package/lib/id-compressor/AppendOnlySortedMap.d.ts +52 -28
- package/lib/id-compressor/AppendOnlySortedMap.d.ts.map +1 -1
- package/lib/id-compressor/AppendOnlySortedMap.js +165 -88
- package/lib/id-compressor/AppendOnlySortedMap.js.map +1 -1
- package/lib/id-compressor/IdCompressor.d.ts +43 -42
- package/lib/id-compressor/IdCompressor.d.ts.map +1 -1
- package/lib/id-compressor/IdCompressor.js +97 -95
- package/lib/id-compressor/IdCompressor.js.map +1 -1
- package/lib/id-compressor/NumericUuid.d.ts +6 -14
- package/lib/id-compressor/NumericUuid.d.ts.map +1 -1
- package/lib/id-compressor/NumericUuid.js +11 -70
- package/lib/id-compressor/NumericUuid.js.map +1 -1
- package/lib/id-compressor/SessionIdNormalizer.d.ts +122 -0
- package/lib/id-compressor/SessionIdNormalizer.d.ts.map +1 -0
- package/lib/id-compressor/SessionIdNormalizer.js +414 -0
- package/lib/id-compressor/SessionIdNormalizer.js.map +1 -0
- package/lib/id-compressor/persisted-types/0.0.1.d.ts +6 -13
- package/lib/id-compressor/persisted-types/0.0.1.d.ts.map +1 -1
- package/lib/id-compressor/persisted-types/0.0.1.js.map +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/persisted-types/0.1.1.d.ts +1 -6
- package/lib/persisted-types/0.1.1.d.ts.map +1 -1
- package/lib/persisted-types/0.1.1.js.map +1 -1
- package/lib/test/AppendOnlySortedMap.perf.tests.d.ts +6 -0
- package/lib/test/AppendOnlySortedMap.perf.tests.d.ts.map +1 -0
- package/lib/test/AppendOnlySortedMap.perf.tests.js +49 -0
- package/lib/test/AppendOnlySortedMap.perf.tests.js.map +1 -0
- package/lib/test/AppendOnlySortedMap.tests.js +56 -14
- package/lib/test/AppendOnlySortedMap.tests.js.map +1 -1
- package/lib/test/Checkout.tests.js +2 -2
- package/lib/test/Checkout.tests.js.map +1 -1
- package/lib/test/Forest.tests.js.map +1 -1
- package/lib/test/IdCompressor.perf.tests.js +8 -2
- package/lib/test/IdCompressor.perf.tests.js.map +1 -1
- package/lib/test/IdCompressor.tests.js +75 -24
- package/lib/test/IdCompressor.tests.js.map +1 -1
- package/lib/test/LogViewer.tests.js +3 -5
- package/lib/test/LogViewer.tests.js.map +1 -1
- package/lib/test/NumericUuid.perf.tests.js +4 -4
- package/lib/test/NumericUuid.perf.tests.js.map +1 -1
- package/lib/test/NumericUuid.tests.js +5 -4
- package/lib/test/NumericUuid.tests.js.map +1 -1
- package/lib/test/RevisionValueCache.tests.js.map +1 -1
- package/lib/test/RevisionView.tests.js.map +1 -1
- package/lib/test/SessionIdNormalizer.tests.d.ts +6 -0
- package/lib/test/SessionIdNormalizer.tests.d.ts.map +1 -0
- package/lib/test/SessionIdNormalizer.tests.js +299 -0
- package/lib/test/SessionIdNormalizer.tests.js.map +1 -0
- package/lib/test/Summary.tests.js +1 -1
- package/lib/test/Summary.tests.js.map +1 -1
- package/lib/test/TreeCompression.tests.js +1 -1
- package/lib/test/TreeCompression.tests.js.map +1 -1
- package/lib/test/Virtualization.tests.js +1 -1
- package/lib/test/Virtualization.tests.js.map +1 -1
- package/lib/test/fuzz/Generators.d.ts +3 -14
- package/lib/test/fuzz/Generators.d.ts.map +1 -1
- package/lib/test/fuzz/Generators.js +60 -151
- package/lib/test/fuzz/Generators.js.map +1 -1
- package/lib/test/fuzz/SharedTreeFuzzTests.d.ts +10 -7
- package/lib/test/fuzz/SharedTreeFuzzTests.d.ts.map +1 -1
- package/lib/test/fuzz/SharedTreeFuzzTests.js +94 -104
- package/lib/test/fuzz/SharedTreeFuzzTests.js.map +1 -1
- package/lib/test/fuzz/Types.d.ts +2 -9
- package/lib/test/fuzz/Types.d.ts.map +1 -1
- package/lib/test/fuzz/Types.js +1 -1
- package/lib/test/fuzz/Types.js.map +1 -1
- package/lib/test/utilities/IdCompressorTestUtilities.d.ts +57 -11
- package/lib/test/utilities/IdCompressorTestUtilities.d.ts.map +1 -1
- package/lib/test/utilities/IdCompressorTestUtilities.js +112 -98
- package/lib/test/utilities/IdCompressorTestUtilities.js.map +1 -1
- package/lib/test/utilities/PendingLocalStateTests.d.ts.map +1 -1
- package/lib/test/utilities/PendingLocalStateTests.js +2 -1
- package/lib/test/utilities/PendingLocalStateTests.js.map +1 -1
- package/lib/test/utilities/SharedTreeTests.d.ts.map +1 -1
- package/lib/test/utilities/SharedTreeTests.js +30 -1
- package/lib/test/utilities/SharedTreeTests.js.map +1 -1
- package/lib/test/utilities/SharedTreeVersioningTests.d.ts.map +1 -1
- package/lib/test/utilities/SharedTreeVersioningTests.js +20 -0
- package/lib/test/utilities/SharedTreeVersioningTests.js.map +1 -1
- package/lib/test/utilities/SummaryLoadPerfTests.d.ts.map +1 -1
- package/lib/test/utilities/SummaryLoadPerfTests.js +6 -3
- package/lib/test/utilities/SummaryLoadPerfTests.js.map +1 -1
- package/lib/test/utilities/TestNode.js.map +1 -1
- package/lib/test/utilities/TestUtilities.d.ts +9 -1
- package/lib/test/utilities/TestUtilities.d.ts.map +1 -1
- package/lib/test/utilities/TestUtilities.js +27 -13
- package/lib/test/utilities/TestUtilities.js.map +1 -1
- package/package.json +19 -17
- package/src/Common.ts +42 -4
- package/src/EditLog.ts +1 -1
- package/src/Identifiers.ts +5 -0
- package/src/LogViewer.ts +4 -20
- package/src/RevisionValueCache.ts +11 -8
- package/src/SharedTree.ts +222 -75
- package/src/SharedTreeEncoder.ts +17 -11
- package/src/TreeCompressor.ts +2 -4
- package/src/UuidUtilities.ts +123 -0
- package/src/id-compressor/AppendOnlySortedMap.ts +183 -94
- package/src/id-compressor/IdCompressor.ts +144 -132
- package/src/id-compressor/NumericUuid.ts +11 -80
- package/src/id-compressor/SessionIdNormalizer.ts +497 -0
- package/src/id-compressor/persisted-types/0.0.1.ts +12 -15
- package/src/index.ts +5 -0
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
/* eslint-disable no-bitwise */
|
|
6
|
-
import { v5 } from 'uuid';
|
|
7
|
-
import Prando from 'prando';
|
|
8
6
|
import { expect } from 'chai';
|
|
7
|
+
import { createWeightedGenerator, interleave, makeRandom, performFuzzActions as performFuzzActionsBase, repeat, take, } from '@fluid-internal/stochastic-test-utils';
|
|
9
8
|
import { assert, assertNotUndefined, fail, getOrCreate } from '../../Common';
|
|
10
9
|
import { IdCompressor, isLocalId } from '../../id-compressor/IdCompressor';
|
|
11
|
-
import {
|
|
10
|
+
import { createSessionId, ensureSessionUuid, numericUuidFromStableId, stableIdFromNumericUuid, } from '../../id-compressor/NumericUuid';
|
|
12
11
|
import { getIds } from '../../id-compressor/IdRange';
|
|
12
|
+
import { assertIsStableId, assertIsUuidString } from '../../UuidUtilities';
|
|
13
|
+
import { expectDefined } from './TestCommon';
|
|
13
14
|
/** Identifies a compressor in a network */
|
|
14
15
|
export var Client;
|
|
15
16
|
(function (Client) {
|
|
@@ -32,8 +33,8 @@ export const DestinationClient = Object.assign(Object.assign({}, Client), MetaCl
|
|
|
32
33
|
/**
|
|
33
34
|
* Creates a new compressor with the supplied cluster capacity.
|
|
34
35
|
*/
|
|
35
|
-
export function createCompressor(client, clusterCapacity = 5,
|
|
36
|
-
const compressor = new IdCompressor(sessionIds.get(client), 1024,
|
|
36
|
+
export function createCompressor(client, clusterCapacity = 5, attributionId) {
|
|
37
|
+
const compressor = new IdCompressor(sessionIds.get(client), 1024, attributionId);
|
|
37
38
|
compressor.clusterCapacity = clusterCapacity;
|
|
38
39
|
return compressor;
|
|
39
40
|
}
|
|
@@ -58,6 +59,10 @@ export const sessionIds = makeSessionIds();
|
|
|
58
59
|
export const sessionNumericUuids = new Map([...sessionIds.entries()].map(([client, sessionId]) => {
|
|
59
60
|
return [client, numericUuidFromStableId(sessionId)];
|
|
60
61
|
}));
|
|
62
|
+
export const attributionIds = new Map(Object.values(Client).map((c, i) => [
|
|
63
|
+
c,
|
|
64
|
+
assertIsUuidString(`00000000-0000-0000-0000-${(i + 1).toString(16).padStart(12, '0')}`),
|
|
65
|
+
]));
|
|
61
66
|
/**
|
|
62
67
|
* Simulates a network of ID compressors.
|
|
63
68
|
* Not suitable for performance testing.
|
|
@@ -73,7 +78,7 @@ export class IdCompressorTestNetwork {
|
|
|
73
78
|
const clientIds = new Map();
|
|
74
79
|
const clientSequencedIds = new Map();
|
|
75
80
|
for (const client of Object.values(Client)) {
|
|
76
|
-
const compressor = createCompressor(client, initialClusterSize, client);
|
|
81
|
+
const compressor = createCompressor(client, initialClusterSize, attributionIds.get(client));
|
|
77
82
|
compressors.set(client, compressor);
|
|
78
83
|
clientProgress.set(client, 0);
|
|
79
84
|
clientIds.set(client, []);
|
|
@@ -274,6 +279,11 @@ export class IdCompressorTestNetwork {
|
|
|
274
279
|
const idIndex = getOrCreate(idIndicesAggregator, idDataA.originatingClient, () => 0);
|
|
275
280
|
originatingClient !== null && originatingClient !== void 0 ? originatingClient : (originatingClient = idDataA.originatingClient);
|
|
276
281
|
assert(idDataA.originatingClient === originatingClient, 'Test infra gave wrong originating client to TestIdData');
|
|
282
|
+
const attributionA = compressorA.attributeId(idDataA.id);
|
|
283
|
+
if (attributionA !== attributionIds.get(idDataA.originatingClient)) {
|
|
284
|
+
// Unification
|
|
285
|
+
expectDefined(idDataA.expectedOverride);
|
|
286
|
+
}
|
|
277
287
|
// Only one client should have this ID as local in its session space, as only one client could have created this ID
|
|
278
288
|
if (isLocalId(sessionSpaceIdA)) {
|
|
279
289
|
localCount++;
|
|
@@ -400,103 +410,107 @@ export function mergeArrayMaps(to, from) {
|
|
|
400
410
|
}
|
|
401
411
|
return to;
|
|
402
412
|
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
413
|
+
const defaultOptions = {
|
|
414
|
+
includeOverrides: false,
|
|
415
|
+
maxClusterSize: 25,
|
|
416
|
+
validateInterval: 200,
|
|
417
|
+
};
|
|
418
|
+
export function makeOpGenerator(options) {
|
|
419
|
+
const { includeOverrides, maxClusterSize, validateInterval } = Object.assign(Object.assign({}, defaultOptions), options);
|
|
420
|
+
function allocateIdsGenerator({ activeClients, clusterSize, random }) {
|
|
421
|
+
const client = random.pick(activeClients);
|
|
422
|
+
const maxIdsPerUsage = clusterSize * 2;
|
|
423
|
+
const numIds = Math.floor(random.real(0, 1) ** 2 * maxIdsPerUsage) + 1;
|
|
424
|
+
const overrides = {};
|
|
425
|
+
if (includeOverrides && random.bool(1 / 4)) {
|
|
426
|
+
for (let j = 0; j < numIds; j++) {
|
|
427
|
+
if (random.bool(1 / 3)) {
|
|
428
|
+
overrides[j] = random.uuid4();
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return {
|
|
433
|
+
type: 'allocateIds',
|
|
434
|
+
client,
|
|
435
|
+
numIds,
|
|
436
|
+
overrides,
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
function changeCapacityGenerator({ random }) {
|
|
440
|
+
return {
|
|
441
|
+
type: 'changeCapacity',
|
|
442
|
+
newSize: Math.min(Math.floor(random.real(0, 1) ** 2 * maxClusterSize) + 1, maxClusterSize),
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
function deliverOperationsGenerator({ random, selectableClients }) {
|
|
446
|
+
return {
|
|
447
|
+
type: 'deliverOperations',
|
|
448
|
+
client: random.pick([...selectableClients, MetaClient.All]),
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
function generateUnifyingIdsGenerator({ activeClients, random }) {
|
|
452
|
+
const clientA = random.pick(activeClients);
|
|
453
|
+
const clientB = random.pick(activeClients.filter((c) => c !== clientA));
|
|
454
|
+
return { type: 'generateUnifyingIds', clientA, clientB, uuid: random.uuid4() };
|
|
455
|
+
}
|
|
456
|
+
function reconnectGenerator({ activeClients, random }) {
|
|
457
|
+
return { type: 'reconnect', client: random.pick(activeClients) };
|
|
458
|
+
}
|
|
459
|
+
return interleave(createWeightedGenerator([
|
|
460
|
+
[changeCapacityGenerator, 1],
|
|
461
|
+
[allocateIdsGenerator, 8],
|
|
462
|
+
[deliverOperationsGenerator, 4],
|
|
463
|
+
[generateUnifyingIdsGenerator, 1],
|
|
464
|
+
[reconnectGenerator, 1],
|
|
465
|
+
]), take(1, repeat({ type: 'validate' })), validateInterval);
|
|
466
|
+
}
|
|
411
467
|
/**
|
|
412
468
|
* Performs random actions on a test network.
|
|
413
|
-
* @param
|
|
414
|
-
* @param
|
|
415
|
-
* @param
|
|
416
|
-
* @param observerClient if provided, this client will never generate local ids
|
|
417
|
-
* @param synchronizeAtEnd if provided, all client will have all operations delivered from the server at the end of the test
|
|
418
|
-
* @param
|
|
419
|
-
* @param validator if provided, this callback will be invoked periodically during the fuzz test.
|
|
469
|
+
* @param generator - the generator used to provide operations
|
|
470
|
+
* @param network - the test network to test
|
|
471
|
+
* @param seed - the seed for the random generation of the fuzz actions
|
|
472
|
+
* @param observerClient - if provided, this client will never generate local ids
|
|
473
|
+
* @param synchronizeAtEnd - if provided, all client will have all operations delivered from the server at the end of the test
|
|
474
|
+
* @param validator - if provided, this callback will be invoked periodically during the fuzz test.
|
|
420
475
|
*/
|
|
421
|
-
export function performFuzzActions(network, seed,
|
|
422
|
-
const
|
|
476
|
+
export function performFuzzActions(generator, network, seed, observerClient, synchronizeAtEnd = true, validator, saveInfo) {
|
|
477
|
+
const random = makeRandom(seed);
|
|
423
478
|
const selectableClients = network.getTargetCompressors(MetaClient.All).map(([client]) => client);
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
case Operation.ChangeCapacity: {
|
|
456
|
-
clusterSize = Math.min(Math.floor(rand.next(0, 1) ** 2 * maxClusterSize) + 1, maxClusterSize);
|
|
457
|
-
network.enqueueCapacityChange(clusterSize);
|
|
458
|
-
break;
|
|
459
|
-
}
|
|
460
|
-
case Operation.AllocateIds: {
|
|
461
|
-
const client = rand.nextArrayItem(activeClients);
|
|
462
|
-
const maxIdsPerUsage = clusterSize * 2;
|
|
463
|
-
const numIds = Math.floor(rand.next(0, 1) ** 2 * maxIdsPerUsage) + 1;
|
|
464
|
-
const overrides = {};
|
|
465
|
-
if (includeOverrides && /* 25% chance: */ rand.nextInt(0, 3) === 0) {
|
|
466
|
-
for (let j = 0; j < numIds; j++) {
|
|
467
|
-
if ( /* 33% chance: */rand.nextInt(0, 2) === 0) {
|
|
468
|
-
overrides[j] = v5((uuidNum++).toString(), uuidNamespace);
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
network.allocateAndSendIds(client, numIds, overrides);
|
|
473
|
-
break;
|
|
474
|
-
}
|
|
475
|
-
case Operation.DeliverOperations: {
|
|
476
|
-
const client = rand.nextArrayItem([...selectableClients, MetaClient.All]);
|
|
477
|
-
network.deliverOperations(client);
|
|
478
|
-
break;
|
|
479
|
-
}
|
|
480
|
-
case Operation.GenerateUnifyingIds: {
|
|
481
|
-
const clientA = rand.nextArrayItem(activeClients);
|
|
482
|
-
const clientB = rand.nextArrayItem(activeClients.filter((c) => c !== clientA));
|
|
483
|
-
const uuid = v5((uuidNum++).toString(), uuidNamespace);
|
|
484
|
-
network.allocateAndSendIds(clientA, 1, { 0: uuid });
|
|
485
|
-
network.allocateAndSendIds(clientB, 1, { 0: uuid });
|
|
486
|
-
break;
|
|
487
|
-
}
|
|
488
|
-
case Operation.GoOfflineThenResume: {
|
|
489
|
-
const client = rand.nextArrayItem(activeClients);
|
|
490
|
-
network.goOfflineThenResume(client);
|
|
491
|
-
break;
|
|
492
|
-
}
|
|
493
|
-
default:
|
|
494
|
-
throw new Error('Unknown operation.');
|
|
495
|
-
}
|
|
496
|
-
if (i !== 0 && i % Math.round(numUsages / 5) === 0) {
|
|
479
|
+
const initialState = {
|
|
480
|
+
random,
|
|
481
|
+
network,
|
|
482
|
+
activeClients: selectableClients.filter((c) => c !== observerClient),
|
|
483
|
+
selectableClients,
|
|
484
|
+
clusterSize: network.initialClusterSize,
|
|
485
|
+
};
|
|
486
|
+
performFuzzActionsBase(generator, {
|
|
487
|
+
allocateIds: (state, { client, numIds, overrides }) => {
|
|
488
|
+
network.allocateAndSendIds(client, numIds, overrides);
|
|
489
|
+
return state;
|
|
490
|
+
},
|
|
491
|
+
changeCapacity: (state, op) => {
|
|
492
|
+
network.enqueueCapacityChange(op.newSize);
|
|
493
|
+
return Object.assign(Object.assign({}, state), { clusterSize: op.newSize });
|
|
494
|
+
},
|
|
495
|
+
deliverOperations: (state, op) => {
|
|
496
|
+
network.deliverOperations(op.client);
|
|
497
|
+
return state;
|
|
498
|
+
},
|
|
499
|
+
generateUnifyingIds: (state, { clientA, clientB, uuid }) => {
|
|
500
|
+
network.allocateAndSendIds(clientA, 1, { 0: uuid });
|
|
501
|
+
network.allocateAndSendIds(clientB, 1, { 0: uuid });
|
|
502
|
+
return state;
|
|
503
|
+
},
|
|
504
|
+
reconnect: (state, { client }) => {
|
|
505
|
+
network.goOfflineThenResume(client);
|
|
506
|
+
return state;
|
|
507
|
+
},
|
|
508
|
+
validate: (state) => {
|
|
509
|
+
network.deliverOperations(DestinationClient.All);
|
|
497
510
|
validator === null || validator === void 0 ? void 0 : validator(network);
|
|
498
|
-
|
|
499
|
-
|
|
511
|
+
return state;
|
|
512
|
+
},
|
|
513
|
+
}, initialState, saveInfo);
|
|
500
514
|
if (synchronizeAtEnd) {
|
|
501
515
|
network.deliverOperations(DestinationClient.All);
|
|
502
516
|
validator === null || validator === void 0 ? void 0 : validator(network);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IdCompressorTestUtilities.js","sourceRoot":"","sources":["../../../src/test/utilities/IdCompressorTestUtilities.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\n/* eslint-disable no-bitwise */\n\nimport { v5 } from 'uuid';\nimport Prando from 'prando';\nimport { expect } from 'chai';\nimport { Serializable } from '@fluidframework/datastore-definitions';\nimport { assert, assertNotUndefined, ClosedMap, fail, getOrCreate } from '../../Common';\nimport { IdCompressor, IdRangeDescriptor, isLocalId } from '../../id-compressor/IdCompressor';\nimport {\n\tassertIsStableId,\n\tcreateSessionId,\n\tensureSessionUuid,\n\tNumericUuid,\n\tnumericUuidFromStableId,\n\tstableIdFromNumericUuid,\n} from '../../id-compressor/NumericUuid';\nimport { FinalCompressedId, SessionId, StableId, SessionSpaceCompressedId } from '../../Identifiers';\nimport { getIds } from '../../id-compressor/IdRange';\nimport type {\n\tIdCreationRange,\n\tSerializedIdCompressorWithOngoingSession,\n\tSerializedIdCompressorWithNoSession,\n} from '../../id-compressor';\n\n/** Identifies a compressor in a network */\nexport enum Client {\n\tClient1 = 'Client1',\n\tClient2 = 'Client2',\n\tClient3 = 'Client3',\n}\n\n/** Identifies a compressor with respect to a specific operation */\nexport enum SemanticClient {\n\tLocalClient = 'LocalClient',\n}\n\n/** Identifies categories of compressors */\nexport enum MetaClient {\n\tAll = 'All',\n}\n\n/**\n * Used to attribute actions to clients in a distributed collaboration session.\n * `Local` implies a local and unsequenced operation. All others imply sequenced operations.\n */\nexport type OriginatingClient = Client | SemanticClient;\nexport const OriginatingClient = { ...Client, ...SemanticClient };\n\n/** Identifies a compressor to which to send an operation */\nexport type DestinationClient = Client | MetaClient;\nexport const DestinationClient = { ...Client, ...MetaClient };\n\n/**\n * Creates a new compressor with the supplied cluster capacity.\n */\nexport function createCompressor<T>(\n\tclient: Client,\n\tclusterCapacity = 5,\n\tattributionInfo?: Serializable<T>\n): IdCompressor {\n\tconst compressor = new IdCompressor(sessionIds.get(client), 1024, attributionInfo);\n\tcompressor.clusterCapacity = clusterCapacity;\n\treturn compressor;\n}\n\n/**\n * A closed map from NamedClient to T.\n */\nexport type ClientMap<T> = ClosedMap<Client, T>;\n\nfunction makeSessionIds(): ClientMap<SessionId> {\n\tconst stableIds = new Map<Client, SessionId>();\n\tconst clients = Object.values(Client);\n\tfor (let i = 0; i < clients.length; i++) {\n\t\t// Place session uuids roughly in the middle of uuid space to increase odds of encountering interesting\n\t\t// orderings in sorted collections\n\t\tconst sessionId = ensureSessionUuid(assertIsStableId(`88888888-8888-4888-b${i}88-888888888888`));\n\t\tstableIds.set(clients[i], sessionId);\n\t}\n\treturn stableIds as ClientMap<SessionId>;\n}\n\n/**\n * An array of session ID strings corresponding to all non-local `Client` entries.\n */\nexport const sessionIds = makeSessionIds();\n\n/**\n * An array of session uuids corresponding to all non-local `Client` entries.\n */\nexport const sessionNumericUuids = new Map(\n\t[...sessionIds.entries()].map(([client, sessionId]) => {\n\t\treturn [client, numericUuidFromStableId(sessionId)];\n\t})\n) as ClientMap<NumericUuid>;\n\n/** An immutable view of an `IdCompressor` */\nexport interface ReadonlyIdCompressor\n\textends Omit<\n\t\tIdCompressor,\n\t\t'generateCompressedId' | 'generateCompressedIdRange' | 'takeNextCreationRange' | 'finalizeCreationRange'\n\t> {\n\treadonly clusterCapacity: number;\n}\n\n/** Information about a generated ID in a network to be validated by tests */\nexport interface TestIdData {\n\treadonly id: SessionSpaceCompressedId;\n\treadonly originatingClient: Client;\n\treadonly sessionId: SessionId;\n\treadonly sessionNumericUuid: NumericUuid;\n\treadonly expectedOverride: string | undefined;\n\treadonly isSequenced: boolean;\n}\n\n/**\n * Simulates a network of ID compressors.\n * Not suitable for performance testing.\n */\nexport class IdCompressorTestNetwork {\n\t/** The compressors used in this network */\n\tprivate readonly compressors: ClientMap<IdCompressor>;\n\t/** The log of operations seen by the server so far. Append-only. */\n\tprivate readonly serverOperations: ([range: IdCreationRange, clientFrom: Client] | number)[] = [];\n\t/** An index into `serverOperations` for each client which represents how many operations have been delivered to that client */\n\tprivate readonly clientProgress: ClientMap<number>;\n\t/** All ids (local and sequenced) that a client has created or received, in order. */\n\tprivate readonly idLogs: ClientMap<TestIdData[]>;\n\t/** All ids that a client has received from the server, in order. */\n\tprivate readonly sequencedIdLogs: ClientMap<TestIdData[]>;\n\n\tpublic constructor(\n\t\tpublic readonly initialClusterSize = 5,\n\t\tprivate readonly onIdReceived?: (network: IdCompressorTestNetwork, clientTo: Client, ids: TestIdData[]) => void\n\t) {\n\t\tconst compressors = new Map<Client, IdCompressor>();\n\t\tconst clientProgress = new Map<Client, number>();\n\t\tconst clientIds = new Map<Client, TestIdData[]>();\n\t\tconst clientSequencedIds = new Map<Client, TestIdData[]>();\n\t\tfor (const client of Object.values(Client)) {\n\t\t\tconst compressor = createCompressor(client, initialClusterSize, client);\n\t\t\tcompressors.set(client, compressor);\n\t\t\tclientProgress.set(client, 0);\n\t\t\tclientIds.set(client, []);\n\t\t\tclientSequencedIds.set(client, []);\n\t\t}\n\t\tthis.compressors = compressors as ClientMap<IdCompressor>;\n\t\tthis.clientProgress = clientProgress as ClientMap<number>;\n\t\tthis.idLogs = clientIds as ClientMap<TestIdData[]>;\n\t\tthis.sequencedIdLogs = clientSequencedIds as ClientMap<TestIdData[]>;\n\t}\n\n\t/**\n\t * Returns an immutable handle to a compressor in the network.\n\t */\n\tpublic getCompressor(client: Client): ReadonlyIdCompressor {\n\t\tconst compressors = this.compressors;\n\t\tconst handler = {\n\t\t\tget(_, property) {\n\t\t\t\tconst compressor = compressors.get(client);\n\t\t\t\treturn compressor[property];\n\t\t\t},\n\t\t\tset(_, property, value): boolean {\n\t\t\t\tconst compressor = compressors.get(client);\n\t\t\t\tcompressor[property] = value;\n\t\t\t\treturn true;\n\t\t\t},\n\t\t};\n\t\treturn new Proxy<IdCompressor>({} as unknown as IdCompressor, handler);\n\t}\n\n\t/**\n\t * Returns a mutable handle to a compressor in the network. Use of mutation methods will break the network invariants and\n\t * should only be used if the network will not be used again.\n\t */\n\tpublic getCompressorUnsafe(client: Client): IdCompressor {\n\t\treturn this.getCompressor(client) as IdCompressor;\n\t}\n\n\t/**\n\t * Returns data for all IDs created and received by this client, including ack's of their own (i.e. their own IDs will appear twice)\n\t */\n\tpublic getIdLog(client: Client): readonly TestIdData[] {\n\t\treturn this.idLogs.get(client);\n\t}\n\n\t/**\n\t * Returns data for all IDs received by this client, including ack's of their own (i.e. their own IDs will appear twice)\n\t */\n\tpublic getSequencedIdLog(client: Client): readonly TestIdData[] {\n\t\treturn this.sequencedIdLogs.get(client);\n\t}\n\n\t/**\n\t * Get all compressors for the given destination\n\t */\n\tpublic getTargetCompressors(clientTo: DestinationClient): [Client, IdCompressor][] {\n\t\treturn clientTo === MetaClient.All\n\t\t\t? [...this.compressors.entries()]\n\t\t\t: ([[clientTo, this.getCompressor(clientTo)]] as [Client, IdCompressor][]);\n\t}\n\n\t/**\n\t * Submit a capacity change operation to the network. It will not take effect immediately but will be processed in sequence order.\n\t */\n\tpublic enqueueCapacityChange(newClusterCapacity: number): void {\n\t\tthis.serverOperations.push(newClusterCapacity);\n\t}\n\n\tprivate addNewId(\n\t\tclient: Client,\n\t\tid: SessionSpaceCompressedId,\n\t\texpectedOverride: string | undefined,\n\t\toriginatingClient: Client,\n\t\tisSequenced: boolean\n\t): void {\n\t\tconst idData = {\n\t\t\tid,\n\t\t\toriginatingClient,\n\t\t\tsessionId: sessionIds.get(originatingClient),\n\t\t\tsessionNumericUuid: sessionNumericUuids.get(originatingClient),\n\t\t\texpectedOverride,\n\t\t\tisSequenced,\n\t\t};\n\t\tconst clientIds = this.idLogs.get(client);\n\t\tclientIds.push(idData);\n\t\tif (isSequenced) {\n\t\t\tconst sequencedIds = this.sequencedIdLogs.get(client);\n\t\t\tsequencedIds.push(idData);\n\t\t}\n\t\tthis.onIdReceived?.(this, client, clientIds);\n\t}\n\n\t/**\n\t * Allocates a new range of local IDs and enqueues them for future delivery via a `testIdDelivery` action.\n\t * Calls to this method determine the total order of delivery, regardless of when `deliverOperations` is called.\n\t */\n\tpublic allocateAndSendIds(\n\t\tclient: Client,\n\t\tnumIds: number\n\t): { range: IdRangeDescriptor<SessionSpaceCompressedId>; sessionId: SessionId };\n\n\t/**\n\t * Allocates a new range of local IDs and enqueues them for future delivery via a `testIdDelivery` action.\n\t * Calls to this method determine the total order of delivery, regardless of when `deliverOperations` is called.\n\t */\n\tpublic allocateAndSendIds(client: Client, numIds: number, overrides: { [index: number]: string }): IdCreationRange;\n\n\tpublic allocateAndSendIds(\n\t\tclient: Client,\n\t\tnumIds: number,\n\t\toverrides: { [index: number]: string } = {}\n\t): { range: IdRangeDescriptor<SessionSpaceCompressedId>; sessionId: SessionId } | IdCreationRange {\n\t\tassert(numIds > 0, 'Must allocate a non-zero number of IDs');\n\t\tconst compressor = this.compressors.get(client);\n\t\tlet nextIdIndex = 0;\n\t\tfor (const [overrideIndex, uuid] of Object.entries(overrides)\n\t\t\t.map(([id, uuid]) => [Number.parseInt(id, 10), uuid] as [number, string])\n\t\t\t.sort(([a], [b]) => a - b)) {\n\t\t\twhile (nextIdIndex < overrideIndex) {\n\t\t\t\tthis.addNewId(client, compressor.generateCompressedId(), undefined, client, false);\n\t\t\t\tnextIdIndex += 1;\n\t\t\t}\n\t\t\tthis.addNewId(client, compressor.generateCompressedId(uuid), uuid, client, false);\n\t\t\tnextIdIndex += 1;\n\t\t}\n\t\tconst numTrailingIds = numIds - nextIdIndex;\n\t\tlet range: IdRangeDescriptor<SessionSpaceCompressedId> | undefined;\n\t\tif (numTrailingIds > 0) {\n\t\t\trange = compressor.generateCompressedIdRange(numTrailingIds);\n\t\t\tconst ids = compressor.getIdsFromRange(range, compressor.localSessionId);\n\t\t\tfor (let i = 0; i < numTrailingIds; i++) {\n\t\t\t\tthis.addNewId(client, ids.get(i), undefined, client, false);\n\t\t\t}\n\t\t}\n\t\tconst creationRange = compressor.takeNextCreationRange();\n\t\tthis.serverOperations.push([creationRange, client]);\n\t\treturn nextIdIndex === 0 ? { range: range ?? fail(), sessionId: compressor.localSessionId } : creationRange;\n\t}\n\n\t/**\n\t * Delivers all undelivered ID ranges and cluster capacity changes from the server to the target clients.\n\t */\n\tpublic deliverOperations(clientTakingDelivery: DestinationClient) {\n\t\tfor (const [clientTo, compressorTo] of this.getTargetCompressors(clientTakingDelivery)) {\n\t\t\tfor (let i = this.clientProgress.get(clientTo); i < this.serverOperations.length; i++) {\n\t\t\t\tconst operation = this.serverOperations[i];\n\t\t\t\tif (typeof operation === 'number') {\n\t\t\t\t\tcompressorTo.clusterCapacity = operation;\n\t\t\t\t} else {\n\t\t\t\t\tconst [range, clientFrom] = operation;\n\t\t\t\t\tcompressorTo.finalizeCreationRange(range);\n\n\t\t\t\t\tconst ids = getIds(range);\n\t\t\t\t\tif (ids !== undefined) {\n\t\t\t\t\t\tlet overrideIndex = 0;\n\t\t\t\t\t\tconst overrides = ids.overrides;\n\t\t\t\t\t\tfor (let id = ids.first; id >= ids.last; id--) {\n\t\t\t\t\t\t\tlet override: string | undefined;\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\toverrides !== undefined &&\n\t\t\t\t\t\t\t\toverrideIndex < overrides.length &&\n\t\t\t\t\t\t\t\tid === overrides[overrideIndex][0]\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\toverride = overrides[overrideIndex][1];\n\t\t\t\t\t\t\t\toverrideIndex++;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst sessionSpaceId = compressorTo.normalizeToSessionSpace(id, range.sessionId);\n\t\t\t\t\t\t\tthis.addNewId(clientTo, sessionSpaceId, override, clientFrom, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tassert(overrideIndex === (overrides?.length ?? 0));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.clientProgress.set(clientTo, this.serverOperations.length);\n\t\t}\n\t}\n\n\t/**\n\t * Simulate a client disconnecting (and serializing), then reconnecting (and deserializing)\n\t */\n\tpublic goOfflineThenResume(client: Client): void {\n\t\tconst compressor = this.compressors.get(client);\n\t\tconst [_, resumedCompressor] = roundtrip(compressor, true);\n\t\tthis.compressors.set(client, resumedCompressor);\n\t}\n\n\t/**\n\t * Ensure general validity of the network state. Useful for calling periodically or at the end of test scenarios.\n\t */\n\tpublic assertNetworkState(): void {\n\t\tconst sequencedLogs = Object.values(Client).map(\n\t\t\t(client) => [this.compressors.get(client), this.getSequencedIdLog(client)] as [IdCompressor, TestIdData[]]\n\t\t);\n\n\t\tconst maxLogLength = sequencedLogs.map(([_, data]) => data.length).reduce((p, n) => Math.max(p, n));\n\n\t\tfunction getNextLogWithEntryAt(logsIndex: number, entryIndex: number): number | undefined {\n\t\t\tfor (let i = logsIndex; i < sequencedLogs.length; i++) {\n\t\t\t\tconst log = sequencedLogs[i];\n\t\t\t\tif (log[1].length > entryIndex) {\n\t\t\t\t\treturn i;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst uuids = new Set<string>();\n\t\tconst finalIds = new Set<FinalCompressedId>();\n\t\tconst idIndicesAggregator = new Map<Client, number>();\n\n\t\tfunction* getLogIndices(\n\t\t\tcolumnIndex: number\n\t\t): Iterable<\n\t\t\t[\n\t\t\t\tcurrent: [compressor: IdCompressor, idData: TestIdData],\n\t\t\t\tnext?: [compressor: IdCompressor, idData: TestIdData]\n\t\t\t]\n\t\t> {\n\t\t\tlet current = getNextLogWithEntryAt(0, columnIndex);\n\t\t\twhile (current !== undefined) {\n\t\t\t\tconst next = getNextLogWithEntryAt(current + 1, columnIndex);\n\t\t\t\tconst [compressor, log] = sequencedLogs[current];\n\t\t\t\tif (next === undefined) {\n\t\t\t\t\tyield [[compressor, log[columnIndex]]];\n\t\t\t\t} else {\n\t\t\t\t\tconst [compressorNext, logNext] = sequencedLogs[next];\n\t\t\t\t\tyield [\n\t\t\t\t\t\t[compressor, log[columnIndex]],\n\t\t\t\t\t\t[compressorNext, logNext[columnIndex]],\n\t\t\t\t\t];\n\t\t\t\t}\n\t\t\t\tcurrent = next;\n\t\t\t}\n\t\t}\n\n\t\tfor (let i = 0; i < maxLogLength; i++) {\n\t\t\tconst creator: [creator: Client, override?: string][] = [];\n\t\t\tlet originatingClient: Client | undefined;\n\t\t\tlet localCount = 0;\n\t\t\tlet rowCount = 0;\n\t\t\tfor (const [current, next] of getLogIndices(i)) {\n\t\t\t\tconst [compressorA, idDataA] = current;\n\t\t\t\tconst sessionSpaceIdA = idDataA.id;\n\t\t\t\tif (isLocalId(sessionSpaceIdA)) {\n\t\t\t\t\tlocalCount += 1;\n\t\t\t\t}\n\t\t\t\tconst idIndex = getOrCreate(idIndicesAggregator, idDataA.originatingClient, () => 0);\n\t\t\t\toriginatingClient ??= idDataA.originatingClient;\n\t\t\t\tassert(\n\t\t\t\t\tidDataA.originatingClient === originatingClient,\n\t\t\t\t\t'Test infra gave wrong originating client to TestIdData'\n\t\t\t\t);\n\n\t\t\t\t// Only one client should have this ID as local in its session space, as only one client could have created this ID\n\t\t\t\tif (isLocalId(sessionSpaceIdA)) {\n\t\t\t\t\tlocalCount++;\n\t\t\t\t\texpect(idDataA.sessionId).to.equal(this.compressors.get(originatingClient).localSessionId);\n\t\t\t\t\texpect(creator.length === 0 || creator[creator.length - 1][1] === idDataA.expectedOverride).to.be\n\t\t\t\t\t\t.true;\n\t\t\t\t\tcreator.push([originatingClient, idDataA.expectedOverride]);\n\t\t\t\t}\n\n\t\t\t\tconst uuidASessionSpace = compressorA.decompress(sessionSpaceIdA);\n\t\t\t\tif (idDataA.expectedOverride !== undefined) {\n\t\t\t\t\texpect(uuidASessionSpace).to.equal(idDataA.expectedOverride);\n\t\t\t\t} else {\n\t\t\t\t\texpect(uuidASessionSpace).to.equal(stableIdFromNumericUuid(idDataA.sessionNumericUuid, idIndex));\n\t\t\t\t}\n\t\t\t\texpect(compressorA.recompress(uuidASessionSpace)).to.equal(sessionSpaceIdA);\n\t\t\t\tuuids.add(uuidASessionSpace);\n\t\t\t\tconst opSpaceIdA = compressorA.normalizeToOpSpace(sessionSpaceIdA);\n\t\t\t\tif (isLocalId(opSpaceIdA)) {\n\t\t\t\t\texpect.fail('IDs should have been finalized.');\n\t\t\t\t}\n\t\t\t\t// TODO: This cast can be removed on typescript 4.6\n\t\t\t\tfinalIds.add(opSpaceIdA as FinalCompressedId);\n\t\t\t\tconst uuidAOpSpace = compressorA.decompress(opSpaceIdA);\n\n\t\t\t\texpect(uuidASessionSpace).to.equal(uuidAOpSpace);\n\n\t\t\t\tif (next !== undefined) {\n\t\t\t\t\tconst [compressorB, idDataB] = next;\n\t\t\t\t\tconst sessionSpaceIdB = idDataB.id;\n\n\t\t\t\t\tconst uuidBSessionSpace = compressorB.decompress(sessionSpaceIdB);\n\t\t\t\t\texpect(uuidASessionSpace).to.equal(uuidBSessionSpace);\n\t\t\t\t\tconst opSpaceIdB = compressorB.normalizeToOpSpace(sessionSpaceIdB);\n\t\t\t\t\tif (opSpaceIdA !== opSpaceIdB) {\n\t\t\t\t\t\tcompressorB.normalizeToOpSpace(sessionSpaceIdB);\n\t\t\t\t\t\tcompressorA.normalizeToOpSpace(sessionSpaceIdA);\n\t\t\t\t\t}\n\t\t\t\t\texpect(opSpaceIdA).to.equal(opSpaceIdB);\n\t\t\t\t\tif (isLocalId(opSpaceIdB)) {\n\t\t\t\t\t\tfail('IDs should have been finalized.');\n\t\t\t\t\t}\n\t\t\t\t\tconst uuidBOpSpace = compressorB.decompress(opSpaceIdB);\n\t\t\t\t\texpect(uuidAOpSpace).to.equal(uuidBOpSpace);\n\t\t\t\t}\n\n\t\t\t\trowCount += 1;\n\t\t\t}\n\n\t\t\t// A local count > 1 indicates that this ID was unified, as more than one client has a local ID for it\n\t\t\t// in their session space.\n\t\t\tif (rowCount === this.sequencedIdLogs.size && localCount <= 1) {\n\t\t\t\texpect(localCount).to.equal(1);\n\t\t\t\tfor (const [[compressor, { id, originatingClient }]] of getLogIndices(i)) {\n\t\t\t\t\texpect(compressor.attributeId(id)).to.equal(originatingClient);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\texpect(uuids.size).to.equal(finalIds.size);\n\t\t\tassert(originatingClient !== undefined);\n\t\t\tidIndicesAggregator.set(\n\t\t\t\toriginatingClient,\n\t\t\t\tassertNotUndefined(idIndicesAggregator.get(originatingClient)) + 1\n\t\t\t);\n\t\t}\n\n\t\tfor (const [compressor] of sequencedLogs) {\n\t\t\texpectSerializes(compressor);\n\t\t}\n\t}\n}\n\n/**\n * Roundtrips the supplied compressor through serialization and deserialization.\n */\nexport function roundtrip(\n\tcompressor: ReadonlyIdCompressor,\n\twithSession: true\n): [SerializedIdCompressorWithOngoingSession, IdCompressor];\n\n/**\n * Roundtrips the supplied compressor through serialization and deserialization.\n */\nexport function roundtrip(\n\tcompressor: ReadonlyIdCompressor,\n\twithSession: false\n): [SerializedIdCompressorWithNoSession, IdCompressor];\n\nexport function roundtrip(\n\tcompressor: ReadonlyIdCompressor,\n\twithSession: boolean\n): [SerializedIdCompressorWithOngoingSession | SerializedIdCompressorWithNoSession, IdCompressor] {\n\tif (withSession) {\n\t\tconst serialized = compressor.serialize(withSession);\n\t\treturn [serialized, IdCompressor.deserialize(serialized)];\n\t}\n\n\tconst nonLocalSerialized = compressor.serialize(withSession);\n\treturn [nonLocalSerialized, IdCompressor.deserialize(nonLocalSerialized, createSessionId())];\n}\n\n/**\n * Asserts that the supplied compressor correctly roundtrips through serialization/deserialization.\n */\nexport function expectSerializes(\n\tcompressor: ReadonlyIdCompressor\n): [SerializedIdCompressorWithNoSession, SerializedIdCompressorWithOngoingSession] {\n\tfunction expectSerializes(\n\t\twithSession: boolean\n\t): SerializedIdCompressorWithOngoingSession | SerializedIdCompressorWithNoSession {\n\t\tlet serialized: SerializedIdCompressorWithOngoingSession | SerializedIdCompressorWithNoSession;\n\t\tlet deserialized: IdCompressor;\n\t\tif (withSession) {\n\t\t\t[serialized, deserialized] = roundtrip(compressor, true);\n\t\t} else {\n\t\t\t[serialized, deserialized] = roundtrip(compressor, false);\n\t\t}\n\t\tconst chainCount: number[] = [];\n\t\tfor (let i = 0; i < serialized.sessions.length; i++) {\n\t\t\tchainCount[i] = 0;\n\t\t}\n\t\tconst chainProcessed: number[] = [...chainCount];\n\n\t\tfor (const cluster of serialized.clusters) {\n\t\t\tconst [sessionIndex] = cluster;\n\t\t\texpect(sessionIndex < serialized.sessions.length);\n\t\t\tchainCount[sessionIndex]++;\n\t\t}\n\n\t\tfor (const cluster of serialized.clusters) {\n\t\t\tconst [sessionIndex, capacity, maybeSize] = cluster;\n\t\t\tconst chainIndex = chainProcessed[sessionIndex];\n\t\t\tif (chainIndex < chainCount[sessionIndex] - 1) {\n\t\t\t\texpect(maybeSize === undefined);\n\t\t\t} else {\n\t\t\t\texpect(maybeSize === undefined || typeof maybeSize !== 'number' || maybeSize < capacity);\n\t\t\t}\n\t\t\tchainProcessed[sessionIndex]++;\n\t\t}\n\n\t\texpect(compressor.equals(deserialized, withSession)).to.be.true;\n\t\treturn serialized;\n\t}\n\n\treturn [\n\t\texpectSerializes(false) as SerializedIdCompressorWithNoSession,\n\t\texpectSerializes(true) as SerializedIdCompressorWithOngoingSession,\n\t];\n}\n\n/**\n * Merges 'from' into 'to', and returns 'to'.\n */\nexport function mergeArrayMaps<K, V>(\n\tto: Pick<Map<K, V[]>, 'get' | 'set'>,\n\tfrom: ReadonlyMap<K, V[]>\n): Pick<Map<K, V[]>, 'get' | 'set'> {\n\tfor (const [key, value] of from.entries()) {\n\t\tconst entry = to.get(key);\n\t\tif (entry !== undefined) {\n\t\t\tentry.push(...value);\n\t\t} else {\n\t\t\tto.set(key, [...value]);\n\t\t}\n\t}\n\treturn to;\n}\n\nenum Operation {\n\tAllocateIds,\n\tDeliverOperations,\n\tChangeCapacity,\n\tGenerateUnifyingIds,\n\tGoOfflineThenResume,\n}\n\n/**\n * Performs random actions on a test network.\n * @param network the test network to test\n * @param seed the seed for the random generation of the fuzz actions\n * @param includeOverrides whether or not the fuzz actions will generate override UUIDs\n * @param observerClient if provided, this client will never generate local ids\n * @param synchronizeAtEnd if provided, all client will have all operations delivered from the server at the end of the test\n * @param numUsages if provided, the number of operations to perform as part of this test. Defaults to 1000.\n * @param validator if provided, this callback will be invoked periodically during the fuzz test.\n */\nexport function performFuzzActions(\n\tnetwork: IdCompressorTestNetwork,\n\tseed: number,\n\tincludeOverrides: boolean,\n\tobserverClient?: Client,\n\tsynchronizeAtEnd: boolean = true,\n\tnumUsages = 1000,\n\tmaxClusterSize = 25,\n\tvalidator?: (network: IdCompressorTestNetwork) => void\n): void {\n\tconst rand = new Prando(seed);\n\tconst selectableClients: Client[] = network.getTargetCompressors(MetaClient.All).map(([client]) => client);\n\tconst activeClients = selectableClients.filter((c) => c !== observerClient);\n\t// Ensure that the same UUIDs are generated for the same seed across different calls\n\tlet uuidNum = 0;\n\tconst uuidNamespace = 'ece2be2e-f374-4ca8-b034-a0bac2da69da';\n\tlet clusterSize: number = network.initialClusterSize;\n\tif (clusterSize > maxClusterSize) {\n\t\tnetwork.enqueueCapacityChange(maxClusterSize);\n\t\tclusterSize = maxClusterSize;\n\t}\n\n\tconst opWeights: [Operation, number][] = [\n\t\t[Operation.ChangeCapacity, 1],\n\t\t[Operation.AllocateIds, 8],\n\t\t[Operation.DeliverOperations, 4],\n\t\t[Operation.GenerateUnifyingIds, 1],\n\t\t[Operation.GoOfflineThenResume, 1],\n\t];\n\n\tconst opSums: [Operation, number][] = [];\n\tlet prevWeight = 0;\n\tfor (const opWeight of opWeights) {\n\t\tconst weight = prevWeight + opWeight[1];\n\t\topSums.push([opWeight[0], weight]);\n\t\tprevWeight = weight;\n\t}\n\n\tfor (let i = 0; i < numUsages; i++) {\n\t\tconst weightSelected = rand.nextInt(1, prevWeight);\n\t\tlet opIndex = 0;\n\t\twhile (weightSelected > opSums[opIndex][1]) {\n\t\t\topIndex++;\n\t\t}\n\t\tconst operation = opWeights[opIndex][0];\n\t\tswitch (operation) {\n\t\t\tcase Operation.ChangeCapacity: {\n\t\t\t\tclusterSize = Math.min(Math.floor(rand.next(0, 1) ** 2 * maxClusterSize) + 1, maxClusterSize);\n\t\t\t\tnetwork.enqueueCapacityChange(clusterSize);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase Operation.AllocateIds: {\n\t\t\t\tconst client = rand.nextArrayItem(activeClients);\n\t\t\t\tconst maxIdsPerUsage = clusterSize * 2;\n\t\t\t\tconst numIds = Math.floor(rand.next(0, 1) ** 2 * maxIdsPerUsage) + 1;\n\t\t\t\tconst overrides: { [index: number]: string } = {};\n\t\t\t\tif (includeOverrides && /* 25% chance: */ rand.nextInt(0, 3) === 0) {\n\t\t\t\t\tfor (let j = 0; j < numIds; j++) {\n\t\t\t\t\t\tif (/* 33% chance: */ rand.nextInt(0, 2) === 0) {\n\t\t\t\t\t\t\toverrides[j] = v5((uuidNum++).toString(), uuidNamespace);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tnetwork.allocateAndSendIds(client, numIds, overrides);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase Operation.DeliverOperations: {\n\t\t\t\tconst client = rand.nextArrayItem([...selectableClients, MetaClient.All]);\n\t\t\t\tnetwork.deliverOperations(client);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase Operation.GenerateUnifyingIds: {\n\t\t\t\tconst clientA = rand.nextArrayItem(activeClients);\n\t\t\t\tconst clientB = rand.nextArrayItem(activeClients.filter((c) => c !== clientA));\n\t\t\t\tconst uuid = v5((uuidNum++).toString(), uuidNamespace);\n\t\t\t\tnetwork.allocateAndSendIds(clientA, 1, { 0: uuid });\n\t\t\t\tnetwork.allocateAndSendIds(clientB, 1, { 0: uuid });\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase Operation.GoOfflineThenResume: {\n\t\t\t\tconst client = rand.nextArrayItem(activeClients);\n\t\t\t\tnetwork.goOfflineThenResume(client);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\tthrow new Error('Unknown operation.');\n\t\t}\n\t\tif (i !== 0 && i % Math.round(numUsages / 5) === 0) {\n\t\t\tvalidator?.(network);\n\t\t}\n\t}\n\n\tif (synchronizeAtEnd) {\n\t\tnetwork.deliverOperations(DestinationClient.All);\n\t\tvalidator?.(network);\n\t}\n}\n\n/**\n * Converts the supplied integer to a uuid.\n */\nexport function integerToStableId(num: number | bigint): StableId {\n\tconst bigintNum = BigInt(num);\n\tconst upper = bigintNum >> BigInt(74);\n\tconst middle = (bigintNum & (BigInt(0xfff) << BigInt(62))) >> BigInt(62);\n\tconst lower = bigintNum & BigInt('0x3fffffffffffffff');\n\tconst upperString = padToLength(upper.toString(16), '0', 12);\n\tconst middleString = `4${padToLength(middle.toString(16), '0', 3)}`;\n\tconst lowerString = padToLength((BigInt('0x8000000000000000') | BigInt(lower)).toString(16), '0', 16);\n\tconst uuid = upperString + middleString + lowerString;\n\treturn assertIsStableId(\n\t\t`${uuid.substr(0, 8)}-${uuid.substr(8, 4)}-${uuid.substr(12, 4)}-${uuid.substr(16, 4)}-${uuid.substr(20)}`\n\t);\n}\n\n/**\n * Pads the strings to a length of 32 with zeroes.\n */\nexport function padToUuidLength(str: string): string {\n\treturn padToLength(str, '0', 32);\n}\n\nfunction padToLength(str: string, char: string, length: number): string {\n\treturn char.repeat(length - str.length) + str;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"IdCompressorTestUtilities.js","sourceRoot":"","sources":["../../../src/test/utilities/IdCompressorTestUtilities.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\n/* eslint-disable no-bitwise */\n\nimport { expect } from 'chai';\nimport {\n\tGenerator,\n\tcreateWeightedGenerator,\n\tinterleave,\n\tmakeRandom,\n\tperformFuzzActions as performFuzzActionsBase,\n\trepeat,\n\tSaveInfo,\n\ttake,\n\tBaseFuzzTestState,\n} from '@fluid-internal/stochastic-test-utils';\nimport { assert, assertNotUndefined, ClosedMap, fail, getOrCreate } from '../../Common';\nimport { IdCompressor, IdRangeDescriptor, isLocalId } from '../../id-compressor/IdCompressor';\nimport {\n\tcreateSessionId,\n\tensureSessionUuid,\n\tNumericUuid,\n\tnumericUuidFromStableId,\n\tstableIdFromNumericUuid,\n} from '../../id-compressor/NumericUuid';\nimport { FinalCompressedId, SessionId, StableId, SessionSpaceCompressedId, AttributionId } from '../../Identifiers';\nimport { getIds } from '../../id-compressor/IdRange';\nimport type {\n\tIdCreationRange,\n\tSerializedIdCompressorWithOngoingSession,\n\tSerializedIdCompressorWithNoSession,\n} from '../../id-compressor';\nimport { assertIsStableId, assertIsUuidString } from '../../UuidUtilities';\nimport { expectDefined } from './TestCommon';\n\n/** Identifies a compressor in a network */\nexport enum Client {\n\tClient1 = 'Client1',\n\tClient2 = 'Client2',\n\tClient3 = 'Client3',\n}\n\n/** Identifies a compressor with respect to a specific operation */\nexport enum SemanticClient {\n\tLocalClient = 'LocalClient',\n}\n\n/** Identifies categories of compressors */\nexport enum MetaClient {\n\tAll = 'All',\n}\n\n/**\n * Used to attribute actions to clients in a distributed collaboration session.\n * `Local` implies a local and unsequenced operation. All others imply sequenced operations.\n */\nexport type OriginatingClient = Client | SemanticClient;\nexport const OriginatingClient = { ...Client, ...SemanticClient };\n\n/** Identifies a compressor to which to send an operation */\nexport type DestinationClient = Client | MetaClient;\nexport const DestinationClient = { ...Client, ...MetaClient };\n\n/**\n * Creates a new compressor with the supplied cluster capacity.\n */\nexport function createCompressor(client: Client, clusterCapacity = 5, attributionId?: AttributionId): IdCompressor {\n\tconst compressor = new IdCompressor(sessionIds.get(client), 1024, attributionId);\n\tcompressor.clusterCapacity = clusterCapacity;\n\treturn compressor;\n}\n\n/**\n * A closed map from NamedClient to T.\n */\nexport type ClientMap<T> = ClosedMap<Client, T>;\n\nfunction makeSessionIds(): ClientMap<SessionId> {\n\tconst stableIds = new Map<Client, SessionId>();\n\tconst clients = Object.values(Client);\n\tfor (let i = 0; i < clients.length; i++) {\n\t\t// Place session uuids roughly in the middle of uuid space to increase odds of encountering interesting\n\t\t// orderings in sorted collections\n\t\tconst sessionId = ensureSessionUuid(assertIsStableId(`88888888-8888-4888-b${i}88-888888888888`));\n\t\tstableIds.set(clients[i], sessionId);\n\t}\n\treturn stableIds as ClientMap<SessionId>;\n}\n\n/**\n * An array of session ID strings corresponding to all non-local `Client` entries.\n */\nexport const sessionIds = makeSessionIds();\n\n/**\n * An array of session uuids corresponding to all non-local `Client` entries.\n */\nexport const sessionNumericUuids = new Map(\n\t[...sessionIds.entries()].map(([client, sessionId]) => {\n\t\treturn [client, numericUuidFromStableId(sessionId)];\n\t})\n) as ClientMap<NumericUuid>;\n\nexport const attributionIds = new Map(\n\tObject.values(Client).map((c, i) => [\n\t\tc,\n\t\tassertIsUuidString(`00000000-0000-0000-0000-${(i + 1).toString(16).padStart(12, '0')}`),\n\t])\n) as ClientMap<AttributionId>;\n\n/** An immutable view of an `IdCompressor` */\nexport interface ReadonlyIdCompressor\n\textends Omit<\n\t\tIdCompressor,\n\t\t'generateCompressedId' | 'generateCompressedIdRange' | 'takeNextCreationRange' | 'finalizeCreationRange'\n\t> {\n\treadonly clusterCapacity: number;\n}\n\n/** Information about a generated ID in a network to be validated by tests */\nexport interface TestIdData {\n\treadonly id: SessionSpaceCompressedId;\n\treadonly originatingClient: Client;\n\treadonly sessionId: SessionId;\n\treadonly sessionNumericUuid: NumericUuid;\n\treadonly expectedOverride: string | undefined;\n\treadonly isSequenced: boolean;\n}\n\n/**\n * Simulates a network of ID compressors.\n * Not suitable for performance testing.\n */\nexport class IdCompressorTestNetwork {\n\t/** The compressors used in this network */\n\tprivate readonly compressors: ClientMap<IdCompressor>;\n\t/** The log of operations seen by the server so far. Append-only. */\n\tprivate readonly serverOperations: ([range: IdCreationRange, clientFrom: Client] | number)[] = [];\n\t/** An index into `serverOperations` for each client which represents how many operations have been delivered to that client */\n\tprivate readonly clientProgress: ClientMap<number>;\n\t/** All ids (local and sequenced) that a client has created or received, in order. */\n\tprivate readonly idLogs: ClientMap<TestIdData[]>;\n\t/** All ids that a client has received from the server, in order. */\n\tprivate readonly sequencedIdLogs: ClientMap<TestIdData[]>;\n\n\tpublic constructor(\n\t\tpublic readonly initialClusterSize = 5,\n\t\tprivate readonly onIdReceived?: (network: IdCompressorTestNetwork, clientTo: Client, ids: TestIdData[]) => void\n\t) {\n\t\tconst compressors = new Map<Client, IdCompressor>();\n\t\tconst clientProgress = new Map<Client, number>();\n\t\tconst clientIds = new Map<Client, TestIdData[]>();\n\t\tconst clientSequencedIds = new Map<Client, TestIdData[]>();\n\t\tfor (const client of Object.values(Client)) {\n\t\t\tconst compressor = createCompressor(client, initialClusterSize, attributionIds.get(client));\n\t\t\tcompressors.set(client, compressor);\n\t\t\tclientProgress.set(client, 0);\n\t\t\tclientIds.set(client, []);\n\t\t\tclientSequencedIds.set(client, []);\n\t\t}\n\t\tthis.compressors = compressors as ClientMap<IdCompressor>;\n\t\tthis.clientProgress = clientProgress as ClientMap<number>;\n\t\tthis.idLogs = clientIds as ClientMap<TestIdData[]>;\n\t\tthis.sequencedIdLogs = clientSequencedIds as ClientMap<TestIdData[]>;\n\t}\n\n\t/**\n\t * Returns an immutable handle to a compressor in the network.\n\t */\n\tpublic getCompressor(client: Client): ReadonlyIdCompressor {\n\t\tconst compressors = this.compressors;\n\t\tconst handler = {\n\t\t\tget(_, property) {\n\t\t\t\tconst compressor = compressors.get(client);\n\t\t\t\treturn compressor[property];\n\t\t\t},\n\t\t\tset(_, property, value): boolean {\n\t\t\t\tconst compressor = compressors.get(client);\n\t\t\t\tcompressor[property] = value;\n\t\t\t\treturn true;\n\t\t\t},\n\t\t};\n\t\treturn new Proxy<IdCompressor>({} as unknown as IdCompressor, handler);\n\t}\n\n\t/**\n\t * Returns a mutable handle to a compressor in the network. Use of mutation methods will break the network invariants and\n\t * should only be used if the network will not be used again.\n\t */\n\tpublic getCompressorUnsafe(client: Client): IdCompressor {\n\t\treturn this.getCompressor(client) as IdCompressor;\n\t}\n\n\t/**\n\t * Returns data for all IDs created and received by this client, including ack's of their own (i.e. their own IDs will appear twice)\n\t */\n\tpublic getIdLog(client: Client): readonly TestIdData[] {\n\t\treturn this.idLogs.get(client);\n\t}\n\n\t/**\n\t * Returns data for all IDs received by this client, including ack's of their own (i.e. their own IDs will appear twice)\n\t */\n\tpublic getSequencedIdLog(client: Client): readonly TestIdData[] {\n\t\treturn this.sequencedIdLogs.get(client);\n\t}\n\n\t/**\n\t * Get all compressors for the given destination\n\t */\n\tpublic getTargetCompressors(clientTo: DestinationClient): [Client, IdCompressor][] {\n\t\treturn clientTo === MetaClient.All\n\t\t\t? [...this.compressors.entries()]\n\t\t\t: ([[clientTo, this.getCompressor(clientTo)]] as [Client, IdCompressor][]);\n\t}\n\n\t/**\n\t * Submit a capacity change operation to the network. It will not take effect immediately but will be processed in sequence order.\n\t */\n\tpublic enqueueCapacityChange(newClusterCapacity: number): void {\n\t\tthis.serverOperations.push(newClusterCapacity);\n\t}\n\n\tprivate addNewId(\n\t\tclient: Client,\n\t\tid: SessionSpaceCompressedId,\n\t\texpectedOverride: string | undefined,\n\t\toriginatingClient: Client,\n\t\tisSequenced: boolean\n\t): void {\n\t\tconst idData = {\n\t\t\tid,\n\t\t\toriginatingClient,\n\t\t\tsessionId: sessionIds.get(originatingClient),\n\t\t\tsessionNumericUuid: sessionNumericUuids.get(originatingClient),\n\t\t\texpectedOverride,\n\t\t\tisSequenced,\n\t\t};\n\t\tconst clientIds = this.idLogs.get(client);\n\t\tclientIds.push(idData);\n\t\tif (isSequenced) {\n\t\t\tconst sequencedIds = this.sequencedIdLogs.get(client);\n\t\t\tsequencedIds.push(idData);\n\t\t}\n\t\tthis.onIdReceived?.(this, client, clientIds);\n\t}\n\n\t/**\n\t * Allocates a new range of local IDs and enqueues them for future delivery via a `testIdDelivery` action.\n\t * Calls to this method determine the total order of delivery, regardless of when `deliverOperations` is called.\n\t */\n\tpublic allocateAndSendIds(\n\t\tclient: Client,\n\t\tnumIds: number\n\t): { range: IdRangeDescriptor<SessionSpaceCompressedId>; sessionId: SessionId };\n\n\t/**\n\t * Allocates a new range of local IDs and enqueues them for future delivery via a `testIdDelivery` action.\n\t * Calls to this method determine the total order of delivery, regardless of when `deliverOperations` is called.\n\t */\n\tpublic allocateAndSendIds(client: Client, numIds: number, overrides: { [index: number]: string }): IdCreationRange;\n\n\tpublic allocateAndSendIds(\n\t\tclient: Client,\n\t\tnumIds: number,\n\t\toverrides: { [index: number]: string } = {}\n\t): { range: IdRangeDescriptor<SessionSpaceCompressedId>; sessionId: SessionId } | IdCreationRange {\n\t\tassert(numIds > 0, 'Must allocate a non-zero number of IDs');\n\t\tconst compressor = this.compressors.get(client);\n\t\tlet nextIdIndex = 0;\n\t\tfor (const [overrideIndex, uuid] of Object.entries(overrides)\n\t\t\t.map(([id, uuid]) => [Number.parseInt(id, 10), uuid] as [number, string])\n\t\t\t.sort(([a], [b]) => a - b)) {\n\t\t\twhile (nextIdIndex < overrideIndex) {\n\t\t\t\tthis.addNewId(client, compressor.generateCompressedId(), undefined, client, false);\n\t\t\t\tnextIdIndex += 1;\n\t\t\t}\n\t\t\tthis.addNewId(client, compressor.generateCompressedId(uuid), uuid, client, false);\n\t\t\tnextIdIndex += 1;\n\t\t}\n\t\tconst numTrailingIds = numIds - nextIdIndex;\n\t\tlet range: IdRangeDescriptor<SessionSpaceCompressedId> | undefined;\n\t\tif (numTrailingIds > 0) {\n\t\t\trange = compressor.generateCompressedIdRange(numTrailingIds);\n\t\t\tconst ids = compressor.getIdsFromRange(range, compressor.localSessionId);\n\t\t\tfor (let i = 0; i < numTrailingIds; i++) {\n\t\t\t\tthis.addNewId(client, ids.get(i), undefined, client, false);\n\t\t\t}\n\t\t}\n\t\tconst creationRange = compressor.takeNextCreationRange();\n\t\tthis.serverOperations.push([creationRange, client]);\n\t\treturn nextIdIndex === 0 ? { range: range ?? fail(), sessionId: compressor.localSessionId } : creationRange;\n\t}\n\n\t/**\n\t * Delivers all undelivered ID ranges and cluster capacity changes from the server to the target clients.\n\t */\n\tpublic deliverOperations(clientTakingDelivery: DestinationClient) {\n\t\tfor (const [clientTo, compressorTo] of this.getTargetCompressors(clientTakingDelivery)) {\n\t\t\tfor (let i = this.clientProgress.get(clientTo); i < this.serverOperations.length; i++) {\n\t\t\t\tconst operation = this.serverOperations[i];\n\t\t\t\tif (typeof operation === 'number') {\n\t\t\t\t\tcompressorTo.clusterCapacity = operation;\n\t\t\t\t} else {\n\t\t\t\t\tconst [range, clientFrom] = operation;\n\t\t\t\t\tcompressorTo.finalizeCreationRange(range);\n\n\t\t\t\t\tconst ids = getIds(range);\n\t\t\t\t\tif (ids !== undefined) {\n\t\t\t\t\t\tlet overrideIndex = 0;\n\t\t\t\t\t\tconst overrides = ids.overrides;\n\t\t\t\t\t\tfor (let id = ids.first; id >= ids.last; id--) {\n\t\t\t\t\t\t\tlet override: string | undefined;\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\toverrides !== undefined &&\n\t\t\t\t\t\t\t\toverrideIndex < overrides.length &&\n\t\t\t\t\t\t\t\tid === overrides[overrideIndex][0]\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\toverride = overrides[overrideIndex][1];\n\t\t\t\t\t\t\t\toverrideIndex++;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst sessionSpaceId = compressorTo.normalizeToSessionSpace(id, range.sessionId);\n\t\t\t\t\t\t\tthis.addNewId(clientTo, sessionSpaceId, override, clientFrom, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tassert(overrideIndex === (overrides?.length ?? 0));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.clientProgress.set(clientTo, this.serverOperations.length);\n\t\t}\n\t}\n\n\t/**\n\t * Simulate a client disconnecting (and serializing), then reconnecting (and deserializing)\n\t */\n\tpublic goOfflineThenResume(client: Client): void {\n\t\tconst compressor = this.compressors.get(client);\n\t\tconst [_, resumedCompressor] = roundtrip(compressor, true);\n\t\tthis.compressors.set(client, resumedCompressor);\n\t}\n\n\t/**\n\t * Ensure general validity of the network state. Useful for calling periodically or at the end of test scenarios.\n\t */\n\tpublic assertNetworkState(): void {\n\t\tconst sequencedLogs = Object.values(Client).map(\n\t\t\t(client) => [this.compressors.get(client), this.getSequencedIdLog(client)] as const\n\t\t);\n\n\t\tconst maxLogLength = sequencedLogs.map(([_, data]) => data.length).reduce((p, n) => Math.max(p, n));\n\n\t\tfunction getNextLogWithEntryAt(logsIndex: number, entryIndex: number): number | undefined {\n\t\t\tfor (let i = logsIndex; i < sequencedLogs.length; i++) {\n\t\t\t\tconst log = sequencedLogs[i];\n\t\t\t\tif (log[1].length > entryIndex) {\n\t\t\t\t\treturn i;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst uuids = new Set<string>();\n\t\tconst finalIds = new Set<FinalCompressedId>();\n\t\tconst idIndicesAggregator = new Map<Client, number>();\n\n\t\tfunction* getLogIndices(\n\t\t\tcolumnIndex: number\n\t\t): Iterable<\n\t\t\t[\n\t\t\t\tcurrent: [compressor: IdCompressor, idData: TestIdData],\n\t\t\t\tnext?: [compressor: IdCompressor, idData: TestIdData]\n\t\t\t]\n\t\t> {\n\t\t\tlet current = getNextLogWithEntryAt(0, columnIndex);\n\t\t\twhile (current !== undefined) {\n\t\t\t\tconst next = getNextLogWithEntryAt(current + 1, columnIndex);\n\t\t\t\tconst [compressor, log] = sequencedLogs[current];\n\t\t\t\tif (next === undefined) {\n\t\t\t\t\tyield [[compressor, log[columnIndex]]];\n\t\t\t\t} else {\n\t\t\t\t\tconst [compressorNext, logNext] = sequencedLogs[next];\n\t\t\t\t\tyield [\n\t\t\t\t\t\t[compressor, log[columnIndex]],\n\t\t\t\t\t\t[compressorNext, logNext[columnIndex]],\n\t\t\t\t\t];\n\t\t\t\t}\n\t\t\t\tcurrent = next;\n\t\t\t}\n\t\t}\n\n\t\tfor (let i = 0; i < maxLogLength; i++) {\n\t\t\tconst creator: [creator: Client, override?: string][] = [];\n\t\t\tlet originatingClient: Client | undefined;\n\t\t\tlet localCount = 0;\n\t\t\tlet rowCount = 0;\n\t\t\tfor (const [current, next] of getLogIndices(i)) {\n\t\t\t\tconst [compressorA, idDataA] = current;\n\t\t\t\tconst sessionSpaceIdA = idDataA.id;\n\t\t\t\tif (isLocalId(sessionSpaceIdA)) {\n\t\t\t\t\tlocalCount += 1;\n\t\t\t\t}\n\t\t\t\tconst idIndex = getOrCreate(idIndicesAggregator, idDataA.originatingClient, () => 0);\n\t\t\t\toriginatingClient ??= idDataA.originatingClient;\n\t\t\t\tassert(\n\t\t\t\t\tidDataA.originatingClient === originatingClient,\n\t\t\t\t\t'Test infra gave wrong originating client to TestIdData'\n\t\t\t\t);\n\t\t\t\tconst attributionA = compressorA.attributeId(idDataA.id);\n\t\t\t\tif (attributionA !== attributionIds.get(idDataA.originatingClient)) {\n\t\t\t\t\t// Unification\n\t\t\t\t\texpectDefined(idDataA.expectedOverride);\n\t\t\t\t}\n\n\t\t\t\t// Only one client should have this ID as local in its session space, as only one client could have created this ID\n\t\t\t\tif (isLocalId(sessionSpaceIdA)) {\n\t\t\t\t\tlocalCount++;\n\t\t\t\t\texpect(idDataA.sessionId).to.equal(this.compressors.get(originatingClient).localSessionId);\n\t\t\t\t\texpect(creator.length === 0 || creator[creator.length - 1][1] === idDataA.expectedOverride).to.be\n\t\t\t\t\t\t.true;\n\t\t\t\t\tcreator.push([originatingClient, idDataA.expectedOverride]);\n\t\t\t\t}\n\n\t\t\t\tconst uuidASessionSpace = compressorA.decompress(sessionSpaceIdA);\n\t\t\t\tif (idDataA.expectedOverride !== undefined) {\n\t\t\t\t\texpect(uuidASessionSpace).to.equal(idDataA.expectedOverride);\n\t\t\t\t} else {\n\t\t\t\t\texpect(uuidASessionSpace).to.equal(stableIdFromNumericUuid(idDataA.sessionNumericUuid, idIndex));\n\t\t\t\t}\n\t\t\t\texpect(compressorA.recompress(uuidASessionSpace)).to.equal(sessionSpaceIdA);\n\t\t\t\tuuids.add(uuidASessionSpace);\n\t\t\t\tconst opSpaceIdA = compressorA.normalizeToOpSpace(sessionSpaceIdA);\n\t\t\t\tif (isLocalId(opSpaceIdA)) {\n\t\t\t\t\texpect.fail('IDs should have been finalized.');\n\t\t\t\t}\n\t\t\t\t// TODO: This cast can be removed on typescript 4.6\n\t\t\t\tfinalIds.add(opSpaceIdA as FinalCompressedId);\n\t\t\t\tconst uuidAOpSpace = compressorA.decompress(opSpaceIdA);\n\n\t\t\t\texpect(uuidASessionSpace).to.equal(uuidAOpSpace);\n\n\t\t\t\tif (next !== undefined) {\n\t\t\t\t\tconst [compressorB, idDataB] = next;\n\t\t\t\t\tconst sessionSpaceIdB = idDataB.id;\n\n\t\t\t\t\tconst uuidBSessionSpace = compressorB.decompress(sessionSpaceIdB);\n\t\t\t\t\texpect(uuidASessionSpace).to.equal(uuidBSessionSpace);\n\t\t\t\t\tconst opSpaceIdB = compressorB.normalizeToOpSpace(sessionSpaceIdB);\n\t\t\t\t\tif (opSpaceIdA !== opSpaceIdB) {\n\t\t\t\t\t\tcompressorB.normalizeToOpSpace(sessionSpaceIdB);\n\t\t\t\t\t\tcompressorA.normalizeToOpSpace(sessionSpaceIdA);\n\t\t\t\t\t}\n\t\t\t\t\texpect(opSpaceIdA).to.equal(opSpaceIdB);\n\t\t\t\t\tif (isLocalId(opSpaceIdB)) {\n\t\t\t\t\t\tfail('IDs should have been finalized.');\n\t\t\t\t\t}\n\t\t\t\t\tconst uuidBOpSpace = compressorB.decompress(opSpaceIdB);\n\t\t\t\t\texpect(uuidAOpSpace).to.equal(uuidBOpSpace);\n\t\t\t\t}\n\n\t\t\t\trowCount += 1;\n\t\t\t}\n\n\t\t\t// A local count > 1 indicates that this ID was unified, as more than one client has a local ID for it\n\t\t\t// in their session space.\n\t\t\tif (rowCount === this.sequencedIdLogs.size && localCount <= 1) {\n\t\t\t\texpect(localCount).to.equal(1);\n\t\t\t\tfor (const [[compressor, { id, originatingClient }]] of getLogIndices(i)) {\n\t\t\t\t\texpect(compressor.attributeId(id)).to.equal(originatingClient);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\texpect(uuids.size).to.equal(finalIds.size);\n\t\t\tassert(originatingClient !== undefined);\n\t\t\tidIndicesAggregator.set(\n\t\t\t\toriginatingClient,\n\t\t\t\tassertNotUndefined(idIndicesAggregator.get(originatingClient)) + 1\n\t\t\t);\n\t\t}\n\n\t\tfor (const [compressor] of sequencedLogs) {\n\t\t\texpectSerializes(compressor);\n\t\t}\n\t}\n}\n\n/**\n * Roundtrips the supplied compressor through serialization and deserialization.\n */\nexport function roundtrip(\n\tcompressor: ReadonlyIdCompressor,\n\twithSession: true\n): [SerializedIdCompressorWithOngoingSession, IdCompressor];\n\n/**\n * Roundtrips the supplied compressor through serialization and deserialization.\n */\nexport function roundtrip(\n\tcompressor: ReadonlyIdCompressor,\n\twithSession: false\n): [SerializedIdCompressorWithNoSession, IdCompressor];\n\nexport function roundtrip(\n\tcompressor: ReadonlyIdCompressor,\n\twithSession: boolean\n): [SerializedIdCompressorWithOngoingSession | SerializedIdCompressorWithNoSession, IdCompressor] {\n\tif (withSession) {\n\t\tconst serialized = compressor.serialize(withSession);\n\t\treturn [serialized, IdCompressor.deserialize(serialized)];\n\t}\n\n\tconst nonLocalSerialized = compressor.serialize(withSession);\n\treturn [nonLocalSerialized, IdCompressor.deserialize(nonLocalSerialized, createSessionId())];\n}\n\n/**\n * Asserts that the supplied compressor correctly roundtrips through serialization/deserialization.\n */\nexport function expectSerializes(\n\tcompressor: ReadonlyIdCompressor\n): [SerializedIdCompressorWithNoSession, SerializedIdCompressorWithOngoingSession] {\n\tfunction expectSerializes(\n\t\twithSession: boolean\n\t): SerializedIdCompressorWithOngoingSession | SerializedIdCompressorWithNoSession {\n\t\tlet serialized: SerializedIdCompressorWithOngoingSession | SerializedIdCompressorWithNoSession;\n\t\tlet deserialized: IdCompressor;\n\t\tif (withSession) {\n\t\t\t[serialized, deserialized] = roundtrip(compressor, true);\n\t\t} else {\n\t\t\t[serialized, deserialized] = roundtrip(compressor, false);\n\t\t}\n\t\tconst chainCount: number[] = [];\n\t\tfor (let i = 0; i < serialized.sessions.length; i++) {\n\t\t\tchainCount[i] = 0;\n\t\t}\n\t\tconst chainProcessed: number[] = [...chainCount];\n\n\t\tfor (const cluster of serialized.clusters) {\n\t\t\tconst [sessionIndex] = cluster;\n\t\t\texpect(sessionIndex < serialized.sessions.length);\n\t\t\tchainCount[sessionIndex]++;\n\t\t}\n\n\t\tfor (const cluster of serialized.clusters) {\n\t\t\tconst [sessionIndex, capacity, maybeSize] = cluster;\n\t\t\tconst chainIndex = chainProcessed[sessionIndex];\n\t\t\tif (chainIndex < chainCount[sessionIndex] - 1) {\n\t\t\t\texpect(maybeSize === undefined);\n\t\t\t} else {\n\t\t\t\texpect(maybeSize === undefined || typeof maybeSize !== 'number' || maybeSize < capacity);\n\t\t\t}\n\t\t\tchainProcessed[sessionIndex]++;\n\t\t}\n\n\t\texpect(compressor.equals(deserialized, withSession)).to.be.true;\n\t\treturn serialized;\n\t}\n\n\treturn [\n\t\texpectSerializes(false) as SerializedIdCompressorWithNoSession,\n\t\texpectSerializes(true) as SerializedIdCompressorWithOngoingSession,\n\t];\n}\n\n/**\n * Merges 'from' into 'to', and returns 'to'.\n */\nexport function mergeArrayMaps<K, V>(\n\tto: Pick<Map<K, V[]>, 'get' | 'set'>,\n\tfrom: ReadonlyMap<K, V[]>\n): Pick<Map<K, V[]>, 'get' | 'set'> {\n\tfor (const [key, value] of from.entries()) {\n\t\tconst entry = to.get(key);\n\t\tif (entry !== undefined) {\n\t\t\tentry.push(...value);\n\t\t} else {\n\t\t\tto.set(key, [...value]);\n\t\t}\n\t}\n\treturn to;\n}\n\ninterface AllocateIds {\n\ttype: 'allocateIds';\n\tclient: Client;\n\tnumIds: number;\n\toverrides: { [index: number]: string };\n}\n\ninterface DeliverOperations {\n\ttype: 'deliverOperations';\n\tclient: DestinationClient;\n}\n\ninterface ChangeCapacity {\n\ttype: 'changeCapacity';\n\tnewSize: number;\n}\n\ninterface GenerateUnifyingIds {\n\ttype: 'generateUnifyingIds';\n\tclientA: Client;\n\tclientB: Client;\n\tuuid: string;\n}\n\n// Represents intent to go offline then resume.\ninterface Reconnect {\n\ttype: 'reconnect';\n\tclient: Client;\n}\n\ninterface Validate {\n\ttype: 'validate';\n}\n\ntype Operation = AllocateIds | DeliverOperations | ChangeCapacity | GenerateUnifyingIds | Reconnect | Validate;\n\ninterface FuzzTestState extends BaseFuzzTestState {\n\tnetwork: IdCompressorTestNetwork;\n\tactiveClients: Client[];\n\tselectableClients: Client[];\n\tclusterSize: number;\n}\n\nexport interface OperationGenerationConfig {\n\t/** whether or not the fuzz actions will generate override UUIDs */\n\tincludeOverrides: boolean;\n\t/** maximum cluster size of the network. Default: 25 */\n\tmaxClusterSize?: number;\n\t/** Number of ops between validation ops. Default: 200 */\n\tvalidateInterval?: number;\n}\n\nconst defaultOptions = {\n\tincludeOverrides: false,\n\tmaxClusterSize: 25,\n\tvalidateInterval: 200,\n};\n\nexport function makeOpGenerator(options: OperationGenerationConfig): Generator<Operation, FuzzTestState> {\n\tconst { includeOverrides, maxClusterSize, validateInterval } = { ...defaultOptions, ...options };\n\n\tfunction allocateIdsGenerator({ activeClients, clusterSize, random }: FuzzTestState): AllocateIds {\n\t\tconst client = random.pick(activeClients);\n\t\tconst maxIdsPerUsage = clusterSize * 2;\n\t\tconst numIds = Math.floor(random.real(0, 1) ** 2 * maxIdsPerUsage) + 1;\n\t\tconst overrides: AllocateIds['overrides'] = {};\n\t\tif (includeOverrides && random.bool(1 / 4)) {\n\t\t\tfor (let j = 0; j < numIds; j++) {\n\t\t\t\tif (random.bool(1 / 3)) {\n\t\t\t\t\toverrides[j] = random.uuid4();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\ttype: 'allocateIds',\n\t\t\tclient,\n\t\t\tnumIds,\n\t\t\toverrides,\n\t\t};\n\t}\n\n\tfunction changeCapacityGenerator({ random }: FuzzTestState): ChangeCapacity {\n\t\treturn {\n\t\t\ttype: 'changeCapacity',\n\t\t\tnewSize: Math.min(Math.floor(random.real(0, 1) ** 2 * maxClusterSize) + 1, maxClusterSize),\n\t\t};\n\t}\n\n\tfunction deliverOperationsGenerator({ random, selectableClients }: FuzzTestState): DeliverOperations {\n\t\treturn {\n\t\t\ttype: 'deliverOperations',\n\t\t\tclient: random.pick([...selectableClients, MetaClient.All]),\n\t\t};\n\t}\n\n\tfunction generateUnifyingIdsGenerator({ activeClients, random }: FuzzTestState): GenerateUnifyingIds {\n\t\tconst clientA = random.pick(activeClients);\n\t\tconst clientB = random.pick(activeClients.filter((c) => c !== clientA));\n\t\treturn { type: 'generateUnifyingIds', clientA, clientB, uuid: random.uuid4() };\n\t}\n\n\tfunction reconnectGenerator({ activeClients, random }: FuzzTestState): Reconnect {\n\t\treturn { type: 'reconnect', client: random.pick(activeClients) };\n\t}\n\n\treturn interleave(\n\t\tcreateWeightedGenerator<Operation, FuzzTestState>([\n\t\t\t[changeCapacityGenerator, 1],\n\t\t\t[allocateIdsGenerator, 8],\n\t\t\t[deliverOperationsGenerator, 4],\n\t\t\t[generateUnifyingIdsGenerator, 1],\n\t\t\t[reconnectGenerator, 1],\n\t\t]),\n\t\ttake(1, repeat<Operation, FuzzTestState>({ type: 'validate' })),\n\t\tvalidateInterval\n\t);\n}\n\n/**\n * Performs random actions on a test network.\n * @param generator - the generator used to provide operations\n * @param network - the test network to test\n * @param seed - the seed for the random generation of the fuzz actions\n * @param observerClient - if provided, this client will never generate local ids\n * @param synchronizeAtEnd - if provided, all client will have all operations delivered from the server at the end of the test\n * @param validator - if provided, this callback will be invoked periodically during the fuzz test.\n */\nexport function performFuzzActions(\n\tgenerator: Generator<Operation, FuzzTestState>,\n\tnetwork: IdCompressorTestNetwork,\n\tseed: number,\n\tobserverClient?: Client,\n\tsynchronizeAtEnd: boolean = true,\n\tvalidator?: (network: IdCompressorTestNetwork) => void,\n\tsaveInfo?: SaveInfo\n): void {\n\tconst random = makeRandom(seed);\n\tconst selectableClients: Client[] = network.getTargetCompressors(MetaClient.All).map(([client]) => client);\n\n\tconst initialState: FuzzTestState = {\n\t\trandom,\n\t\tnetwork,\n\t\tactiveClients: selectableClients.filter((c) => c !== observerClient),\n\t\tselectableClients,\n\t\tclusterSize: network.initialClusterSize,\n\t};\n\n\tperformFuzzActionsBase(\n\t\tgenerator,\n\t\t{\n\t\t\tallocateIds: (state, { client, numIds, overrides }) => {\n\t\t\t\tnetwork.allocateAndSendIds(client, numIds, overrides);\n\t\t\t\treturn state;\n\t\t\t},\n\t\t\tchangeCapacity: (state, op) => {\n\t\t\t\tnetwork.enqueueCapacityChange(op.newSize);\n\t\t\t\treturn { ...state, clusterSize: op.newSize };\n\t\t\t},\n\t\t\tdeliverOperations: (state, op) => {\n\t\t\t\tnetwork.deliverOperations(op.client);\n\t\t\t\treturn state;\n\t\t\t},\n\t\t\tgenerateUnifyingIds: (state, { clientA, clientB, uuid }) => {\n\t\t\t\tnetwork.allocateAndSendIds(clientA, 1, { 0: uuid });\n\t\t\t\tnetwork.allocateAndSendIds(clientB, 1, { 0: uuid });\n\t\t\t\treturn state;\n\t\t\t},\n\t\t\treconnect: (state, { client }) => {\n\t\t\t\tnetwork.goOfflineThenResume(client);\n\t\t\t\treturn state;\n\t\t\t},\n\t\t\tvalidate: (state) => {\n\t\t\t\tnetwork.deliverOperations(DestinationClient.All);\n\t\t\t\tvalidator?.(network);\n\t\t\t\treturn state;\n\t\t\t},\n\t\t},\n\t\tinitialState,\n\t\tsaveInfo\n\t);\n\n\tif (synchronizeAtEnd) {\n\t\tnetwork.deliverOperations(DestinationClient.All);\n\t\tvalidator?.(network);\n\t}\n}\n\n/**\n * Converts the supplied integer to a uuid.\n */\nexport function integerToStableId(num: number | bigint): StableId {\n\tconst bigintNum = BigInt(num);\n\tconst upper = bigintNum >> BigInt(74);\n\tconst middle = (bigintNum & (BigInt(0xfff) << BigInt(62))) >> BigInt(62);\n\tconst lower = bigintNum & BigInt('0x3fffffffffffffff');\n\tconst upperString = padToLength(upper.toString(16), '0', 12);\n\tconst middleString = `4${padToLength(middle.toString(16), '0', 3)}`;\n\tconst lowerString = padToLength((BigInt('0x8000000000000000') | BigInt(lower)).toString(16), '0', 16);\n\tconst uuid = upperString + middleString + lowerString;\n\treturn assertIsStableId(\n\t\t`${uuid.substr(0, 8)}-${uuid.substr(8, 4)}-${uuid.substr(12, 4)}-${uuid.substr(16, 4)}-${uuid.substr(20)}`\n\t);\n}\n\n/**\n * Pads the strings to a length of 32 with zeroes.\n */\nexport function padToUuidLength(str: string): string {\n\treturn padToLength(str, '0', 32);\n}\n\nfunction padToLength(str: string, char: string, length: number): string {\n\treturn char.repeat(length - str.length) + str;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PendingLocalStateTests.d.ts","sourceRoot":"","sources":["../../../src/test/utilities/PendingLocalStateTests.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,OAAO,EAEN,sCAAsC,EACtC,mCAAmC,EAGnC,MAAM,iBAAiB,CAAC;AAezB;;;;GAIG;AACH,wBAAgB,yBAAyB,CACxC,KAAK,EAAE,MAAM,EACb,8BAA8B,EAAE,CAC/B,OAAO,EAAE,mCAAmC,KACxC,OAAO,CAAC,sCAAsC,CAAC,
|
|
1
|
+
{"version":3,"file":"PendingLocalStateTests.d.ts","sourceRoot":"","sources":["../../../src/test/utilities/PendingLocalStateTests.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,OAAO,EAEN,sCAAsC,EACtC,mCAAmC,EAGnC,MAAM,iBAAiB,CAAC;AAezB;;;;GAIG;AACH,wBAAgB,yBAAyB,CACxC,KAAK,EAAE,MAAM,EACb,8BAA8B,EAAE,CAC/B,OAAO,EAAE,mCAAmC,KACxC,OAAO,CAAC,sCAAsC,CAAC,QAgHpD"}
|
|
@@ -24,7 +24,8 @@ async function withContainerOffline(provider, container, action) {
|
|
|
24
24
|
export function runPendingLocalStateTests(title, setUpLocalServerTestSharedTree) {
|
|
25
25
|
describe(title, () => {
|
|
26
26
|
const documentId = 'documentId';
|
|
27
|
-
|
|
27
|
+
/* TODO: Enable when stashed ops are supported: WriteFormat.v0_1_1 */
|
|
28
|
+
[WriteFormat.v0_0_2].forEach((writeFormat) => {
|
|
28
29
|
it(`is applied to all connected containers (v${writeFormat})`, async () => {
|
|
29
30
|
var _a, _b, _c;
|
|
30
31
|
const { container, tree, testObjectProvider } = await setUpLocalServerTestSharedTree({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PendingLocalStateTests.js","sourceRoot":"","sources":["../../../src/test/utilities/PendingLocalStateTests.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 { expect } from 'chai';\nimport { IContainer } from '@fluidframework/container-definitions';\nimport { requestFluidObject } from '@fluidframework/runtime-utils';\nimport { ITestFluidObject, ITestObjectProvider } from '@fluidframework/test-utils';\nimport { fail } from '../../Common';\nimport { ChangeInternal, Edit, WriteFormat } from '../../persisted-types';\nimport type { EditLog } from '../../EditLog';\nimport { SharedTree } from '../../SharedTree';\nimport { Change, StablePlace } from '../../ChangeTypes';\nimport {\n\tgetEditLogInternal,\n\tLocalServerSharedTreeTestingComponents,\n\tLocalServerSharedTreeTestingOptions,\n\tsetUpTestTree,\n\tstabilizeEdit,\n} from './TestUtilities';\n\nasync function withContainerOffline<TReturn>(\n\tprovider: ITestObjectProvider,\n\tcontainer: IContainer,\n\taction: () => TReturn\n): Promise<{ actionReturn: TReturn; pendingLocalState: string }> {\n\tawait provider.ensureSynchronized();\n\tawait provider.opProcessingController.pauseProcessing(container);\n\tconst actionReturn = action();\n\tconst pendingLocalState = container.closeAndGetPendingLocalState();\n\tprovider.opProcessingController.resumeProcessing(container);\n\treturn { actionReturn, pendingLocalState };\n}\n\n/**\n * Runs a test suite for SharedTree's ability to apply pending local state stashed by the host.\n * See documentation on `applyStashedOp`.\n * This suite can be used to test other implementations that aim to fulfill `SharedTree`'s contract.\n */\nexport function runPendingLocalStateTests(\n\ttitle: string,\n\tsetUpLocalServerTestSharedTree: (\n\t\toptions: LocalServerSharedTreeTestingOptions\n\t) => Promise<LocalServerSharedTreeTestingComponents>\n) {\n\tdescribe(title, () => {\n\t\tconst documentId = 'documentId';\n\n\t\t[WriteFormat.v0_0_2 /* TODO: Enable when stashed ops are supported: WriteFormat.v0_1_1 */].forEach(\n\t\t\t(writeFormat) => {\n\t\t\t\tit(`is applied to all connected containers (v${writeFormat})`, async () => {\n\t\t\t\t\tconst { container, tree, testObjectProvider } = await setUpLocalServerTestSharedTree({\n\t\t\t\t\t\tid: documentId,\n\t\t\t\t\t\twriteFormat,\n\t\t\t\t\t});\n\t\t\t\t\tconst testTree = setUpTestTree(tree);\n\t\t\t\t\tconst { tree: tree2 } = await setUpLocalServerTestSharedTree({\n\t\t\t\t\t\tid: documentId,\n\t\t\t\t\t\ttestObjectProvider,\n\t\t\t\t\t\twriteFormat,\n\t\t\t\t\t});\n\t\t\t\t\tconst url = (await container.getAbsoluteUrl('/')) ?? fail('Container unable to resolve \"/\".');\n\t\t\t\t\tawait testObjectProvider.ensureSynchronized();\n\t\t\t\t\tconst initialEditLogLength = tree.edits.length;\n\n\t\t\t\t\tconst { pendingLocalState, actionReturn: edit } = await withContainerOffline(\n\t\t\t\t\t\ttestObjectProvider,\n\t\t\t\t\t\tcontainer,\n\t\t\t\t\t\t() =>\n\t\t\t\t\t\t\ttree.applyEdit(...Change.insertTree(testTree.buildLeaf(), StablePlace.after(testTree.left)))\n\t\t\t\t\t);\n\t\t\t\t\tawait testObjectProvider.ensureSynchronized();\n\t\t\t\t\tconst leftTraitAfterOfflineClose = tree2.currentView.getTrait(\n\t\t\t\t\t\ttestTree.left.traitLocation.translate(tree2)\n\t\t\t\t\t);\n\t\t\t\t\tconst loader = testObjectProvider.makeTestLoader();\n\n\t\t\t\t\t// Simulate reconnect of user 1; a new container will be created which passes the stashed local state in its\n\t\t\t\t\t// load request.\n\t\t\t\t\tconst container3 = await loader.resolve({ url }, pendingLocalState);\n\t\t\t\t\tconst dataObject3 = await requestFluidObject<ITestFluidObject>(container3, '/');\n\t\t\t\t\tconst tree3 = await dataObject3.getSharedObject<SharedTree>(documentId);\n\t\t\t\t\texpect((tree3.edits as unknown as EditLog<ChangeInternal>).isLocalEdit(edit.id)).to.be.true; // Kludge\n\n\t\t\t\t\tawait testObjectProvider.ensureSynchronized();\n\n\t\t\t\t\texpect(leftTraitAfterOfflineClose.length).to.equal(\n\t\t\t\t\t\t1,\n\t\t\t\t\t\t'Second tree should not receive edits made by first tree after it went offline.'\n\t\t\t\t\t);\n\t\t\t\t\texpect(tree3.currentView.getTrait(testTree.left.traitLocation.translate(tree3)).length).to.equal(\n\t\t\t\t\t\t2,\n\t\t\t\t\t\t'Tree which loaded with stashed pending edits should apply them.'\n\t\t\t\t\t);\n\t\t\t\t\texpect(tree2.currentView.getTrait(testTree.left.traitLocation.translate(tree2)).length).to.equal(\n\t\t\t\t\t\t2,\n\t\t\t\t\t\t'Tree collaborating with a client that applies stashed pending edits should see them.'\n\t\t\t\t\t);\n\n\t\t\t\t\tconst stableEdit = stabilizeEdit(tree, edit as unknown as Edit<ChangeInternal>);\n\t\t\t\t\texpect(\n\t\t\t\t\t\tstabilizeEdit(tree2, (await getEditLogInternal(tree2).tryGetEdit(edit.id)) ?? fail())\n\t\t\t\t\t).to.deep.equal(stableEdit);\n\t\t\t\t\texpect(\n\t\t\t\t\t\tstabilizeEdit(tree3, (await getEditLogInternal(tree3).tryGetEdit(edit.id)) ?? fail())\n\t\t\t\t\t).to.deep.equal(stableEdit);\n\t\t\t\t\texpect(tree2.edits.length).to.equal(initialEditLogLength + 1);\n\t\t\t\t\texpect(tree3.edits.length).to.equal(initialEditLogLength + 1);\n\t\t\t\t});\n\t\t\t}\n\t\t);\n\n\t\tit('Deals with stashed handle ops gracefully', async () => {\n\t\t\t// Setup\n\t\t\tconst { container, tree, testObjectProvider } = await setUpLocalServerTestSharedTree({\n\t\t\t\tid: documentId,\n\t\t\t\twriteFormat: WriteFormat.v0_1_1,\n\t\t\t});\n\t\t\tconst testTree = setUpTestTree(tree);\n\t\t\tawait setUpLocalServerTestSharedTree({\n\t\t\t\tid: documentId,\n\t\t\t\ttestObjectProvider,\n\t\t\t\twriteFormat: WriteFormat.v0_1_1,\n\t\t\t});\n\n\t\t\tconst url = (await container.getAbsoluteUrl('/')) ?? fail('Container unable to resolve \"/\".');\n\n\t\t\tawait testObjectProvider.ensureSynchronized();\n\t\t\tawait testObjectProvider.opProcessingController.pauseProcessing();\n\t\t\t// Generate enough edits to cause a chunk upload.\n\t\t\tfor (let i = 0; i < (tree.edits as EditLog).editsPerChunk; i++) {\n\t\t\t\ttree.applyEdit(\n\t\t\t\t\t...Change.insertTree(testTree.buildLeaf(), StablePlace.atEndOf(testTree.left.traitLocation))\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Process all of those messages, sequencing them but without informing the container that they have been sequenced.\n\t\t\tawait testObjectProvider.opProcessingController.processOutgoing(container);\n\t\t\t// Inform the container that all of the edits it generated above have been sequenced, thereby filling an edit chunk\n\t\t\t// whose responsibility to upload is on the container.\n\t\t\tawait testObjectProvider.opProcessingController.processIncoming(container);\n\t\t\t// Process outgoing/incoming once more to handle the blob attach op.\n\t\t\tawait testObjectProvider.opProcessingController.processOutgoing(container);\n\t\t\tawait testObjectProvider.opProcessingController.processIncoming(container);\n\n\t\t\tconst pendingLocalState = container.closeAndGetPendingLocalState();\n\t\t\tconst loader = testObjectProvider.makeTestLoader();\n\n\t\t\tconst container2 = await loader.resolve({ url }, pendingLocalState);\n\t\t\tconst dataObject2 = await requestFluidObject<ITestFluidObject>(container2, '/');\n\t\t\tconst tree2 = await dataObject2.getSharedObject<SharedTree>(documentId);\n\t\t\tawait testObjectProvider.ensureSynchronized();\n\n\t\t\tconst editLog = tree2.edits as EditLog;\n\t\t\tconst unuploadedEditChunks = Array.from(editLog.getEditChunksReadyForUpload());\n\t\t\texpect(unuploadedEditChunks.length).to.equal(0);\n\t\t\texpect(editLog.getEditLogSummary().editChunks.length).to.equal(2);\n\t\t});\n\t});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"PendingLocalStateTests.js","sourceRoot":"","sources":["../../../src/test/utilities/PendingLocalStateTests.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 { expect } from 'chai';\nimport { IContainer } from '@fluidframework/container-definitions';\nimport { requestFluidObject } from '@fluidframework/runtime-utils';\nimport { ITestFluidObject, ITestObjectProvider } from '@fluidframework/test-utils';\nimport { fail } from '../../Common';\nimport { ChangeInternal, Edit, WriteFormat } from '../../persisted-types';\nimport type { EditLog } from '../../EditLog';\nimport { SharedTree } from '../../SharedTree';\nimport { Change, StablePlace } from '../../ChangeTypes';\nimport {\n\tgetEditLogInternal,\n\tLocalServerSharedTreeTestingComponents,\n\tLocalServerSharedTreeTestingOptions,\n\tsetUpTestTree,\n\tstabilizeEdit,\n} from './TestUtilities';\n\nasync function withContainerOffline<TReturn>(\n\tprovider: ITestObjectProvider,\n\tcontainer: IContainer,\n\taction: () => TReturn\n): Promise<{ actionReturn: TReturn; pendingLocalState: string }> {\n\tawait provider.ensureSynchronized();\n\tawait provider.opProcessingController.pauseProcessing(container);\n\tconst actionReturn = action();\n\tconst pendingLocalState = container.closeAndGetPendingLocalState();\n\tprovider.opProcessingController.resumeProcessing(container);\n\treturn { actionReturn, pendingLocalState };\n}\n\n/**\n * Runs a test suite for SharedTree's ability to apply pending local state stashed by the host.\n * See documentation on `applyStashedOp`.\n * This suite can be used to test other implementations that aim to fulfill `SharedTree`'s contract.\n */\nexport function runPendingLocalStateTests(\n\ttitle: string,\n\tsetUpLocalServerTestSharedTree: (\n\t\toptions: LocalServerSharedTreeTestingOptions\n\t) => Promise<LocalServerSharedTreeTestingComponents>\n) {\n\tdescribe(title, () => {\n\t\tconst documentId = 'documentId';\n\t\t/* TODO: Enable when stashed ops are supported: WriteFormat.v0_1_1 */\n\t\t[WriteFormat.v0_0_2].forEach((writeFormat) => {\n\t\t\tit(`is applied to all connected containers (v${writeFormat})`, async () => {\n\t\t\t\tconst { container, tree, testObjectProvider } = await setUpLocalServerTestSharedTree({\n\t\t\t\t\tid: documentId,\n\t\t\t\t\twriteFormat,\n\t\t\t\t});\n\t\t\t\tconst testTree = setUpTestTree(tree);\n\t\t\t\tconst { tree: tree2 } = await setUpLocalServerTestSharedTree({\n\t\t\t\t\tid: documentId,\n\t\t\t\t\ttestObjectProvider,\n\t\t\t\t\twriteFormat,\n\t\t\t\t});\n\t\t\t\tconst url = (await container.getAbsoluteUrl('/')) ?? fail('Container unable to resolve \"/\".');\n\t\t\t\tawait testObjectProvider.ensureSynchronized();\n\t\t\t\tconst initialEditLogLength = tree.edits.length;\n\n\t\t\t\tconst { pendingLocalState, actionReturn: edit } = await withContainerOffline(\n\t\t\t\t\ttestObjectProvider,\n\t\t\t\t\tcontainer,\n\t\t\t\t\t() => tree.applyEdit(...Change.insertTree(testTree.buildLeaf(), StablePlace.after(testTree.left)))\n\t\t\t\t);\n\t\t\t\tawait testObjectProvider.ensureSynchronized();\n\t\t\t\tconst leftTraitAfterOfflineClose = tree2.currentView.getTrait(\n\t\t\t\t\ttestTree.left.traitLocation.translate(tree2)\n\t\t\t\t);\n\t\t\t\tconst loader = testObjectProvider.makeTestLoader();\n\n\t\t\t\t// Simulate reconnect of user 1; a new container will be created which passes the stashed local state in its\n\t\t\t\t// load request.\n\t\t\t\tconst container3 = await loader.resolve({ url }, pendingLocalState);\n\t\t\t\tconst dataObject3 = await requestFluidObject<ITestFluidObject>(container3, '/');\n\t\t\t\tconst tree3 = await dataObject3.getSharedObject<SharedTree>(documentId);\n\t\t\t\texpect((tree3.edits as unknown as EditLog<ChangeInternal>).isLocalEdit(edit.id)).to.be.true; // Kludge\n\n\t\t\t\tawait testObjectProvider.ensureSynchronized();\n\n\t\t\t\texpect(leftTraitAfterOfflineClose.length).to.equal(\n\t\t\t\t\t1,\n\t\t\t\t\t'Second tree should not receive edits made by first tree after it went offline.'\n\t\t\t\t);\n\t\t\t\texpect(tree3.currentView.getTrait(testTree.left.traitLocation.translate(tree3)).length).to.equal(\n\t\t\t\t\t2,\n\t\t\t\t\t'Tree which loaded with stashed pending edits should apply them.'\n\t\t\t\t);\n\t\t\t\texpect(tree2.currentView.getTrait(testTree.left.traitLocation.translate(tree2)).length).to.equal(\n\t\t\t\t\t2,\n\t\t\t\t\t'Tree collaborating with a client that applies stashed pending edits should see them.'\n\t\t\t\t);\n\n\t\t\t\tconst stableEdit = stabilizeEdit(tree, edit as unknown as Edit<ChangeInternal>);\n\t\t\t\texpect(\n\t\t\t\t\tstabilizeEdit(tree2, (await getEditLogInternal(tree2).tryGetEdit(edit.id)) ?? fail())\n\t\t\t\t).to.deep.equal(stableEdit);\n\t\t\t\texpect(\n\t\t\t\t\tstabilizeEdit(tree3, (await getEditLogInternal(tree3).tryGetEdit(edit.id)) ?? fail())\n\t\t\t\t).to.deep.equal(stableEdit);\n\t\t\t\texpect(tree2.edits.length).to.equal(initialEditLogLength + 1);\n\t\t\t\texpect(tree3.edits.length).to.equal(initialEditLogLength + 1);\n\t\t\t});\n\t\t});\n\n\t\tit('Deals with stashed handle ops gracefully', async () => {\n\t\t\t// Setup\n\t\t\tconst { container, tree, testObjectProvider } = await setUpLocalServerTestSharedTree({\n\t\t\t\tid: documentId,\n\t\t\t\twriteFormat: WriteFormat.v0_1_1,\n\t\t\t});\n\t\t\tconst testTree = setUpTestTree(tree);\n\t\t\tawait setUpLocalServerTestSharedTree({\n\t\t\t\tid: documentId,\n\t\t\t\ttestObjectProvider,\n\t\t\t\twriteFormat: WriteFormat.v0_1_1,\n\t\t\t});\n\n\t\t\tconst url = (await container.getAbsoluteUrl('/')) ?? fail('Container unable to resolve \"/\".');\n\n\t\t\tawait testObjectProvider.ensureSynchronized();\n\t\t\tawait testObjectProvider.opProcessingController.pauseProcessing();\n\t\t\t// Generate enough edits to cause a chunk upload.\n\t\t\tfor (let i = 0; i < (tree.edits as EditLog).editsPerChunk; i++) {\n\t\t\t\ttree.applyEdit(\n\t\t\t\t\t...Change.insertTree(testTree.buildLeaf(), StablePlace.atEndOf(testTree.left.traitLocation))\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Process all of those messages, sequencing them but without informing the container that they have been sequenced.\n\t\t\tawait testObjectProvider.opProcessingController.processOutgoing(container);\n\t\t\t// Inform the container that all of the edits it generated above have been sequenced, thereby filling an edit chunk\n\t\t\t// whose responsibility to upload is on the container.\n\t\t\tawait testObjectProvider.opProcessingController.processIncoming(container);\n\t\t\t// Process outgoing/incoming once more to handle the blob attach op.\n\t\t\tawait testObjectProvider.opProcessingController.processOutgoing(container);\n\t\t\tawait testObjectProvider.opProcessingController.processIncoming(container);\n\n\t\t\tconst pendingLocalState = container.closeAndGetPendingLocalState();\n\t\t\tconst loader = testObjectProvider.makeTestLoader();\n\n\t\t\tconst container2 = await loader.resolve({ url }, pendingLocalState);\n\t\t\tconst dataObject2 = await requestFluidObject<ITestFluidObject>(container2, '/');\n\t\t\tconst tree2 = await dataObject2.getSharedObject<SharedTree>(documentId);\n\t\t\tawait testObjectProvider.ensureSynchronized();\n\n\t\t\tconst editLog = tree2.edits as EditLog;\n\t\t\tconst unuploadedEditChunks = Array.from(editLog.getEditChunksReadyForUpload());\n\t\t\texpect(unuploadedEditChunks.length).to.equal(0);\n\t\t\texpect(editLog.getEditLogSummary().editChunks.length).to.equal(2);\n\t\t});\n\t});\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SharedTreeTests.d.ts","sourceRoot":"","sources":["../../../src/test/utilities/SharedTreeTests.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAuBH,OAAO,EAeN,WAAW,EACX,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"SharedTreeTests.d.ts","sourceRoot":"","sources":["../../../src/test/utilities/SharedTreeTests.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAuBH,OAAO,EAeN,WAAW,EACX,MAAM,uBAAuB,CAAC;AAa/B,OAAO,EAGN,2BAA2B,EAC3B,wBAAwB,EAcxB,MAAM,iBAAiB,CAAC;AAazB;;;GAGG;AACH,wBAAgB,4BAA4B,CAC3C,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,WAAW,EACxB,qCAAqC,EAAE,CAAC,OAAO,CAAC,EAAE,wBAAwB,KAAK,2BAA2B,QA49C1G"}
|