Skip to content

Commit fd379d6

Browse files
naugturRafaelGSS
authored andcommitted
test: add a test documenting the plugin signature and lifecycle
1 parent 76d573f commit fd379d6

File tree

2 files changed

+155
-0
lines changed

2 files changed

+155
-0
lines changed

doc/Plugins.md

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ plugins within the benchmarking framework.
77

88
[V8NeverOptimizePlugin](#class-v8neveroptimizeplugin) is enabled by default.
99

10+
To observe how a plugin is used, see the `plugin-api-doc.js` file in tests and explore its results.
11+
1012
## Structure
1113

1214
Each plugin is expected to follow a specific structure with required methods

test/plugin-api-doc.js

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
// @ts-check
2+
const { Suite } = require("../lib/index.js");
3+
const { describe, it } = require("node:test");
4+
const assert = require("node:assert");
5+
6+
class ExamplePlugin {
7+
#aggregation = 0;
8+
constructor() {}
9+
10+
isSupported() {
11+
return true;
12+
}
13+
14+
beforeClockTemplate() {
15+
return [`record("- evaluated beforeClockCode");`];
16+
}
17+
18+
afterClockTemplate({ context }) {
19+
return [
20+
`
21+
${context}.example=1;
22+
record("- evaluated afterClockCode");
23+
`,
24+
];
25+
}
26+
27+
onCompleteBenchmark([time, iterations, results]) {
28+
this.#aggregation += results.example;
29+
}
30+
31+
toString() {
32+
return "ExamplePlugin";
33+
}
34+
35+
getReport() {
36+
return `examplePlugin report`;
37+
}
38+
39+
getResult() {
40+
return {
41+
examplePluginAggregation: this.#aggregation,
42+
};
43+
}
44+
}
45+
46+
describe("plugin API", async () => {
47+
const bench = new Suite({
48+
reporter: () => {},
49+
plugins: [captureAll(new ExamplePlugin())],
50+
});
51+
bench.add("task1", async () => {
52+
record("- task1");
53+
});
54+
bench.add("task2", async () => {
55+
record("- task2");
56+
});
57+
const [bench1] = await bench.run();
58+
59+
it("matches method signatures", async () => {
60+
const recordedMethodSignatures = getSignatures();
61+
assert.deepStrictEqual(recordedMethodSignatures, [
62+
"afterClockTemplate({awaitOrEmpty, bench, context, timer})",
63+
"beforeClockTemplate({awaitOrEmpty, bench, context, timer})",
64+
"getReport()",
65+
"getResult()",
66+
"isSupported()",
67+
"onCompleteBenchmark([number, number, object])",
68+
"toString()",
69+
]);
70+
});
71+
it("produces history", async () => {
72+
printExcerptFromHistory();
73+
});
74+
it("aggregates results", async () => {
75+
console.log("Benchmark results plugins field:", bench1.plugins);
76+
assert(bench1.plugins[0].result.examplePluginAggregation > 1);
77+
assert.strictEqual(bench1.plugins[0].report, "examplePlugin report");
78+
});
79+
});
80+
81+
// ============================================
82+
// Utilities to capture the methods and history.
83+
// Moved down the file to keep the test code clean. Hoisting is how they're available.
84+
// No need to look at them, stop reading now, look at the test output instead.
85+
86+
function record(name, args) {
87+
if (args && args.length) {
88+
history.push([name, 1, JSON.stringify(Array.from(args))]);
89+
} else {
90+
const last = history[history.length - 1];
91+
if (last && last[0] === name) {
92+
last[1]++;
93+
} else {
94+
history.push([name, 1]);
95+
}
96+
}
97+
}
98+
99+
function printExcerptFromHistory(n = 25) {
100+
const excerpt = [
101+
...history.slice(0, n),
102+
["(... redacted for brevity ...)", 1],
103+
...history.slice(-n),
104+
]
105+
.map(
106+
([name, count, args]) =>
107+
`${name} ${count > 1 ? "x" + count : ""}${
108+
args ? " with args: " + args : ""
109+
}`
110+
)
111+
.join("\n| ");
112+
console.log("+----------------------------------");
113+
console.log("| Plugin lifecycle log:");
114+
console.log("+----------------------------------");
115+
console.log("|", excerpt);
116+
console.log("+----------------------------------");
117+
}
118+
function getSignatures() {
119+
return Object.entries(API)
120+
.map(
121+
([name, args]) =>
122+
`${name}(${Array.from(args)
123+
.map((a) => {
124+
if (!a) return "";
125+
if (Array.isArray(a))
126+
return "[" + a.map((a) => typeof a).join(", ") + "]";
127+
return "{" + Object.keys(a).sort().join(", ") + "}";
128+
})
129+
.join(", ")})`
130+
)
131+
.sort();
132+
}
133+
var history, API;
134+
function captureAll(pluginInstance) {
135+
history = [];
136+
API = {};
137+
globalThis.record = record; // make record available in the tasks
138+
139+
return new Proxy(pluginInstance, {
140+
get(target, prop, receiver) {
141+
return function (...args) {
142+
record(prop, args);
143+
API[prop] = args;
144+
if (typeof target[prop] === "function") {
145+
return target[prop].apply(target, args);
146+
}
147+
};
148+
},
149+
has(target, prop) {
150+
return true;
151+
},
152+
});
153+
}

0 commit comments

Comments
 (0)