@@ -8,9 +8,33 @@ import {
88 teardownCore ,
99} from "@codspeed/core" ;
1010import path from "path" ;
11- import { Benchmark , Suite } from "vitest" ;
11+ import { Benchmark , chai , Suite , Task } from "vitest" ;
1212import { NodeBenchmarkRunner } from "vitest/runners" ;
13- import { getBenchFn } from "vitest/suite" ;
13+ import { getBenchFn , getHooks } from "vitest/suite" ;
14+
15+ type SuiteHooks = ReturnType < typeof getHooks > ;
16+
17+ function getSuiteHooks ( suite : Suite , name : keyof SuiteHooks ) {
18+ return getHooks ( suite ) [ name ] ;
19+ }
20+
21+ export async function callSuiteHook < T extends keyof SuiteHooks > (
22+ suite : Suite ,
23+ currentTask : Task ,
24+ name : T
25+ ) : Promise < void > {
26+ if ( name === "beforeEach" && suite . suite ) {
27+ await callSuiteHook ( suite . suite , currentTask , name ) ;
28+ }
29+
30+ const hooks = getSuiteHooks ( suite , name ) ;
31+
32+ await Promise . all ( hooks . map ( ( fn ) => fn ( ) ) ) ;
33+
34+ if ( name === "afterEach" && suite . suite ) {
35+ await callSuiteHook ( suite . suite , currentTask , name ) ;
36+ }
37+ }
1438
1539const currentFileName =
1640 typeof __filename === "string"
@@ -26,39 +50,62 @@ function logCodSpeed(message: string) {
2650 console . log ( `[CodSpeed] ${ message } ` ) ;
2751}
2852
29- async function runBenchmarkSuite ( suite : Suite , parentSuiteName ?: string ) {
30- const benchmarkGroup : Benchmark [ ] = [ ] ;
31- const benchmarkSuiteGroup : Suite [ ] = [ ] ;
32- for ( const task of suite . tasks ) {
33- if ( task . mode !== "run" ) continue ;
53+ async function runBench ( benchmark : Benchmark , currentSuiteName : string ) {
54+ const uri = `${ currentSuiteName } ::${ benchmark . name } ` ;
55+ const fn = getBenchFn ( benchmark ) ;
3456
35- if ( task . meta ?. benchmark ) benchmarkGroup . push ( task as Benchmark ) ;
36- else if ( task . type === "suite" ) benchmarkSuiteGroup . push ( task ) ;
57+ await callSuiteHook ( benchmark . suite , benchmark , "beforeEach" ) ;
58+ try {
59+ await optimizeFunction ( fn ) ;
60+ } catch ( e ) {
61+ // if the error is not an assertion error, we want to fail the run
62+ // we allow assertion errors because we want to be able to use `expect` in the benchmark to allow for better authoring
63+ // assertions are allowed to fail in the optimization phase since it might be linked to stateful code
64+ if ( ! ( e instanceof chai . AssertionError ) ) {
65+ throw e ;
66+ }
3767 }
68+ await callSuiteHook ( benchmark . suite , benchmark , "afterEach" ) ;
69+
70+ await callSuiteHook ( benchmark . suite , benchmark , "beforeEach" ) ;
71+ await mongoMeasurement . start ( uri ) ;
72+ await ( async function __codspeed_root_frame__ ( ) {
73+ Measurement . startInstrumentation ( ) ;
74+ // @ts -expect-error we do not need to bind the function to an instance of tinybench's Bench
75+ await fn ( ) ;
76+ Measurement . stopInstrumentation ( uri ) ;
77+ } ) ( ) ;
78+ await mongoMeasurement . stop ( uri ) ;
79+ await callSuiteHook ( benchmark . suite , benchmark , "afterEach" ) ;
80+
81+ logCodSpeed ( `${ uri } done` ) ;
82+ }
3883
84+ async function runBenchmarkSuite ( suite : Suite , parentSuiteName ?: string ) {
3985 const currentSuiteName = parentSuiteName
4086 ? parentSuiteName + "::" + suite . name
4187 : suite . name ;
4288
43- for ( const subSuite of benchmarkSuiteGroup ) {
44- await runBenchmarkSuite ( subSuite , currentSuiteName ) ;
89+ // do not call `beforeAll` if we are in the root suite, since it is already called by vitest
90+ // see https://github.com/vitest-dev/vitest/blob/1fee63f2598edc228017f18eca325f85ee54aee0/packages/runner/src/run.ts#L293
91+ if ( parentSuiteName !== undefined ) {
92+ await callSuiteHook ( suite , suite , "beforeAll" ) ;
4593 }
4694
47- for ( const benchmark of benchmarkGroup ) {
48- const uri = `${ currentSuiteName } ::${ benchmark . name } ` ;
49- const fn = getBenchFn ( benchmark ) ;
95+ for ( const task of suite . tasks ) {
96+ if ( task . mode !== "run" ) continue ;
5097
51- await optimizeFunction ( fn ) ;
52- await mongoMeasurement . start ( uri ) ;
53- await ( async function __codspeed_root_frame__ ( ) {
54- Measurement . startInstrumentation ( ) ;
55- // @ts -expect-error we do not need to bind the function to an instance of tinybench's Bench
56- await fn ( ) ;
57- Measurement . stopInstrumentation ( uri ) ;
58- } ) ( ) ;
59- await mongoMeasurement . stop ( uri ) ;
60-
61- logCodSpeed ( ` ${ uri } done` ) ;
98+ if ( task . meta ?. benchmark ) {
99+ await runBench ( task as Benchmark , currentSuiteName ) ;
100+ } else if ( task . type === "suite" ) {
101+ await runBenchmarkSuite ( task , currentSuiteName ) ;
102+ }
103+ }
104+
105+ // do not call `afterAll` if we are in the root suite, since it is already called by vitest
106+ // see https://github.com/vitest-dev/vitest/blob/1fee63f2598edc228017f18eca325f85ee54aee0/packages/runner/src/run.ts#L324
107+ if ( parentSuiteName !== undefined ) {
108+ await callSuiteHook ( suite , suite , "afterAll" ) ;
62109 }
63110}
64111
0 commit comments