Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide different layers' APIs to describeCompat suites #15917

Merged
merged 17 commits into from
Jun 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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";
agarwal-navin marked this conversation as resolved.
Show resolved Hide resolved
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;
Abe27342 marked this conversation as resolved.
Show resolved Hide resolved
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