Skip to content

Commit

Permalink
test: add cache test cases (#8854)
Browse files Browse the repository at this point in the history
  • Loading branch information
jerrykingxyz authored Dec 26, 2024
1 parent 97dd0f8 commit 1e12553
Show file tree
Hide file tree
Showing 14 changed files with 528 additions and 0 deletions.
36 changes: 36 additions & 0 deletions packages/rspack-test-tools/etc/test-tools.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,33 @@ export class BuiltinProcessor<T extends ECompilerType> extends SnapshotProcessor
static defaultOptions<T extends ECompilerType>(this: BuiltinProcessor<T>, context: ITestContext): TCompilerOptions<T>;
}

// @public (undocumented)
export class CacheProcessor<T extends ECompilerType> extends BasicProcessor<T> {
constructor(_cacheOptions: ICacheProcessorOptions<T>);
// (undocumented)
afterAll(context: ITestContext): Promise<void>;
// (undocumented)
protected _cacheOptions: ICacheProcessorOptions<T>;
// (undocumented)
static defaultOptions<T extends ECompilerType>(this: CacheProcessor<T>, context: ITestContext): TCompilerOptions<T>;
// (undocumented)
static findBundle<T extends ECompilerType>(this: CacheProcessor<T>, context: ITestContext): string[];
// (undocumented)
static overrideOptions<T extends ECompilerType>(this: CacheProcessor<T>, context: ITestContext, options: TCompilerOptions<T>): void;
// (undocumented)
run(env: ITestEnv, context: ITestContext): Promise<void>;
// (undocumented)
protected runner: ITestRunner | null;
// (undocumented)
protected updateOptions: TUpdateOptions;
}

// @public (undocumented)
export class CacheRunnerFactory<T extends ECompilerType> extends BasicRunnerFactory<T> {
// (undocumented)
protected createRunner(file: string, stats: TCompilerStatsCompilation<T>, compilerOptions: TCompilerOptions<T>, env: ITestEnv): ITestRunner;
}

// @public (undocumented)
export function checkChunkModules(statsJson: any, chunkModulesMap: any, strict?: boolean): boolean;

Expand Down Expand Up @@ -153,6 +180,9 @@ export class ConfigProcessor<T extends ECompilerType> extends MultiTaskProcessor
// @public (undocumented)
export function createBuiltinCase(name: string, src: string, dist: string): void;

// @public (undocumented)
export function createCacheCase(name: string, src: string, dist: string, target: TCompilerOptions<ECompilerType.Rspack>["target"]): void;

// @public (undocumented)
export function createCompilerCase(name: string, src: string, dist: string, testConfig: string): void;

