feat(test): Add Deno.test.ignore and Deno.test.only (#20365)

Closes https://github.com/denoland/deno/issues/17106
This commit is contained in:
Bartek Iwańczuk 2023-09-06 14:17:33 +02:00 committed by GitHub
parent 9befa566ec
commit 147c845c95
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 538 additions and 311 deletions

View file

@ -600,11 +600,11 @@ let currentBenchUserExplicitStart = null;
/** @type {number | null} */
let currentBenchUserExplicitEnd = null;
// Main test function provided by Deno.
function test(
function testInner(
nameOrFnOrOptions,
optionsOrFn,
maybeFn,
overrides = {},
) {
if (typeof ops.op_register_test != "function") {
return;
@ -690,6 +690,8 @@ function test(
testDesc = { ...defaults, ...nameOrFnOrOptions, fn, name };
}
testDesc = { ...testDesc, ...overrides };
// Delete this prop in case the user passed it. It's used to detect steps.
delete testDesc.parent;
const jsError = core.destructureError(new Error());
@ -721,6 +723,27 @@ function test(
});
}
// Main test function provided by Deno.
function test(
nameOrFnOrOptions,
optionsOrFn,
maybeFn,
) {
return testInner(nameOrFnOrOptions, optionsOrFn, maybeFn);
}
test.ignore = function (nameOrFnOrOptions, optionsOrFn, maybeFn) {
return testInner(nameOrFnOrOptions, optionsOrFn, maybeFn, { ignore: true });
};
test.only = function (
nameOrFnOrOptions,
optionsOrFn,
maybeFn,
) {
return testInner(nameOrFnOrOptions, optionsOrFn, maybeFn, { only: true });
};
let registeredWarmupBench = false;
// Main bench function provided by Deno.

View file

@ -379,54 +379,63 @@ impl Visit for TestStepCollector<'_> {
fn visit_var_decl(&mut self, node: &ast::VarDecl) {
if let Some(test_context) = &self.maybe_test_context {
for decl in &node.decls {
if let Some(init) = &decl.init {
match init.as_ref() {
// Identify destructured assignments of `step` from test context
ast::Expr::Ident(ident) => {
if ident.sym == *test_context {
if let ast::Pat::Object(object_pat) = &decl.name {
for prop in &object_pat.props {
match prop {
ast::ObjectPatProp::Assign(prop) => {
if prop.key.sym.eq("step") {
self.vars.insert(prop.key.sym.to_string());
}
}
ast::ObjectPatProp::KeyValue(prop) => {
if let ast::PropName::Ident(key_ident) = &prop.key {
if key_ident.sym.eq("step") {
if let ast::Pat::Ident(value_ident) =
&prop.value.as_ref()
{
self.vars.insert(value_ident.id.sym.to_string());
}
}
}
}
_ => (),
}
let Some(init) = &decl.init else {
continue;
};
match init.as_ref() {
// Identify destructured assignments of `step` from test context
ast::Expr::Ident(ident) => {
if ident.sym != *test_context {
continue;
}
let ast::Pat::Object(object_pat) = &decl.name else {
continue;
};
for prop in &object_pat.props {
match prop {
ast::ObjectPatProp::Assign(prop) => {
if prop.key.sym.eq("step") {
self.vars.insert(prop.key.sym.to_string());
}
}
}
}
// Identify variable assignments where the init is test context
// `.step`
ast::Expr::Member(member_expr) => {
if let ast::Expr::Ident(obj_ident) = member_expr.obj.as_ref() {
if obj_ident.sym == *test_context {
if let ast::MemberProp::Ident(prop_ident) = &member_expr.prop
{
if prop_ident.sym.eq("step") {
if let ast::Pat::Ident(binding_ident) = &decl.name {
self.vars.insert(binding_ident.id.sym.to_string());
ast::ObjectPatProp::KeyValue(prop) => {
if let ast::PropName::Ident(key_ident) = &prop.key {
if key_ident.sym.eq("step") {
if let ast::Pat::Ident(value_ident) = &prop.value.as_ref()
{
self.vars.insert(value_ident.id.sym.to_string());
}
}
}
}
_ => (),
}
}
_ => (),
}
// Identify variable assignments where the init is test context
// `.step`
ast::Expr::Member(member_expr) => {
let ast::Expr::Ident(obj_ident) = member_expr.obj.as_ref() else {
continue;
};
if obj_ident.sym != *test_context {
continue;
}
let ast::MemberProp::Ident(prop_ident) = &member_expr.prop else {
continue;
};
if prop_ident.sym.eq("step") {
if let ast::Pat::Ident(binding_ident) = &decl.name {
self.vars.insert(binding_ident.id.sym.to_string());
}
}
}
_ => (),
}
}
}
@ -463,95 +472,151 @@ impl TestCollector {
impl Visit for TestCollector {
fn visit_call_expr(&mut self, node: &ast::CallExpr) {
if let ast::Callee::Expr(callee_expr) = &node.callee {
match callee_expr.as_ref() {
ast::Expr::Ident(ident) => {
if self.vars.contains(&ident.sym.to_string()) {
visit_call_expr(
node,
Some(&self.fns),
source_range_to_lsp_range(&ident.range(), &self.text_info),
None,
&self.text_info,
&mut self.test_module,
);
}
fn visit_if_deno_test(
collector: &mut TestCollector,
node: &ast::CallExpr,
range: &deno_ast::SourceRange,
ns_prop_ident: &ast::Ident,
member_expr: &ast::MemberExpr,
) {
if ns_prop_ident.sym.to_string() == "test" {
let ast::Expr::Ident(ident) = member_expr.obj.as_ref() else {
return;
};
if ident.sym.to_string() != "Deno" {
return;
}
ast::Expr::Member(member_expr) => {
if let ast::MemberProp::Ident(ns_prop_ident) = &member_expr.prop {
if ns_prop_ident.sym.to_string() == "test" {
if let ast::Expr::Ident(ident) = member_expr.obj.as_ref() {
if ident.sym.to_string() == "Deno" {
visit_call_expr(
node,
Some(&self.fns),
source_range_to_lsp_range(
&ns_prop_ident.range(),
&self.text_info,
),
None,
&self.text_info,
&mut self.test_module,
);
}
}
}
}
}
_ => (),
visit_call_expr(
node,
Some(&collector.fns),
source_range_to_lsp_range(range, &collector.text_info),
None,
&collector.text_info,
&mut collector.test_module,
);
}
}
let ast::Callee::Expr(callee_expr) = &node.callee else {
return;
};
match callee_expr.as_ref() {
ast::Expr::Ident(ident) => {
if self.vars.contains(&ident.sym.to_string()) {
visit_call_expr(
node,
Some(&self.fns),
source_range_to_lsp_range(&ident.range(), &self.text_info),
None,
&self.text_info,
&mut self.test_module,
);
}
}
ast::Expr::Member(member_expr) => {
let ast::MemberProp::Ident(ns_prop_ident) = &member_expr.prop else {
return;
};
let ns_prop_ident_name = ns_prop_ident.sym.to_string();
visit_if_deno_test(
self,
node,
&ns_prop_ident.range(),
ns_prop_ident,
member_expr,
);
if ns_prop_ident_name == "ignore" || ns_prop_ident_name == "only" {
let ast::Expr::Member(child_member_expr) = member_expr.obj.as_ref()
else {
return;
};
let ast::MemberProp::Ident(child_ns_prop_ident) =
&child_member_expr.prop
else {
return;
};
visit_if_deno_test(
self,
node,
&ns_prop_ident.range(),
child_ns_prop_ident,
child_member_expr,
);
}
}
_ => (),
}
}
fn visit_var_decl(&mut self, node: &ast::VarDecl) {
for decl in &node.decls {
if let Some(init) = &decl.init {
match init.as_ref() {
// Identify destructured assignments of `test` from `Deno`
ast::Expr::Ident(ident) => {
if ident.sym.to_string() == "Deno" {
if let ast::Pat::Object(object_pat) = &decl.name {
for prop in &object_pat.props {
match prop {
ast::ObjectPatProp::Assign(prop) => {
let name = prop.key.sym.to_string();
if name == "test" {
self.vars.insert(name);
}
}
ast::ObjectPatProp::KeyValue(prop) => {
if let ast::PropName::Ident(key_ident) = &prop.key {
if key_ident.sym.to_string() == "test" {
if let ast::Pat::Ident(value_ident) =
&prop.value.as_ref()
{
self.vars.insert(value_ident.id.sym.to_string());
}
}
}
}
_ => (),
let Some(init) = &decl.init else { continue };
match init.as_ref() {
// Identify destructured assignments of `test` from `Deno`
ast::Expr::Ident(ident) => {
if ident.sym.to_string() != "Deno" {
continue;
}
let ast::Pat::Object(object_pat) = &decl.name else {
continue;
};
for prop in &object_pat.props {
match prop {
ast::ObjectPatProp::Assign(prop) => {
let name = prop.key.sym.to_string();
if name == "test" {
self.vars.insert(name);
}
}
ast::ObjectPatProp::KeyValue(prop) => {
let ast::PropName::Ident(key_ident) = &prop.key else {
continue;
};
if key_ident.sym.to_string() == "test" {
if let ast::Pat::Ident(value_ident) = &prop.value.as_ref() {
self.vars.insert(value_ident.id.sym.to_string());
}
}
}
_ => (),
}
}
// Identify variable assignments where the init is `Deno.test`
ast::Expr::Member(member_expr) => {
if let ast::Expr::Ident(obj_ident) = member_expr.obj.as_ref() {
if obj_ident.sym.to_string() == "Deno" {
if let ast::MemberProp::Ident(prop_ident) = &member_expr.prop {
if prop_ident.sym.to_string() == "test" {
if let ast::Pat::Ident(binding_ident) = &decl.name {
self.vars.insert(binding_ident.id.sym.to_string());
}
}
}
}
}
}
_ => (),
}
// Identify variable assignments where the init is `Deno.test`
ast::Expr::Member(member_expr) => {
let ast::Expr::Ident(obj_ident) = member_expr.obj.as_ref() else {
continue;
};
if obj_ident.sym.to_string() != "Deno" {
continue;
};
let ast::MemberProp::Ident(prop_ident) = &member_expr.prop else {
continue;
};
if prop_ident.sym.to_string() != "test" {
continue;
}
if let ast::Pat::Ident(binding_ident) = &decl.name {
self.vars.insert(binding_ident.id.sym.to_string());
}
}
_ => (),
}
}
}
@ -934,6 +999,8 @@ pub mod tests {
let test_module = collect(
r#"
Deno.test(async function someFunction() {});
Deno.test.ignore(function foo() {});
Deno.test.only(function bar() {});
"#,
);
@ -942,20 +1009,41 @@ pub mod tests {
&TestModule {
specifier: test_module.specifier.clone(),
script_version: test_module.script_version.clone(),
defs: vec![(
"e0f6a73647b763f82176c98a019e54200b799a32007f9859fb782aaa9e308568"
.to_string(),
TestDefinition {
id:
"e0f6a73647b763f82176c98a019e54200b799a32007f9859fb782aaa9e308568"
.to_string(),
name: "someFunction".to_string(),
range: Some(new_range(1, 11, 1, 15)),
is_dynamic: false,
parent_id: None,
step_ids: Default::default(),
}
),]
defs: vec![
(
"87f28e06f5ddadd90a74a93b84df2e31b9edced8301b0ad4c8fbab8d806ec99d".to_string(),
TestDefinition {
id: "87f28e06f5ddadd90a74a93b84df2e31b9edced8301b0ad4c8fbab8d806ec99d".to_string(),
name: "foo".to_string(),
range: Some(new_range(2, 16, 2, 22)),
is_dynamic: false,
parent_id: None,
step_ids: Default::default(),
},
),
(
"e0f6a73647b763f82176c98a019e54200b799a32007f9859fb782aaa9e308568".to_string(),
TestDefinition {
id: "e0f6a73647b763f82176c98a019e54200b799a32007f9859fb782aaa9e308568".to_string(),
name: "someFunction".to_string(),
range: Some(new_range(1, 11, 1, 15)),
is_dynamic: false,
parent_id: None,
step_ids: Default::default(),
}
),
(
"e1bd61cdaf5e64863d3d85baffe3e43bd57cdb8dc0b5d6a9e03ade18b7f68d47".to_string(),
TestDefinition {
id: "e1bd61cdaf5e64863d3d85baffe3e43bd57cdb8dc0b5d6a9e03ade18b7f68d47".to_string(),
name: "bar".to_string(),
range: Some(new_range(3, 16, 3, 20)),
is_dynamic: false,
parent_id: None,
step_ids: Default::default(),
}
)
]
.into_iter()
.collect(),
}

View file

@ -1,4 +1,4 @@
for (let i = 0; i < 10; i++) {
for (let i = 0; i < 5; i++) {
Deno.test({
name: `test ${i}`,
ignore: true,
@ -7,3 +7,11 @@ for (let i = 0; i < 10; i++) {
},
});
}
for (let i = 5; i < 10; i++) {
Deno.test.ignore({
name: `test ${i}`,
fn() {
throw new Error("unreachable");
},
});
}

View file

@ -1,7 +1,8 @@
Check [WILDCARD]/test/only.ts
running 1 test from ./test/only.ts
running 2 tests from ./test/only.ts
only ... ok ([WILDCARD])
only2 ... ok ([WILDCARD])
ok | 1 passed | 0 failed | 2 filtered out ([WILDCARD])
ok | 2 passed | 0 failed | 2 filtered out ([WILDCARD])
error: Test failed because the "only" option was used

View file

@ -9,6 +9,11 @@ Deno.test({
fn() {},
});
Deno.test.only({
name: "only2",
fn() {},
});
Deno.test({
name: "after",
fn() {},

View file

@ -812,188 +812,290 @@ declare namespace Deno {
permissions?: PermissionOptions;
}
/** Register a test which will be run when `deno test` is used on the command
* line and the containing module looks like a test module.
*
* `fn` can be async if required.
*
* ```ts
* import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
*
* Deno.test({
* name: "example test",
* fn() {
* assertEquals("world", "world");
* },
* });
*
* Deno.test({
* name: "example ignored test",
* ignore: Deno.build.os === "windows",
* fn() {
* // This test is ignored only on Windows machines
* },
* });
*
* Deno.test({
* name: "example async test",
* async fn() {
* const decoder = new TextDecoder("utf-8");
* const data = await Deno.readFile("hello_world.txt");
* assertEquals(decoder.decode(data), "Hello world");
* }
* });
* ```
*
* @category Testing
*/
export function test(t: TestDefinition): void;
export const test: DenoTest;
/** Register a test which will be run when `deno test` is used on the command
* line and the containing module looks like a test module.
*
* `fn` can be async if required.
*
* ```ts
* import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
*
* Deno.test("My test description", () => {
* assertEquals("hello", "hello");
* });
*
* Deno.test("My async test description", async () => {
* const decoder = new TextDecoder("utf-8");
* const data = await Deno.readFile("hello_world.txt");
* assertEquals(decoder.decode(data), "Hello world");
* });
* ```
*
* @category Testing
*/
export function test(
name: string,
fn: (t: TestContext) => void | Promise<void>,
): void;
interface DenoTest {
/** Register a test which will be run when `deno test` is used on the command
* line and the containing module looks like a test module.
*
* `fn` can be async if required.
*
* ```ts
* import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
*
* Deno.test({
* name: "example test",
* fn() {
* assertEquals("world", "world");
* },
* });
*
* Deno.test({
* name: "example ignored test",
* ignore: Deno.build.os === "windows",
* fn() {
* // This test is ignored only on Windows machines
* },
* });
*
* Deno.test({
* name: "example async test",
* async fn() {
* const decoder = new TextDecoder("utf-8");
* const data = await Deno.readFile("hello_world.txt");
* assertEquals(decoder.decode(data), "Hello world");
* }
* });
* ```
*
* @category Testing
*/
(t: TestDefinition): void;
/** Register a test which will be run when `deno test` is used on the command
* line and the containing module looks like a test module.
*
* `fn` can be async if required. Declared function must have a name.
*
* ```ts
* import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
*
* Deno.test(function myTestName() {
* assertEquals("hello", "hello");
* });
*
* Deno.test(async function myOtherTestName() {
* const decoder = new TextDecoder("utf-8");
* const data = await Deno.readFile("hello_world.txt");
* assertEquals(decoder.decode(data), "Hello world");
* });
* ```
*
* @category Testing
*/
export function test(fn: (t: TestContext) => void | Promise<void>): void;
/** Register a test which will be run when `deno test` is used on the command
* line and the containing module looks like a test module.
*
* `fn` can be async if required.
*
* ```ts
* import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
*
* Deno.test("My test description", () => {
* assertEquals("hello", "hello");
* });
*
* Deno.test("My async test description", async () => {
* const decoder = new TextDecoder("utf-8");
* const data = await Deno.readFile("hello_world.txt");
* assertEquals(decoder.decode(data), "Hello world");
* });
* ```
*
* @category Testing
*/
(
name: string,
fn: (t: TestContext) => void | Promise<void>,
): void;
/** Register a test which will be run when `deno test` is used on the command
* line and the containing module looks like a test module.
*
* `fn` can be async if required.
*
* ```ts
* import {assert, fail, assertEquals} from "https://deno.land/std/testing/asserts.ts";
*
* Deno.test("My test description", { permissions: { read: true } }, (): void => {
* assertEquals("hello", "hello");
* });
*
* Deno.test("My async test description", { permissions: { read: false } }, async (): Promise<void> => {
* const decoder = new TextDecoder("utf-8");
* const data = await Deno.readFile("hello_world.txt");
* assertEquals(decoder.decode(data), "Hello world");
* });
* ```
*
* @category Testing
*/
export function test(
name: string,
options: Omit<TestDefinition, "fn" | "name">,
fn: (t: TestContext) => void | Promise<void>,
): void;
/** Register a test which will be run when `deno test` is used on the command
* line and the containing module looks like a test module.
*
* `fn` can be async if required. Declared function must have a name.
*
* ```ts
* import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
*
* Deno.test(function myTestName() {
* assertEquals("hello", "hello");
* });
*
* Deno.test(async function myOtherTestName() {
* const decoder = new TextDecoder("utf-8");
* const data = await Deno.readFile("hello_world.txt");
* assertEquals(decoder.decode(data), "Hello world");
* });
* ```
*
* @category Testing
*/
(fn: (t: TestContext) => void | Promise<void>): void;
/** Register a test which will be run when `deno test` is used on the command
* line and the containing module looks like a test module.
*
* `fn` can be async if required.
*
* ```ts
* import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
*
* Deno.test(
* {
* name: "My test description",
* permissions: { read: true },
* },
* () => {
* assertEquals("hello", "hello");
* },
* );
*
* Deno.test(
* {
* name: "My async test description",
* permissions: { read: false },
* },
* async () => {
* const decoder = new TextDecoder("utf-8");
* const data = await Deno.readFile("hello_world.txt");
* assertEquals(decoder.decode(data), "Hello world");
* },
* );
* ```
*
* @category Testing
*/
export function test(
options: Omit<TestDefinition, "fn">,
fn: (t: TestContext) => void | Promise<void>,
): void;
/** Register a test which will be run when `deno test` is used on the command
* line and the containing module looks like a test module.
*
* `fn` can be async if required.
*
* ```ts
* import {assert, fail, assertEquals} from "https://deno.land/std/testing/asserts.ts";
*
* Deno.test("My test description", { permissions: { read: true } }, (): void => {
* assertEquals("hello", "hello");
* });
*
* Deno.test("My async test description", { permissions: { read: false } }, async (): Promise<void> => {
* const decoder = new TextDecoder("utf-8");
* const data = await Deno.readFile("hello_world.txt");
* assertEquals(decoder.decode(data), "Hello world");
* });
* ```
*
* @category Testing
*/
(
name: string,
options: Omit<TestDefinition, "fn" | "name">,
fn: (t: TestContext) => void | Promise<void>,
): void;
/** Register a test which will be run when `deno test` is used on the command
* line and the containing module looks like a test module.
*
* `fn` can be async if required. Declared function must have a name.
*
* ```ts
* import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
*
* Deno.test(
* { permissions: { read: true } },
* function myTestName() {
* assertEquals("hello", "hello");
* },
* );
*
* Deno.test(
* { permissions: { read: false } },
* async function myOtherTestName() {
* const decoder = new TextDecoder("utf-8");
* const data = await Deno.readFile("hello_world.txt");
* assertEquals(decoder.decode(data), "Hello world");
* },
* );
* ```
*
* @category Testing
*/
export function test(
options: Omit<TestDefinition, "fn" | "name">,
fn: (t: TestContext) => void | Promise<void>,
): void;
/** Register a test which will be run when `deno test` is used on the command
* line and the containing module looks like a test module.
*
* `fn` can be async if required.
*
* ```ts
* import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
*
* Deno.test(
* {
* name: "My test description",
* permissions: { read: true },
* },
* () => {
* assertEquals("hello", "hello");
* },
* );
*
* Deno.test(
* {
* name: "My async test description",
* permissions: { read: false },
* },
* async () => {
* const decoder = new TextDecoder("utf-8");
* const data = await Deno.readFile("hello_world.txt");
* assertEquals(decoder.decode(data), "Hello world");
* },
* );
* ```
*
* @category Testing
*/
(
options: Omit<TestDefinition, "fn" | "name">,
fn: (t: TestContext) => void | Promise<void>,
): void;
/** Register a test which will be run when `deno test` is used on the command
* line and the containing module looks like a test module.
*
* `fn` can be async if required. Declared function must have a name.
*
* ```ts
* import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
*
* Deno.test(
* { permissions: { read: true } },
* function myTestName() {
* assertEquals("hello", "hello");
* },
* );
*
* Deno.test(
* { permissions: { read: false } },
* async function myOtherTestName() {
* const decoder = new TextDecoder("utf-8");
* const data = await Deno.readFile("hello_world.txt");
* assertEquals(decoder.decode(data), "Hello world");
* },
* );
* ```
*
* @category Testing
*/
(
options: Omit<TestDefinition, "fn">,
fn: (t: TestContext) => void | Promise<void>,
): void;
/** Shorthand property for ignoring a particular test case.
*
* @category Testing
*/
ignore(t: Omit<TestDefinition, "ignore">): void;
/** Shorthand property for ignoring a particular test case.
*
* @category Testing
*/
ignore(
name: string,
fn: (t: TestContext) => void | Promise<void>,
): void;
/** Shorthand property for ignoring a particular test case.
*
* @category Testing
*/
ignore(fn: (t: TestContext) => void | Promise<void>): void;
/** Shorthand property for ignoring a particular test case.
*
* @category Testing
*/
ignore(
name: string,
options: Omit<TestDefinition, "fn" | "name" | "ignore">,
fn: (t: TestContext) => void | Promise<void>,
): void;
/** Shorthand property for ignoring a particular test case.
*
* @category Testing
*/
ignore(
options: Omit<TestDefinition, "fn" | "name" | "ignore">,
fn: (t: TestContext) => void | Promise<void>,
): void;
/** Shorthand property for ignoring a particular test case.
*
* @category Testing
*/
ignore(
options: Omit<TestDefinition, "fn" | "ignore">,
fn: (t: TestContext) => void | Promise<void>,
): void;
/** Shorthand property for focusing a particular test case.
*
* @category Testing
*/
only(t: Omit<TestDefinition, "only">): void;
/** Shorthand property for focusing a particular test case.
*
* @category Testing
*/
only(
name: string,
fn: (t: TestContext) => void | Promise<void>,
): void;
/** Shorthand property for focusing a particular test case.
*
* @category Testing
*/
only(fn: (t: TestContext) => void | Promise<void>): void;
/** Shorthand property for focusing a particular test case.
*
* @category Testing
*/
only(
name: string,
options: Omit<TestDefinition, "fn" | "name" | "only">,
fn: (t: TestContext) => void | Promise<void>,
): void;
/** Shorthand property for focusing a particular test case.
*
* @category Testing
*/
only(
options: Omit<TestDefinition, "fn" | "name" | "only">,
fn: (t: TestContext) => void | Promise<void>,
): void;
/** Shorthand property for focusing a particular test case.
*
* @category Testing
*/
only(
options: Omit<TestDefinition, "fn" | "only">,
fn: (t: TestContext) => void | Promise<void>,
): void;
}
/**
* Context that is passed to a benchmarked function. The instance is shared