Skip to content

Commit

Permalink
Reapply microsoft#15917
Browse files Browse the repository at this point in the history
  • Loading branch information
Abe27342 committed Jun 21, 2023
1 parent 12179d0 commit cfea1b1
Show file tree
Hide file tree
Showing 19 changed files with 394 additions and 221 deletions.
2 changes: 1 addition & 1 deletion examples/data-objects/table-document/src/test/.mocharc.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@

const packageDir = `${__dirname}/../..`;

const getFluidTestMochaConfig = require("@fluid-internal/test-version-utils/mocharc-common.js");
const getFluidTestMochaConfig = require("@fluid-internal/test-version-utils/mocharc-common.cjs");
const config = getFluidTestMochaConfig(packageDir);
module.exports = config;
2 changes: 1 addition & 1 deletion examples/data-objects/webflow/src/test/.mocharc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@

const packageDir = `${__dirname}/../..`;

const getFluidTestMochaConfig = require("@fluid-internal/test-version-utils/mocharc-common.js");
const getFluidTestMochaConfig = require("@fluid-internal/test-version-utils/mocharc-common.cjs");
const config = getFluidTestMochaConfig(packageDir);
module.exports = config;
2 changes: 1 addition & 1 deletion packages/test/test-end-to-end-tests/src/test/.mocharc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
"use strict";

const packageDir = `${__dirname}/../..`;
const getFluidTestMochaConfig = require("@fluid-internal/test-version-utils/mocharc-common.js");
const getFluidTestMochaConfig = require("@fluid-internal/test-version-utils/mocharc-common.cjs");
const config = getFluidTestMochaConfig(packageDir, ["source-map-support/register"]);
module.exports = config;
Original file line number Diff line number Diff line change
Expand Up @@ -4,75 +4,73 @@
*/

import { strict as assert } from "assert";

import {
ContainerRuntimeFactoryWithDefaultDataStore,
DataObject,
DataObjectFactory,
} from "@fluidframework/aqueduct";
import { IContainer } from "@fluidframework/container-definitions";
import { ContainerRuntime, IContainerRuntimeOptions } from "@fluidframework/container-runtime";
import { IFluidHandle } from "@fluidframework/core-interfaces";
import { SharedMatrix } from "@fluidframework/matrix";
import type { SharedMatrix } from "@fluidframework/matrix";
import { Marker, ReferenceType, reservedMarkerIdKey } from "@fluidframework/merge-tree";
import { requestFluidObject } from "@fluidframework/runtime-utils";
import { ISummaryTree, SummaryType } from "@fluidframework/protocol-definitions";
import { SharedString } from "@fluidframework/sequence";
import type { SharedString } from "@fluidframework/sequence";
import { TelemetryNullLogger } from "@fluidframework/telemetry-utils";
import { ITestObjectProvider, waitForContainerConnection } from "@fluidframework/test-utils";
import { describeFullCompat } from "@fluid-internal/test-version-utils";
import { UndoRedoStackManager } from "@fluidframework/undo-redo";