Expand Down Expand Up @@ -570,6 +600,12 @@ export interface IBasicRunnerOptions<T extends ECompilerType> {
export interface IBuiltinProcessorOptions<T extends ECompilerType> extends Omit<ISnapshotProcessorOptions<T>, "runable"> {
}

// @public (undocumented)
export interface ICacheProcessorOptions<T extends ECompilerType> extends Omit<IBasicProcessorOptions<T>, "runable"> {
// (undocumented)
target: TCompilerOptions<T>["target"];
}

// @public (undocumented)
export interface ICompareOptions {
// (undocumented)
Expand Down
44 changes: 44 additions & 0 deletions packages/rspack-test-tools/src/case/cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { CacheProcessor } from "../processor/cache";
import { CacheRunnerFactory } from "../runner";
import { BasicCaseCreator } from "../test/creator";
import { ECompilerType, type TCompilerOptions } from "../type";

type TTarget = TCompilerOptions<ECompilerType.Rspack>["target"];

const creators: Map<
TTarget,
BasicCaseCreator<ECompilerType.Rspack>
> = new Map();

function getCreator(target: TTarget) {
if (!creators.has(target)) {
creators.set(
target,
new BasicCaseCreator({
clean: true,
describe: true,
target,
steps: ({ name, target }) => [
new CacheProcessor({
name,
target: target as TTarget,
compilerType: ECompilerType.Rspack,
configFiles: ["rspack.config.js", "webpack.config.js"]
})
],
runner: CacheRunnerFactory
})
);
}
return creators.get(target)!;
}

export function createCacheCase(
name: string,
src: string,
dist: string,
target: TCompilerOptions<ECompilerType.Rspack>["target"]
) {
const creator = getCreator(target);
creator.create(name, src, dist);
}
1 change: 1 addition & 0 deletions packages/rspack-test-tools/src/case/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ export * from "./stats-output";
export * from "./treeshaking";
export * from "./watch";
export * from "./new-incremental";
export * from "./cache";
38 changes: 38 additions & 0 deletions packages/rspack-test-tools/src/helper/loaders/hot-update.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { TUpdateOptions } from "../../type";

export default function (this: any, c: string) {
let content = c;
if (content.includes("NEXT_HMR")) {
content = `
${content}
let __hmr_children__ = [...module.children];
let __hmr_used_exports__ = __hmr_children__.reduce((res, child) => {
res[child] = __webpack_module_cache__[child].exports;
return res;
}, {});
module.hot.accept(__hmr_children__, () => {
__hmr_children__.forEach((child) => {
const reexports = __webpack_require__(child);
for (let key in reexports) {
Object.defineProperty(__hmr_used_exports__[child], key, {
configurable: true,
enumerable: true,
get: () => reexports[key]
});
}
});
});
`;
}
content = content.replace(/NEXT_HMR/g, "NEXT_HMR.bind(null, module)");

const options: TUpdateOptions = this.getOptions();
const items = content.split(/---+\r?\n/g);
if (items.length <= 1) {
return content;
}

options.totalUpdates = Math.max(options.totalUpdates, items.length);
options.changedFiles.push(this.resourcePath);
return items[options.updateIndex];
}
40 changes: 40 additions & 0 deletions packages/rspack-test-tools/src/helper/plugins/hot-update.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { Compiler } from "@rspack/core";
import type { TUpdateOptions } from "../../type";

export class TestHotUpdatePlugin {
constructor(private updateOptions: TUpdateOptions) {}
apply(compiler: Compiler) {
compiler.hooks.beforeRun.tap("TestHotUpdatePlugin", () => {
compiler.modifiedFiles = new Set(this.updateOptions.changedFiles);
this.updateOptions.changedFiles = [];
});

compiler.hooks.compilation.tap("TestHotUpdatePlugin", compilation => {
compilation.hooks.additionalTreeRuntimeRequirements.tap(
"HMR_TEST_PLUGIN",
(_chunk: any, set: any) => {
set.add(compiler.webpack.RuntimeGlobals.moduleCache);
}
);
compilation.hooks.runtimeModule.tap(
"HMR_TEST_PLUGIN",
(module: any, _set: any) => {
if (module.constructorName === "DefinePropertyGettersRuntimeModule") {
module.source.source = Buffer.from(
`
__webpack_require__.d = function (exports, definition) {
for (var key in definition) {
if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
Object.defineProperty(exports, key, { configurable: true, enumerable: true, get: definition[key] });
}
}
};
`,
"utf-8"
);
}
}
);
});
}
}
1 change: 1 addition & 0 deletions packages/rspack-test-tools/src/helper/plugins/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./hot-update";
164 changes: 164 additions & 0 deletions packages/rspack-test-tools/src/processor/cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import path from "node:path";
import { rspack } from "@rspack/core";

import { TestHotUpdatePlugin } from "../helper/plugins";
import {
ECompilerType,
type ITestContext,
type ITestEnv,
type ITestRunner,
type TCompilerOptions,
type TUpdateOptions
} from "../type";
import { BasicProcessor, type IBasicProcessorOptions } from "./basic";

export interface ICacheProcessorOptions<T extends ECompilerType>
extends Omit<IBasicProcessorOptions<T>, "runable"> {
target: TCompilerOptions<T>["target"];
}

export class CacheProcessor<T extends ECompilerType> extends BasicProcessor<T> {
protected updateOptions: TUpdateOptions;
protected runner: ITestRunner | null = null;

constructor(protected _cacheOptions: ICacheProcessorOptions<T>) {
const fakeUpdateLoaderOptions: TUpdateOptions = {
updateIndex: 0,
totalUpdates: 1,
changedFiles: []
};
super({
defaultOptions: CacheProcessor.defaultOptions,
overrideOptions: CacheProcessor.overrideOptions,
findBundle: CacheProcessor.findBundle,
runable: true,
..._cacheOptions
});
this.updateOptions = fakeUpdateLoaderOptions;
}

async run(env: ITestEnv, context: ITestContext) {
context.setValue(
this._options.name,
"hotUpdateContext",
this.updateOptions
);
await super.run(env, context);
}

static findBundle<T extends ECompilerType>(
this: CacheProcessor<T>,
context: ITestContext
): string[] {
const files: string[] = [];
const prefiles: string[] = [];
const compiler = context.getCompiler(this._cacheOptions.name);
if (!compiler) throw new Error("Compiler should exists when find bundle");
const stats = compiler.getStats();
if (!stats) throw new Error("Stats should exists when find bundle");
const info = stats.toJson({ all: false, entrypoints: true });
if (
this._cacheOptions.target === "web" ||
this._cacheOptions.target === "webworker"
) {
for (const file of info.entrypoints!.main.assets!) {
if (file.name.endsWith(".js")) {
files.push(file.name);
} else {
prefiles.push(file.name);
}
}
} else {
const assets = info.entrypoints!.main.assets!.filter(s =>
s.name.endsWith(".js")
);
files.push(assets[assets.length - 1].name);
}
return [...prefiles, ...files];
}

async afterAll(context: ITestContext) {
await super.afterAll(context);
if (
this.updateOptions.updateIndex + 1 !==
this.updateOptions.totalUpdates
) {
throw new Error(
`Should run all hot steps (${this.updateOptions.updateIndex + 1} / ${this.updateOptions.totalUpdates}): ${this._options.name}`
);
}
}

static defaultOptions<T extends ECompilerType>(
this: CacheProcessor<T>,
context: ITestContext
): TCompilerOptions<T> {
const options = {
context: context.getSource(),
mode: "development",
devtool: false,
output: {
path: context.getDist(),
filename: "bundle.js",
chunkFilename: "[name].chunk.[fullhash].js",
publicPath: "https://test.cases/path/",
library: { type: "commonjs2" }
},
optimization: {
moduleIds: "named"
},
target: this._cacheOptions.target,
experiments: {
css: true,
rspackFuture: {
bundlerInfo: {
force: false
}
}
}
} as TCompilerOptions<T>;

if (this._cacheOptions.compilerType === ECompilerType.Rspack) {
options.plugins ??= [];
(options as TCompilerOptions<ECompilerType.Rspack>).plugins!.push(
new rspack.HotModuleReplacementPlugin()
);
}

return options;
}

static overrideOptions<T extends ECompilerType>(
this: CacheProcessor<T>,
context: ITestContext,
options: TCompilerOptions<T>
): void {
if (!options.entry) {
options.entry = "./index.js";
}

options.module ??= {};
for (const cssModuleType of ["css/auto", "css/module", "css"] as const) {
options.module!.generator ??= {};
options.module!.generator[cssModuleType] ??= {};
options.module!.generator[cssModuleType]!.exportsOnly ??=
this._cacheOptions.target === "async-node";
}
options.module.rules ??= [];
options.module.rules.push({
test: /\.(js|css|json)/,
use: [
{
loader: path.resolve(__dirname, "../helper/loaders/hot-update.js"),
options: this.updateOptions
}
]
});
if (this._cacheOptions.compilerType === ECompilerType.Rspack) {
options.plugins ??= [];
(options as TCompilerOptions<ECompilerType.Rspack>).plugins!.push(
new TestHotUpdatePlugin(this.updateOptions)
);
}
}
}
1 change: 1 addition & 0 deletions packages/rspack-test-tools/src/processor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ export * from "./snapshot";
export * from "./stats";
export * from "./stats-api";
export * from "./watch";
export * from "./cache";
Loading

0 comments on commit 1e12553

Please sign in to comment.