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

feat: expose solidity test runner config via user config #5837

Open
wants to merge 5 commits into
base: v-next
Choose a base branch
from
Open
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
18 changes: 18 additions & 0 deletions v-next/example-project/contracts/Counter.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,24 @@ contract CounterTest {
require(counter.x() == 0, "Initial value should be 0");
}

function testFailInitialValue() public view {
require(counter.x() == 1, "Initial value should be 1");
}

function testFuzzInc(uint8 x) public {
for (uint8 i = 0; i < x; i++) {
counter.inc();
}
require(counter.x() == x, "Value after calling inc x times should be x");
}

function testFailFuzzInc(uint8 x) public {
for (uint8 i = 0; i < x; i++) {
counter.inc();
}
require(counter.x() == x + 1, "Value after calling inc x times should be x + 1");
}

// function invariant() public pure {
// assert(true);
// }
Expand All @@ -47,6 +58,13 @@ contract FailingCounterTest {
);
}

function testFailFuzzInc(uint8 x) public {
for (uint8 i = 0; i < x; i++) {
counter.inc();
}
require(counter.x() == x, "Value after calling inc x times should be x");
}

// function invariant() public pure {
// assert(false);
// }
Expand Down
3 changes: 3 additions & 0 deletions v-next/example-project/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ const config: HardhatUserConfig = {
version: "0.8.1",
},
},
test: {
testFail: true,
}
},
test: {
version: "0.8.2",
Expand Down
101 changes: 100 additions & 1 deletion v-next/hardhat/src/internal/builtin-plugins/solidity-test/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,109 @@
import type { RunOptions } from "./runner.js";
import type { ArtifactsManager } from "../../../types/artifacts.js";
import type { Artifact } from "@ignored/edr";
import type { SolidityTestUserConfig } from "../../../types/config.js";
import type {
Artifact,
SolidityTestRunnerConfigArgs,
CachedChains,
CachedEndpoints,
PathPermission,
StorageCachingConfig,
AddressLabel,
} from "@ignored/edr";

import { HardhatError } from "@ignored/hardhat-vnext-errors";
import { exists } from "@ignored/hardhat-vnext-utils/fs";
import { hexStringToBytes } from "@ignored/hardhat-vnext-utils/hex";
import { resolveFromRoot } from "@ignored/hardhat-vnext-utils/path";

function hexStringToBuffer(hexString: string): Buffer {
return Buffer.from(hexStringToBytes(hexString));
}

export function solidityTestUserConfigToRunOptions(
config: SolidityTestUserConfig,
): RunOptions {
return config;
}

export function solidityTestUserConfigToSolidityTestRunnerConfigArgs(
projectRoot: string,
config: SolidityTestUserConfig,
): SolidityTestRunnerConfigArgs {
const fsPermissions: PathPermission[] | undefined = [
config.fsPermissions?.readWrite?.map((path) => ({ access: 0, path })) ?? [],
config.fsPermissions?.read?.map((path) => ({ access: 0, path })) ?? [],
config.fsPermissions?.write?.map((path) => ({ access: 0, path })) ?? [],
].flat(1);

const labels: AddressLabel[] | undefined = config.labels?.map(
({ address, label }) => ({
address: hexStringToBuffer(address),
label,
}),
);

let rpcStorageCaching: StorageCachingConfig | undefined;
if (config.rpcStorageCaching !== undefined) {
let chains: CachedChains | string[];
if (Array.isArray(config.rpcStorageCaching.chains)) {
chains = config.rpcStorageCaching.chains;
} else {
const rpcStorageCachingChains: "All" | "None" =
config.rpcStorageCaching.chains;
switch (rpcStorageCachingChains) {
case "All":
chains = 0;
break;
case "None":
chains = 1;
break;
}
}
let endpoints: CachedEndpoints | string;
if (config.rpcStorageCaching.endpoints instanceof RegExp) {
endpoints = config.rpcStorageCaching.endpoints.source;
} else {
const rpcStorageCachingEndpoints: "All" | "Remote" =
config.rpcStorageCaching.endpoints;
switch (rpcStorageCachingEndpoints) {
case "All":
endpoints = 0;
break;
case "Remote":
endpoints = 1;
break;
}
}
rpcStorageCaching = {
chains,
endpoints,
};
}

const sender: Buffer | undefined =
config.sender === undefined ? undefined : hexStringToBuffer(config.sender);
const txOrigin: Buffer | undefined =
config.txOrigin === undefined
? undefined
: hexStringToBuffer(config.txOrigin);
const blockCoinbase: Buffer | undefined =
config.blockCoinbase === undefined
? undefined
: hexStringToBuffer(config.blockCoinbase);

return {
projectRoot,
...config,
fsPermissions,
labels,
sender,
txOrigin,
blockCoinbase,
rpcStorageCaching,
};
}

export async function getArtifacts(
hardhatArtifacts: ArtifactsManager,
): Promise<Artifact[]> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,12 @@
import type { HardhatPlugin } from "../../../types/plugins.js";

import { ArgumentType } from "../../../types/arguments.js";
import { task } from "../../core/config.js";

const hardhatPlugin: HardhatPlugin = {
id: "builtin:solidity-tests",
tasks: [
task(["test", "solidity"], "Run the Solidity tests")
.setAction(import.meta.resolve("./task-action.js"))
.addOption({
name: "timeout",
description:
"The maximum time in milliseconds to wait for all the test suites to finish",
type: ArgumentType.INT,
defaultValue: 60 * 60 * 1000,
})
.addFlag({
name: "noCompile",
description: "Don't compile the project before running the tests",
Expand Down
28 changes: 14 additions & 14 deletions v-next/hardhat/src/internal/builtin-plugins/solidity-test/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ function getEdrContext(): EdrContext {
export interface RunOptions {
/**
* The maximum time in milliseconds to wait for all the test suites to finish.
*
* If not provided, the default is 1 hour.
*/
timeout?: number;
}
Expand Down Expand Up @@ -62,18 +60,20 @@ export function run(

const remainingSuites = new Set(testSuiteIds.map(formatArtifactId));

// NOTE: The timeout prevents the situation in which the stream is never
// closed. This can happen if we receive fewer suite results than the
// number of test suites. The timeout is set to 1 hour.
const duration = options?.timeout ?? 60 * 60 * 1000;
const timeout = setTimeout(() => {
controller.error(
new HardhatError(HardhatError.ERRORS.SOLIDITY_TESTS.RUNNER_TIMEOUT, {
duration,
suites: Array.from(remainingSuites).join(", "),
}),
);
}, duration);
let timeout: NodeJS.Timeout | undefined;
if (options?.timeout !== undefined) {
timeout = setTimeout(() => {
controller.error(
new HardhatError(
HardhatError.ERRORS.SOLIDITY_TESTS.RUNNER_TIMEOUT,
{
duration: options.timeout,
suites: Array.from(remainingSuites).join(", "),
},
),
);
}, options.timeout);
}

// TODO: Just getting the context here to get it initialized, but this
// is not currently tied to the `runSolidityTests` function.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
import type { RunOptions } from "./runner.js";
import type { TestEvent } from "./types.js";
import type { NewTaskActionFunction } from "../../../types/tasks.js";
import type { SolidityTestRunnerConfigArgs } from "@ignored/edr";

import { finished } from "node:stream/promises";

import { createNonClosingWriter } from "@ignored/hardhat-vnext-utils/stream";

import { getArtifacts, isTestArtifact } from "./helpers.js";
import {
getArtifacts,
isTestArtifact,
solidityTestUserConfigToRunOptions,
solidityTestUserConfigToSolidityTestRunnerConfigArgs,
} from "./helpers.js";
import { testReporter } from "./reporter.js";
import { run } from "./runner.js";

interface TestActionArguments {
timeout: number;
noCompile: boolean;
}

const runSolidityTests: NewTaskActionFunction<TestActionArguments> = async (
{ timeout, noCompile },
{ noCompile },
hre,
) => {
if (!noCompile) {
Expand All @@ -42,14 +47,19 @@ const runSolidityTests: NewTaskActionFunction<TestActionArguments> = async (
console.log("Running Solidity tests");
console.log();

const config = {
projectRoot: hre.config.paths.root,
};

let includesFailures = false;
let includesErrors = false;

const options: RunOptions = { timeout };
const profileName = hre.globalOptions.buildProfile;
const profile = hre.config.solidity.profiles[profileName];
const testOptions = profile.test;

const config: SolidityTestRunnerConfigArgs =
solidityTestUserConfigToSolidityTestRunnerConfigArgs(
hre.config.paths.root,
testOptions,
);
const options: RunOptions = solidityTestUserConfigToRunOptions(testOptions);

const runStream = run(artifacts, testSuiteIds, config, options);

Expand Down
Loading
Loading