class TestDataObject extends DataObject {
public get _root() {
return this.root;
}
/**
* Validates this scenario: When all references to a data store are deleted, the data store is marked as unreferenced
* in the next summary. When a reference to the data store is re-added, it is marked as referenced in the next summary.
* Basically, if the handle to a data store is not stored in any DDS, its summary tree will have the "unreferenced"
* property set to true. If the handle to a data store exists or it's a root data store, its summary tree does not have
* the "unreferenced" property.
*/
describeFullCompat("GC reference updates in local summary", (getTestObjectProvider, apis) => {
const { SharedMatrix, SharedString } = apis.dds;

public get _context() {
return this.context;
}
class TestDataObject extends apis.dataRuntime.DataObject {
public get _root() {
return this.root;
}

public get _context() {
return this.context;
}

private readonly matrixKey = "matrix";
public matrix!: SharedMatrix;
public undoRedoStackManager!: UndoRedoStackManager;
private readonly matrixKey = "matrix";
public matrix!: SharedMatrix;
public undoRedoStackManager!: UndoRedoStackManager;

private readonly sharedStringKey = "sharedString";
public sharedString!: SharedString;
private readonly sharedStringKey = "sharedString";
public sharedString!: SharedString;

protected async initializingFirstTime() {
const sharedMatrix = SharedMatrix.create(this.runtime);
this.root.set(this.matrixKey, sharedMatrix.handle);
protected async initializingFirstTime() {
const sharedMatrix = SharedMatrix.create(this.runtime);
this.root.set(this.matrixKey, sharedMatrix.handle);

const sharedString = SharedString.create(this.runtime);
this.root.set(this.sharedStringKey, sharedString.handle);
}
const sharedString = SharedString.create(this.runtime);
this.root.set(this.sharedStringKey, sharedString.handle);
}

protected async hasInitialized() {
const matrixHandle = this.root.get<IFluidHandle<SharedMatrix>>(this.matrixKey);
assert(matrixHandle !== undefined, "SharedMatrix not found");
this.matrix = await matrixHandle.get();
protected async hasInitialized() {
const matrixHandle = this.root.get<IFluidHandle<SharedMatrix>>(this.matrixKey);
assert(matrixHandle !== undefined, "SharedMatrix not found");
this.matrix = await matrixHandle.get();

this.undoRedoStackManager = new UndoRedoStackManager();
this.matrix.insertRows(0, 3);
this.matrix.insertCols(0, 3);
this.matrix.openUndo(this.undoRedoStackManager);
this.undoRedoStackManager = new UndoRedoStackManager();
this.matrix.insertRows(0, 3);
this.matrix.insertCols(0, 3);
this.matrix.openUndo(this.undoRedoStackManager);

const sharedStringHandle = this.root.get<IFluidHandle<SharedString>>(this.sharedStringKey);
assert(sharedStringHandle !== undefined, "SharedMatrix not found");
this.sharedString = await sharedStringHandle.get();
const sharedStringHandle = this.root.get<IFluidHandle<SharedString>>(
this.sharedStringKey,
);
assert(sharedStringHandle !== undefined, "SharedMatrix not found");
this.sharedString = await sharedStringHandle.get();
}
}
}

/**
* Validates this scenario: When all references to a data store are deleted, the data store is marked as unreferenced
* in the next summary. When a reference to the data store is re-added, it is marked as referenced in the next summary.
* Basically, if the handle to a data store is not stored in any DDS, its summary tree will have the "unreferenced"
* property set to true. If the handle to a data store exists or it's a root data store, its summary tree does not have
* the "unreferenced" property.
*/
describeFullCompat("GC reference updates in local summary", (getTestObjectProvider) => {
let provider: ITestObjectProvider;
const factory = new DataObjectFactory(
const factory = new apis.dataRuntime.DataObjectFactory(
"TestDataObject",
TestDataObject,
[SharedMatrix.getFactory(), SharedString.getFactory()],
Expand All @@ -87,7 +85,7 @@ describeFullCompat("GC reference updates in local summary", (getTestObjectProvid
},
gcOptions: { gcAllowed: true },
};
const runtimeFactory = new ContainerRuntimeFactoryWithDefaultDataStore(
const runtimeFactory = new apis.containerRuntime.ContainerRuntimeFactoryWithDefaultDataStore(
factory,
[[factory.type, Promise.resolve(factory)]],
undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ module.exports = {
rules: {
"@typescript-eslint/strict-boolean-expressions": "off", // requires strictNullChecks=true in tsconfig
"import/no-nodejs-modules": "off",
// ESLint's resolver doesn't resolve relative imports of ESNext modules correctly, since
// it resolves the path relative to the .ts file (and assumes a file with a .js extension
// should exist there)
// AB#4614 tracks moving to eslint-import-resolver-typescript (which handles such imports
// out of the box) and removing this exception.
"import/no-unresolved": ["error", { ignore: ["^\\.(.*)\\.js$"] }],
},
parserOptions: {
project: ["./tsconfig.json", "./src/test/tsconfig.json"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
*/

"use strict";

const options = require("./dist/compatOptions");
const options = import("./dist/compatOptions.js");
const getFluidTestMochaConfig = require("@fluidframework/mocha-test-setup/mocharc-common.js");

function getFluidTestVariant() {
Expand All @@ -32,11 +31,7 @@ function getFluidTestMochaConfigWithCompat(packageDir, additionalRequiredModules
testReportPrefix += `_${options.compatKind.join("_")}`;
}

return getFluidTestMochaConfig(
packageDir,
["@fluid-internal/test-version-utils", ...additionalRequiredModules],
testReportPrefix,
);
return getFluidTestMochaConfig(packageDir, additionalRequiredModules, testReportPrefix);
}

module.exports = getFluidTestMochaConfigWithCompat;
1 change: 1 addition & 0 deletions packages/test/test-version-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"license": "MIT",
"author": "Microsoft and contributors",
"sideEffects": false,
"type": "module",
"main": "dist/index.js",
"module": "lib/index.js",
"types": "dist/index.d.ts",
Expand Down
40 changes: 36 additions & 4 deletions packages/test/test-version-utils/src/compatConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
* Licensed under the MIT License.
*/
import { Lazy, assert } from "@fluidframework/common-utils";
import { ensurePackageInstalled } from "./testApi";
import { pkgVersion } from "./packageVersion";
import { ensurePackageInstalled } from "./testApi.js";
import { pkgVersion } from "./packageVersion.js";
import {
CompatKind,
compatKind,
Expand All @@ -14,7 +14,7 @@ import {
tenantIndex,
baseVersion,
reinstall,
} from "./compatOptions";
} from "./compatOptions.js";

/*
* Generate configuration combinations for a particular compat version
Expand Down Expand Up @@ -229,8 +229,40 @@ export const configList = new Lazy<readonly CompatConfig[]>(() => {
return _configList;
});

/*
/**
* Mocha start up to ensure legacy versions are installed
* @privateRemarks
* This isn't currently used in a global setup hook due to https://github.com/mochajs/mocha/issues/4508.
* Instead, we ensure that all requested compatibility versions are loaded at `describeCompat` module import time by
* leveraging top-level await.
*
* This makes compatibility layer APIs (e.g. DDSes, data object, etc.) available at mocha suite creation time rather than
* hook/test execution time, which is convenient for test authors: this sort of code can be used
* ```ts
* describeCompat("my suite", (getTestObjectProvider, apis) => {
* class MyDataObject extends apis.dataRuntime.DataObject {
* // ...
* }
* });
* ```
*
* instead of code like this:
*
* ```ts
* describeCompat("my suite", (getTestObjectProvider, getApis) => {
*
* const makeDataObjectClass = (apis: CompatApis) => class MyDataObject extends apis.dataRuntime.DataObject {
* // ...
* }
*
* before(() => {
* // `getApis` can only be invoked from inside a hook or test
* const MyDataObject = makeDataObjectClass(getApis())
* });
* });
* ```
*
* If the linked github issue is ever fixed, this can be once again used as a global setup fixture.
*/
export async function mochaGlobalSetup() {
const versions = new Set(configList.value.map((value) => value.compatVersion));
Expand Down
4 changes: 2 additions & 2 deletions packages/test/test-version-utils/src/compatOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

import nconf from "nconf";
import { RouterliciousEndpoint, TestDriverTypes } from "@fluidframework/test-driver-definitions";
import { resolveVersion } from "./versionUtils";
import { pkgVersion } from "./packageVersion";
import { resolveVersion } from "./versionUtils.js";
import { pkgVersion } from "./packageVersion.js";

/**
* Different kind of compat version config
Expand Down
69 changes: 41 additions & 28 deletions packages/test/test-version-utils/src/compatUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,14 @@ import {
} from "@fluidframework/test-utils";
import { TestDriverTypes } from "@fluidframework/test-driver-definitions";
import { mixinAttributor } from "@fluid-experimental/attributor";
import { pkgVersion } from "./packageVersion";
import { getLoaderApi, getContainerRuntimeApi, getDataRuntimeApi, getDriverApi } from "./testApi";
import { pkgVersion } from "./packageVersion.js";
import {
getLoaderApi,
getContainerRuntimeApi,
getDataRuntimeApi,
getDriverApi,
CompatApis,
} from "./testApi.js";

export const TestDataObjectType = "@fluid-example/test-dataStore";

Expand Down Expand Up @@ -90,43 +96,28 @@ function createGetDataStoreFactoryFunction(api: ReturnType<typeof getDataRuntime
// Only support current version, not baseVersion support
export const getDataStoreFactory = createGetDataStoreFactoryFunction(getDataRuntimeApi(pkgVersion));

async function createVersionedFluidTestDriver(
baseVersion: string,
export async function getVersionedTestObjectProviderFromApis(
apis: Omit<CompatApis, "dds">,
driverConfig?: {
type?: TestDriverTypes;
config?: FluidTestDriverConfig;
version?: number | string;
},
) {
const driverApi = getDriverApi(baseVersion, driverConfig?.version);
return createFluidTestDriver(driverConfig?.type ?? "local", driverConfig?.config, driverApi);
}

export async function getVersionedTestObjectProvider(
baseVersion: string,
loaderVersion?: number | string,
driverConfig?: {
type?: TestDriverTypes;
config?: FluidTestDriverConfig;
version?: number | string;
},
runtimeVersion?: number | string,
dataRuntimeVersion?: number | string,
): Promise<TestObjectProvider> {
const loaderApi = getLoaderApi(baseVersion, loaderVersion);
const containerRuntimeApi = getContainerRuntimeApi(baseVersion, runtimeVersion);
const dataRuntimeApi = getDataRuntimeApi(baseVersion, dataRuntimeVersion);
const driver = await createVersionedFluidTestDriver(baseVersion, driverConfig);
const driver = await createFluidTestDriver(
driverConfig?.type ?? "local",
driverConfig?.config,
apis.driver,
);
const innerRequestHandler = async (request: IRequest, runtime: IContainerRuntimeBase) =>
runtime.IFluidHandleContext.resolveHandle(request);

const getDataStoreFactoryFn = createGetDataStoreFactoryFunction(dataRuntimeApi);
const getDataStoreFactoryFn = createGetDataStoreFactoryFunction(apis.dataRuntime);
const containerFactoryFn = (containerOptions?: ITestContainerConfig) => {
const dataStoreFactory = getDataStoreFactoryFn(containerOptions);
const runtimeCtor =
containerOptions?.enableAttribution === true
? mixinAttributor(containerRuntimeApi.ContainerRuntime)
: containerRuntimeApi.ContainerRuntime;
? mixinAttributor(apis.containerRuntime.ContainerRuntime)
: apis.containerRuntime.ContainerRuntime;
const factoryCtor = createTestContainerRuntimeFactory(runtimeCtor);
return new factoryCtor(
TestDataObjectType,
Expand All @@ -136,5 +127,27 @@ export async function getVersionedTestObjectProvider(
);
};

return new TestObjectProvider(loaderApi.Loader, driver, containerFactoryFn);
return new TestObjectProvider(apis.loader.Loader, driver, containerFactoryFn);
}

export async function getVersionedTestObjectProvider(
baseVersion: string,
loaderVersion?: number | string,
driverConfig?: {
type?: TestDriverTypes;
config?: FluidTestDriverConfig;
version?: number | string;
},
runtimeVersion?: number | string,
dataRuntimeVersion?: number | string,
): Promise<TestObjectProvider> {
return getVersionedTestObjectProviderFromApis(
{
loader: getLoaderApi(baseVersion, loaderVersion),
containerRuntime: getContainerRuntimeApi(baseVersion, runtimeVersion),
dataRuntime: getDataRuntimeApi(baseVersion, dataRuntimeVersion),
driver: getDriverApi(baseVersion, driverConfig?.version),
},
driverConfig,
);
}
Loading

0 comments on commit cfea1b1

Please sign in to comment.