Skip to content

Commit

Permalink
feat(sequence): add more tests (microsoft#19398)
Browse files Browse the repository at this point in the history
Adds some more tests to sequence. Specifically some interval-related
tests from another PR that got killed, and skipped tests related to
grouped batching and reconnect, which currently has a number of crashes.
  • Loading branch information
connorskees authored Feb 2, 2024
1 parent b6c8421 commit 1d2c739
Show file tree
Hide file tree
Showing 11 changed files with 283 additions and 64 deletions.
10 changes: 5 additions & 5 deletions packages/dds/merge-tree/src/partialLengths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1185,7 +1185,7 @@ export class PartialSequenceLengths {
}

/* eslint-disable @typescript-eslint/dot-notation */
function verifyPartialLengths(
function verifyPartialLengthsInner(
partialSeqLengths: PartialSequenceLengths,
partialLengths: PartialSequenceLengthsSet,
clientPartials: boolean,
Expand Down Expand Up @@ -1266,7 +1266,7 @@ function verifyPartialLengths(
return count;
}

export function verifyExpected(
export function verifyExpectedPartialLengths(
mergeTree: MergeTree,
node: IMergeBlock,
refSeq: number,
Expand Down Expand Up @@ -1305,11 +1305,11 @@ export function verifyExpected(
}
}

export function verify(partialSeqLengths: PartialSequenceLengths) {
export function verifyPartialLengths(partialSeqLengths: PartialSequenceLengths) {
if (partialSeqLengths["clientSeqNumbers"]) {
for (const cliSeq of partialSeqLengths["clientSeqNumbers"]) {
if (cliSeq) {
verifyPartialLengths(partialSeqLengths, cliSeq, true);
verifyPartialLengthsInner(partialSeqLengths, cliSeq, true);
}
}

Expand All @@ -1319,7 +1319,7 @@ export function verify(partialSeqLengths: PartialSequenceLengths) {
0x059 /* "Client view exists but flat view does not!" */,
);

verifyPartialLengths(partialSeqLengths, partialSeqLengths["partialLengths"], false);
verifyPartialLengthsInner(partialSeqLengths, partialSeqLengths["partialLengths"], false);
} else {
// If we don't have a client view, we shouldn't have the flat view either
assert(
Expand Down
1 change: 1 addition & 0 deletions packages/dds/merge-tree/src/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export {
markRangeRemoved,
nodeOrdinalsHaveIntegrity,
validatePartialLengths,
useStrictPartialLengthChecks,
} from "./testUtils";
export {
annotateRange,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

import { strict as assert } from "assert";
import { LoggingError } from "@fluidframework/telemetry-utils";
import { PartialSequenceLengths, verify, verifyExpected } from "../partialLengths";
import { MergeTree } from "../mergeTree";
import { ReconnectTestHelper } from "./reconnectHelper";
import { useStrictPartialLengthChecks } from "./testUtils";

/**
* Some tests contain ASCII diagrams of the trees to make it easier to reason about
Expand Down Expand Up @@ -41,15 +41,13 @@ import { ReconnectTestHelper } from "./reconnectHelper";

for (const incremental of [true, false]) {
describe(`obliterate partial lengths incremental = ${incremental}`, () => {
useStrictPartialLengthChecks();

beforeEach(() => {
PartialSequenceLengths.options.verifier = verify;
PartialSequenceLengths.options.verifyExpected = verifyExpected;
MergeTree.options.incrementalUpdate = incremental;
});

afterEach(() => {
PartialSequenceLengths.options.verifier = undefined;
PartialSequenceLengths.options.verifyExpected = undefined;
MergeTree.options.incrementalUpdate = true;
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,18 @@

import { strict as assert } from "assert";
import { MergeTreeDeltaType } from "../ops";
import { PartialSequenceLengths, verify, verifyExpected } from "../partialLengths";
import { TestClient } from "./testClient";
import { insertText, validatePartialLengths } from "./testUtils";
import { insertText, useStrictPartialLengthChecks, validatePartialLengths } from "./testUtils";

describe("obliterate partial lengths", () => {
let client: TestClient;
let refSeq: number;
const localClientId = 17;
const remoteClientId = 18;

useStrictPartialLengthChecks();

beforeEach(() => {
PartialSequenceLengths.options.verifier = verify;
PartialSequenceLengths.options.verifyExpected = verifyExpected;
client = new TestClient({
mergeTreeEnableObliterate: true,
});
Expand All @@ -34,10 +33,6 @@ describe("obliterate partial lengths", () => {
refSeq = client.getCurrentSeq();
});

afterEach(() => {
PartialSequenceLengths.options.verifier = undefined;
});

it("removes text", () => {
assert.equal(client.getText(), "hello world");
const localObliterateOp = client.obliterateRangeLocal(0, "hello world".length);
Expand Down
8 changes: 3 additions & 5 deletions packages/dds/merge-tree/src/test/obliterate.reconnect.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,19 @@
*/

import { strict as assert } from "assert";
import { PartialSequenceLengths, verify, verifyExpected } from "../partialLengths";
import { MergeTree } from "../mergeTree";
import { ReconnectTestHelper } from "./reconnectHelper";
import { useStrictPartialLengthChecks } from "./testUtils";

for (const incremental of [true, false]) {
describe(`obliterate partial lengths incremental = ${incremental}`, () => {
useStrictPartialLengthChecks();

beforeEach(() => {
PartialSequenceLengths.options.verifier = verify;
PartialSequenceLengths.options.verifyExpected = verifyExpected;
MergeTree.options.incrementalUpdate = incremental;
});

afterEach(() => {
PartialSequenceLengths.options.verifier = undefined;
PartialSequenceLengths.options.verifyExpected = undefined;
MergeTree.options.incrementalUpdate = true;
});

Expand Down
18 changes: 9 additions & 9 deletions packages/dds/merge-tree/src/test/partialLength.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,24 @@
import { UnassignedSequenceNumber } from "../constants";
import { MergeTree } from "../mergeTree";
import { MergeTreeDeltaType } from "../ops";
import { PartialSequenceLengths, verify, verifyExpected } from "../partialLengths";
import { TextSegment } from "../textSegment";
import { insertSegments, insertText, markRangeRemoved, validatePartialLengths } from "./testUtils";
import {
insertSegments,
insertText,
markRangeRemoved,
useStrictPartialLengthChecks,
validatePartialLengths,
} from "./testUtils";

describe("partial lengths", () => {
let mergeTree: MergeTree;
const localClientId = 17;
const remoteClientId = 18;
const refSeq = 0;

useStrictPartialLengthChecks();

beforeEach(() => {
PartialSequenceLengths.options.verifier = verify;
PartialSequenceLengths.options.verifyExpected = verifyExpected;
mergeTree = new MergeTree();
insertSegments({
mergeTree,
Expand All @@ -33,11 +38,6 @@ describe("partial lengths", () => {
mergeTree.startCollaboration(localClientId, /* minSeq: */ 0, /* currentSeq: */ 0);
});

afterEach(() => {
PartialSequenceLengths.options.verifier = undefined;
PartialSequenceLengths.options.verifyExpected = undefined;
});

it("passes with no additional ops", () => {
validatePartialLengths(localClientId, mergeTree, [{ seq: refSeq, len: 12 }]);
});
Expand Down
24 changes: 24 additions & 0 deletions packages/dds/merge-tree/src/test/testUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ import { MergeTree } from "../mergeTree";
import { walkAllChildSegments } from "../mergeTreeNodeWalk";
import { UnassignedSequenceNumber } from "../constants";
import { LocalReferenceCollection } from "../localReference";
import {
PartialSequenceLengths,
verifyExpectedPartialLengths,
verifyPartialLengths,
} from "../partialLengths";
import { loadText } from "./text";

export function loadTextFromFile(filename: string, mergeTree: MergeTree, segLimit = 0) {
Expand Down Expand Up @@ -267,3 +272,22 @@ export function validateRefCount(collection?: LocalReferenceCollection) {
// eslint-disable-next-line @typescript-eslint/dot-notation
assert.equal(collection["refCount"], expectedLength);
}

/**
* Enable stricter partial length assertions inside tests
*
* Note that these assertions can be expensive, and so should not be enabled in
* production code or tests that run through thousands of ops (e.g. the SharedString
* fuzz tests).
*/
export function useStrictPartialLengthChecks() {
beforeEach(() => {
PartialSequenceLengths.options.verifier = verifyPartialLengths;
PartialSequenceLengths.options.verifyExpected = verifyExpectedPartialLengths;
});

afterEach(() => {
PartialSequenceLengths.options.verifier = undefined;
PartialSequenceLengths.options.verifyExpected = undefined;
});
}
11 changes: 6 additions & 5 deletions packages/dds/sequence/src/sequence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -594,11 +594,12 @@ export abstract class SharedSegmentSequence<T extends ISegment>
const insertIndex: number = Math.max(start, end);

// Insert first, so local references can slide to the inserted seg if any
const insert = this.client.insertSegmentLocal(insertIndex, segment);
if (insert) {
if (start < end) {
this.client.removeRangeLocal(start, end);
}
const insert = this.guardReentrancy(() =>
this.client.insertSegmentLocal(insertIndex, segment),
);

if (insert && start < end) {
this.removeRange(start, end);
}
}

Expand Down
3 changes: 3 additions & 0 deletions packages/dds/sequence/src/sequenceFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ export class SharedStringFactory implements IChannelFactory {
return sharedString;
}

/**
* {@inheritDoc @fluidframework/datastore-definitions#IChannelFactory.create}
*/
public create(document: IFluidDataStoreRuntime, id: string): SharedString {
const sharedString = new SharedString(document, id, this.attributes);
sharedString.initializeLocal();
Expand Down
28 changes: 28 additions & 0 deletions packages/dds/sequence/src/test/fuzz/sharedString.fuzz.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,31 @@ describe("SharedString fuzz testing with rebased batches", () => {
// replay: 0,
});
});

// todo: potentially related to AB#7050
//
// `intervalRebasing.spec.ts` contains some reduced tests exhibiting the crashes
// linked to AB#7050
describe.skip("SharedString fuzz testing with rebased batches and reconnect", () => {
const noReconnectWithRebaseModel = {
...baseSharedStringModel,
workloadName: "SharedString with rebasing and reconnect",
};

createDDSFuzzSuite(noReconnectWithRebaseModel, {
...defaultFuzzOptions,
reconnectProbability: 0.3,
numberOfClients: 3,
clientJoinOptions: {
maxNumberOfClients: 3,
clientAddProbability: 0.0,
},
rebaseProbability: 0.2,
containerRuntimeOptions: {
flushMode: FlushMode.TurnBased,
enableGroupedBatching: true,
},
// Uncomment this line to replay a specific seed from its failure file:
// replay: 0,
});
});
Loading

0 comments on commit 1d2c739

Please sign in to comment.