From 6dea7803bcb10b693d0ddd7770ad3951d512f1c7 Mon Sep 17 00:00:00 2001 From: galargh Date: Thu, 24 Oct 2024 16:55:31 +0100 Subject: [PATCH 1/7] feat: define solidity test sources in config --- .../solidity-test/hook-handlers/config.ts | 74 +++++++++++++++++++ .../builtin-plugins/solidity-test/index.ts | 15 ++++ .../solidity-test/type-extensions.ts | 11 +++ .../test/internal/core/hook-manager.ts | 12 +-- 4 files changed, 107 insertions(+), 5 deletions(-) create mode 100644 v-next/hardhat/src/internal/builtin-plugins/solidity-test/hook-handlers/config.ts create mode 100644 v-next/hardhat/src/internal/builtin-plugins/solidity-test/type-extensions.ts diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/hook-handlers/config.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/hook-handlers/config.ts new file mode 100644 index 0000000000..a3a96995b0 --- /dev/null +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/hook-handlers/config.ts @@ -0,0 +1,74 @@ +import type { ConfigHooks } from "@ignored/hardhat-vnext/types/hooks"; + +import { isObject } from "@ignored/hardhat-vnext-utils/lang"; +import { resolveFromRoot } from "@ignored/hardhat-vnext-utils/path"; +import { + conditionalUnionType, + validateUserConfigZodType, +} from "@ignored/hardhat-vnext-zod-utils"; +import { z } from "zod"; + +const userConfigType = z.object({ + paths: z + .object({ + test: conditionalUnionType( + [ + [isObject, z.object({ solidity: z.string().optional() })], + [(data) => typeof data === "string", z.string()], + ], + "Expected a string or an object with an optional 'solidity' property", + ).optional(), + }) + .optional(), +}); + +export default async (): Promise> => { + const handlers: Partial = { + validateUserConfig: async (userConfig) => { + return validateUserConfigZodType(userConfig, userConfigType); + }, + resolveUserConfig: async ( + userConfig, + resolveConfigurationVariable, + next, + ) => { + const resolvedConfig = await next( + userConfig, + resolveConfigurationVariable, + ); + + let testsPaths: string[]; + + // TODO: use isObject when the type narrowing issue is fixed + if ( + typeof userConfig.paths?.tests === "object" && + userConfig.paths.tests.solidity !== undefined + ) { + if (Array.isArray(userConfig.paths.tests.solidity)) { + testsPaths = userConfig.paths.tests.solidity; + } else { + testsPaths = [userConfig.paths.tests.solidity]; + } + } else { + testsPaths = resolvedConfig.paths.sources.solidity; + } + + const resolvedPaths = testsPaths.map((p) => + resolveFromRoot(resolvedConfig.paths.root, p), + ); + + return { + ...resolvedConfig, + paths: { + ...resolvedConfig.paths, + tests: { + ...resolvedConfig.paths.tests, + solidity: resolvedPaths, + }, + }, + }; + }, + }; + + return handlers; +}; diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/index.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/index.ts index 8211f9fbdc..cd05bb892d 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/index.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/index.ts @@ -5,6 +5,21 @@ import { task } from "../../core/config.js"; const hardhatPlugin: HardhatPlugin = { id: "builtin:solidity-tests", + dependencies: [ + async () => { + const { default: artifactsPlugin } = await import( + "../artifacts/index.js" + ); + return artifactsPlugin; + }, + async () => { + const { default: solidityPlugin } = await import("../solidity/index.js"); + return solidityPlugin; + }, + ], + hookHandlers: { + config: import.meta.resolve("./hook-handlers/config.js"), + }, tasks: [ task(["test", "solidity"], "Run the Solidity tests") .setAction(import.meta.resolve("./task-action.js")) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/type-extensions.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/type-extensions.ts new file mode 100644 index 0000000000..72564ccc48 --- /dev/null +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/type-extensions.ts @@ -0,0 +1,11 @@ +import "@ignored/hardhat-vnext/types/config"; + +declare module "@ignored/hardhat-vnext/types/config" { + export interface TestPathsUserConfig { + solidity?: string | string[]; + } + + export interface TestPathsConfig { + solidity: string[]; + } +} diff --git a/v-next/hardhat/test/internal/core/hook-manager.ts b/v-next/hardhat/test/internal/core/hook-manager.ts index 8c4940e4f9..648a0054de 100644 --- a/v-next/hardhat/test/internal/core/hook-manager.ts +++ b/v-next/hardhat/test/internal/core/hook-manager.ts @@ -257,8 +257,9 @@ describe("HookManager", () => { _context: HookContext, givenHre: HardhatRuntimeEnvironment, ): Promise => { - givenHre.config.paths.tests = - "./test-folder-from-plugin1"; + givenHre.config.paths.tests = { + solidity: ["./test-folder-from-plugin1"], + }; }, } as Partial; @@ -277,8 +278,9 @@ describe("HookManager", () => { _context: HookContext, givenHre: HardhatRuntimeEnvironment, ): Promise => { - givenHre.config.paths.tests = - "./test-folder-from-overriding-plugin2"; + givenHre.config.paths.tests = { + solidity: ["./test-folder-from-overriding-plugin2"], + }; }, } as Partial; @@ -302,7 +304,7 @@ describe("HookManager", () => { assert.equal(result.length, 2); assert.equal( - hre.config.paths.tests, + hre.config.paths.tests.solidity[0], "./test-folder-from-overriding-plugin2", ); }); From 0ebcfbf6da4b64afc45356c8e9525c63b57804c2 Mon Sep 17 00:00:00 2001 From: galargh Date: Sat, 26 Oct 2024 22:37:18 +0200 Subject: [PATCH 2/7] feat: compile solidity test sources only --- v-next/example-project/hardhat.config.ts | 5 + .../builtin-plugins/solidity-test/helpers.ts | 138 +++++++++++------- .../builtin-plugins/solidity-test/index.ts | 14 +- .../solidity-test/task-action.ts | 66 ++++++--- .../solidity/build-system/build-system.ts | 4 +- .../src/types/solidity/build-system.ts | 6 +- .../builtin-plugins/solidity-test/helpers.ts | 98 ------------- .../solidity-test/test-fixtures/Test.t.sol | 43 ------ 8 files changed, 151 insertions(+), 223 deletions(-) delete mode 100644 v-next/hardhat/test/internal/builtin-plugins/solidity-test/helpers.ts delete mode 100644 v-next/hardhat/test/internal/builtin-plugins/solidity-test/test-fixtures/Test.t.sol diff --git a/v-next/example-project/hardhat.config.ts b/v-next/example-project/hardhat.config.ts index 2b8fed92b4..8e0d93ae1b 100644 --- a/v-next/example-project/hardhat.config.ts +++ b/v-next/example-project/hardhat.config.ts @@ -148,6 +148,11 @@ const config: HardhatUserConfig = { tests: { mocha: "test/mocha", nodeTest: "test/node", + solidity: [ + "contracts/Counter.sol", + "contracts/Counter.t.sol", + "contracts/WithForge.t.sol", + ], }, }, solidity: { diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/helpers.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/helpers.ts index a5f71e45ce..99f8c39706 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/helpers.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/helpers.ts @@ -1,66 +1,104 @@ -import type { ArtifactsManager } from "../../../types/artifacts.js"; -import type { Artifact } from "@ignored/edr"; +import type { + Artifact as HardhatArtifact, + BuildInfo, +} from "../../../types/artifacts.js"; +import type { + CompilationJobCreationError, + FailedFileBuildResult, + FileBuildResult, +} from "../../../types/solidity/build-system.js"; +import type { + ArtifactId as EdrArtifactId, + Artifact as EdrArtifact, +} from "@ignored/edr"; -import { HardhatError } from "@ignored/hardhat-vnext-errors"; -import { exists } from "@ignored/hardhat-vnext-utils/fs"; -import { resolveFromRoot } from "@ignored/hardhat-vnext-utils/path"; +import path from "node:path"; -export async function getArtifacts( - hardhatArtifacts: ArtifactsManager, -): Promise { - const fqns = await hardhatArtifacts.getAllFullyQualifiedNames(); - const artifacts: Artifact[] = []; +import { HardhatError } from "@ignored/hardhat-vnext-errors"; +import { readJsonFile } from "@ignored/hardhat-vnext-utils/fs"; - for (const fqn of fqns) { - const hardhatArtifact = await hardhatArtifacts.readArtifact(fqn); - const buildInfo = await hardhatArtifacts.getBuildInfo(fqn); +import { FileBuildResultType } from "../../../types/solidity/build-system.js"; - if (buildInfo === undefined) { - throw new HardhatError( - HardhatError.ERRORS.SOLIDITY_TESTS.BUILD_INFO_NOT_FOUND_FOR_CONTRACT, - { - fqn, - }, - ); - } +type SolidityBuildResults = + | Map + | CompilationJobCreationError; +type SuccessfulSolidityBuildResults = Map< + string, + Exclude +>; - const id = { - name: hardhatArtifact.contractName, - solcVersion: buildInfo.solcVersion, - source: hardhatArtifact.sourceName, - }; +export function throwIfSolidityBuildFailed( + results: SolidityBuildResults, +): asserts results is SuccessfulSolidityBuildResults { + if ("reason" in results) { + throw new HardhatError( + HardhatError.ERRORS.SOLIDITY.COMPILATION_JOB_CREATION_ERROR, + { + reason: results.formattedReason, + rootFilePath: results.rootFilePath, + buildProfile: results.buildProfile, + }, + ); + } - const contract = { - abi: JSON.stringify(hardhatArtifact.abi), - bytecode: hardhatArtifact.bytecode, - deployedBytecode: hardhatArtifact.deployedBytecode, - }; + const sucessful = [...results.values()].every( + ({ type }) => + type === FileBuildResultType.CACHE_HIT || + type === FileBuildResultType.BUILD_SUCCESS, + ); - const artifact = { id, contract }; - artifacts.push(artifact); + if (!sucessful) { + throw new HardhatError(HardhatError.ERRORS.SOLIDITY.BUILD_FAILED); } - - return artifacts; } -export async function isTestArtifact( - root: string, - artifact: Artifact, -): Promise { - const { source } = artifact.id; +export async function getArtifacts( + results: SuccessfulSolidityBuildResults, + artifactsRootPath: string, +): Promise { + const artifacts: EdrArtifact[] = []; - if (!source.endsWith(".t.sol")) { - return false; - } + for (const [source, result] of results.entries()) { + for (const artifactPath of result.contractArtifactsGenerated) { + const artifact: HardhatArtifact = await readJsonFile(artifactPath); + const buildInfo: BuildInfo = await readJsonFile( + path.join(artifactsRootPath, "build-info", `${result.buildId}.json`), + ); - // NOTE: We also check whether the file exists in the workspace to filter out - // the artifacts from node modules. - const sourcePath = resolveFromRoot(root, source); - const sourceExists = await exists(sourcePath); + const id = { + name: artifact.contractName, + solcVersion: buildInfo.solcVersion, + source, + }; - if (!sourceExists) { - return false; + const contract = { + abi: JSON.stringify(artifact.abi), + bytecode: artifact.bytecode, + deployedBytecode: artifact.deployedBytecode, + }; + + artifacts.push({ + id, + contract, + }); + } } - return true; + return artifacts; +} + +export async function getTestSuiteIds( + artifacts: EdrArtifact[], + rootFilePaths: string[], + projectRoot: string, +): Promise { + const testSources = rootFilePaths + .filter((p) => { + return p.endsWith(".t.sol"); + }) + .map((p) => path.relative(projectRoot, p)); + + return artifacts + .map(({ id }) => id) + .filter(({ source }) => testSources.includes(source)); } diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/index.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/index.ts index cd05bb892d..4770891eb7 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/index.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/index.ts @@ -3,20 +3,10 @@ import type { HardhatPlugin } from "../../../types/plugins.js"; import { ArgumentType } from "../../../types/arguments.js"; import { task } from "../../core/config.js"; +import "./type-extensions.js"; + const hardhatPlugin: HardhatPlugin = { id: "builtin:solidity-tests", - dependencies: [ - async () => { - const { default: artifactsPlugin } = await import( - "../artifacts/index.js" - ); - return artifactsPlugin; - }, - async () => { - const { default: solidityPlugin } = await import("../solidity/index.js"); - return solidityPlugin; - }, - ], hookHandlers: { config: import.meta.resolve("./hook-handlers/config.js"), }, diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/task-action.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/task-action.ts index 02b8e14019..cc04db04dd 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/task-action.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/task-action.ts @@ -1,12 +1,25 @@ import type { RunOptions } from "./runner.js"; import type { TestEvent } from "./types.js"; +import type { BuildOptions } from "../../../types/solidity/build-system.js"; import type { NewTaskActionFunction } from "../../../types/tasks.js"; import { finished } from "node:stream/promises"; +import { + getAllFilesMatching, + isDirectory, +} from "@ignored/hardhat-vnext-utils/fs"; +import { resolveFromRoot } from "@ignored/hardhat-vnext-utils/path"; import { createNonClosingWriter } from "@ignored/hardhat-vnext-utils/stream"; -import { getArtifacts, isTestArtifact } from "./helpers.js"; + +import { shouldMergeCompilationJobs } from "../solidity/build-profiles.js"; + +import { + getArtifacts, + getTestSuiteIds, + throwIfSolidityBuildFailed, +} from "./helpers.js"; import { testReporter } from "./reporter.js"; import { run } from "./runner.js"; @@ -19,25 +32,44 @@ const runSolidityTests: NewTaskActionFunction = async ( { timeout, noCompile }, hre, ) => { - if (!noCompile) { - await hre.tasks.getTask("compile").run({}); - console.log(); - } - - const artifacts = await getArtifacts(hre.artifacts); - const testSuiteIds = ( + const rootFilePaths = ( await Promise.all( - artifacts.map(async (artifact) => { - if (await isTestArtifact(hre.config.paths.root, artifact)) { - return artifact.id; - } - }), + hre.config.paths.tests.solidity + .map((p) => resolveFromRoot(hre.config.paths.root, p)) + .map(async (p) => { + if (await isDirectory(p)) { + return getAllFilesMatching(p, (f) => f.endsWith(".sol")); + } else if (p.endsWith(".sol") === true) { + return [p]; + } else { + return []; + } + }), ) - ).filter((artifact) => artifact !== undefined); + ).flat(1); + + const buildOptions: BuildOptions = { + // NOTE: The uncached sources will still be compiled event if `noCompile` + // is true. We could consider adding a `cacheOnly` option to support true + // `noCompile` behavior. + force: !noCompile, + buildProfile: hre.globalOptions.buildProfile, + mergeCompilationJobs: shouldMergeCompilationJobs( + hre.globalOptions.buildProfile, + ), + quiet: false, + }; - if (testSuiteIds.length === 0) { - return; - } + const results = await hre.solidity.build(rootFilePaths, buildOptions); + + throwIfSolidityBuildFailed(results); + + const artifacts = await getArtifacts(results, hre.config.paths.artifacts); + const testSuiteIds = await getTestSuiteIds( + artifacts, + rootFilePaths, + hre.config.paths.root, + ); console.log("Running Solidity tests"); console.log(); diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts index 349a39a8db..d4e17181c9 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts @@ -67,7 +67,7 @@ export interface SolidityBuildSystemOptions { export class SolidityBuildSystemImplementation implements SolidityBuildSystem { readonly #options: SolidityBuildSystemOptions; - readonly #defaultConcurrenty = Math.max(os.cpus().length - 1, 1); + readonly #defaultConcurrency = Math.max(os.cpus().length - 1, 1); #downloadedCompilers = false; constructor(options: SolidityBuildSystemOptions) { @@ -118,7 +118,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { compilationJobs, (compilationJob) => this.runCompilationJob(compilationJob), { - concurrency: options?.concurrency ?? this.#defaultConcurrenty, + concurrency: options?.concurrency ?? this.#defaultConcurrency, // An error when running the compiler is not a compilation failure, but // a fatal failure trying to run it, so we just throw on the first error stopOnError: true, diff --git a/v-next/hardhat/src/types/solidity/build-system.ts b/v-next/hardhat/src/types/solidity/build-system.ts index 821622e61e..f10aaa5b4f 100644 --- a/v-next/hardhat/src/types/solidity/build-system.ts +++ b/v-next/hardhat/src/types/solidity/build-system.ts @@ -149,7 +149,11 @@ export interface CacheHitFileBuildResult { type: FileBuildResultType.CACHE_HIT; // TODO: Should we remove this? It is a buildId of an already existing build // info. + // NOTE: The buildId and contractArtifactsGenerated are useful when one uses + // the build system programatically and wants to find out what artifacts + // were generated for a given file, and with what configuration. buildId: string; + contractArtifactsGenerated: string[]; } export interface SuccessfulFileBuildResult { @@ -265,7 +269,7 @@ export interface SolidityBuildSystem { * This method should only be used after a complete build has succeeded, as * it relies on the build system to have generated all the necessary artifact * files. - + * @param rootFilePaths All the root files of the project. */ cleanupArtifacts(rootFilePaths: string[]): Promise; diff --git a/v-next/hardhat/test/internal/builtin-plugins/solidity-test/helpers.ts b/v-next/hardhat/test/internal/builtin-plugins/solidity-test/helpers.ts deleted file mode 100644 index 245bf895ec..0000000000 --- a/v-next/hardhat/test/internal/builtin-plugins/solidity-test/helpers.ts +++ /dev/null @@ -1,98 +0,0 @@ -import type { Artifact } from "@ignored/edr"; - -import assert from "node:assert/strict"; -import { describe, it } from "node:test"; - -import { isTestArtifact } from "../../../../src/internal/builtin-plugins/solidity-test/helpers.js"; - -const testCases = [ - { - contract: "Abstract", - expected: true, - }, - { - contract: "NoTest", - expected: true, - }, - { - contract: "PublicTest", - expected: true, - }, - { - contract: "ExternalTest", - expected: true, - }, - { - contract: "PrivateTest", - expected: true, - }, - { - contract: "InternalTest", - expected: true, - }, - { - contract: "PublicInvariant", - expected: true, - }, - { - contract: "ExternalInvariant", - expected: true, - }, - { - contract: "PrivateInvariant", - expected: true, - }, - { - contract: "InternalInvariant", - expected: true, - }, -]; - -describe("isTestArtifact", () => { - for (const { contract, expected } of testCases) { - it(`should return ${expected} for the ${contract} contract`, async () => { - const artifact: Artifact = { - id: { - name: contract, - source: `test-fixtures/Test.t.sol`, - solcVersion: "0.8.20", - }, - contract: { - abi: "", - }, - }; - const actual = await isTestArtifact(import.meta.dirname, artifact); - assert.equal(actual, expected); - }); - } - - it("should return false if a file does not exist", async () => { - const artifact: Artifact = { - id: { - name: "Contract", - source: `test-fixtures/NonExistent.t.sol`, - solcVersion: "0.8.20", - }, - contract: { - abi: "", - }, - }; - const actual = await isTestArtifact(import.meta.dirname, artifact); - assert.equal(actual, false); - }); - - it("should return false if the file has the wrong extension", async () => { - const artifact: Artifact = { - id: { - name: "Contract", - source: `test-fixtures/WrongExtension.sol`, - solcVersion: "0.8.20", - }, - contract: { - abi: "", - }, - }; - const actual = await isTestArtifact(import.meta.dirname, artifact); - assert.equal(actual, false); - }); -}); diff --git a/v-next/hardhat/test/internal/builtin-plugins/solidity-test/test-fixtures/Test.t.sol b/v-next/hardhat/test/internal/builtin-plugins/solidity-test/test-fixtures/Test.t.sol deleted file mode 100644 index 025f6a7e35..0000000000 --- a/v-next/hardhat/test/internal/builtin-plugins/solidity-test/test-fixtures/Test.t.sol +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.20; - -abstract contract Abstract { - function testPublic() public {} - function testExternal() external {} - function invariantPublic() public {} - function invariantExternal() external {} -} - -contract NoTest {} - -contract PublicTest { - function test() public {} -} - -contract ExternalTest { - function test() external {} -} - -contract PrivateTest { - function test() private {} -} - -contract InternalTest { - function test() internal {} -} - -contract PublicInvariant { - function invariant() public {} -} - -contract ExternalInvariant { - function invariant() external {} -} - -contract PrivateInvariant { - function invariant() private {} -} - -contract InternalInvariant { - function invariant() internal {} -} From 63e021570b84fb5e39295a7e2e8fade20d4a3691 Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 4 Dec 2024 12:17:18 +0100 Subject: [PATCH 3/7] docs: add inline comments to solidity test helpers functions --- .../builtin-plugins/solidity-test/helpers.ts | 15 +++++++++++++++ .../builtin-plugins/solidity-test/task-action.ts | 2 ++ 2 files changed, 17 insertions(+) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/helpers.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/helpers.ts index 99f8c39706..abdafc0448 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/helpers.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/helpers.ts @@ -27,6 +27,11 @@ type SuccessfulSolidityBuildResults = Map< Exclude >; +/** + * This function asserts that the given Solidity build results are successful. + * It throws a HardhatError if the build results indicate that the compilation + * job failed. + */ export function throwIfSolidityBuildFailed( results: SolidityBuildResults, ): asserts results is SuccessfulSolidityBuildResults { @@ -52,6 +57,11 @@ export function throwIfSolidityBuildFailed( } } +/** + * This function returns the artifacts generated during the compilation associated + * with the given Solidity build results. It relies on the fact that each successful + * build result has a corresponding artifact generated property. + */ export async function getArtifacts( results: SuccessfulSolidityBuildResults, artifactsRootPath: string, @@ -87,6 +97,11 @@ export async function getArtifacts( return artifacts; } +/** + * This function returns the test suite ids associated with the given artifacts. + * The test suite ID is the relative path of the test file, relative to the + * project root. + */ export async function getTestSuiteIds( artifacts: EdrArtifact[], rootFilePaths: string[], diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/task-action.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/task-action.ts index cc04db04dd..f3cac0e3ee 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/task-action.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/task-action.ts @@ -37,6 +37,8 @@ const runSolidityTests: NewTaskActionFunction = async ( hre.config.paths.tests.solidity .map((p) => resolveFromRoot(hre.config.paths.root, p)) .map(async (p) => { + // NOTE: The paths specified in the `paths.tests.solidity` array + // can be either directories or files. if (await isDirectory(p)) { return getAllFilesMatching(p, (f) => f.endsWith(".sol")); } else if (p.endsWith(".sol") === true) { From a4a5b61e12c2b523856f9ba20baa63030802c8db Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 4 Dec 2024 12:23:53 +0100 Subject: [PATCH 4/7] fix: ran pnpm prettier --write to fix linting error --- .../src/internal/builtin-plugins/solidity-test/task-action.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/task-action.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/task-action.ts index f3cac0e3ee..be72c52439 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/task-action.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/task-action.ts @@ -12,7 +12,6 @@ import { import { resolveFromRoot } from "@ignored/hardhat-vnext-utils/path"; import { createNonClosingWriter } from "@ignored/hardhat-vnext-utils/stream"; - import { shouldMergeCompilationJobs } from "../solidity/build-profiles.js"; import { From 18dca3108c5b212a25336220873ee10986e5cabd Mon Sep 17 00:00:00 2001 From: galargh Date: Sat, 4 Jan 2025 00:19:06 +0100 Subject: [PATCH 5/7] chore: restore the semantics of paths.tests.solidity --- v-next/example-project/hardhat.config.ts | 6 +-- .../contracts/WithForge.sol} | 0 .../builtin-plugins/solidity-test/helpers.ts | 10 ++--- .../solidity-test/hook-handlers/config.ts | 23 +++------- .../solidity-test/task-action.ts | 45 ++++++++----------- .../solidity-test/type-extensions.ts | 4 +- .../solidity/build-system/build-system.ts | 5 ++- .../test/internal/core/config-validation.ts | 8 +++- .../test/internal/core/hook-manager.ts | 6 +-- 9 files changed, 43 insertions(+), 64 deletions(-) rename v-next/example-project/{contracts/WithForge.t.sol => test/contracts/WithForge.sol} (100%) diff --git a/v-next/example-project/hardhat.config.ts b/v-next/example-project/hardhat.config.ts index a20ff826bf..a71a2e2bbc 100644 --- a/v-next/example-project/hardhat.config.ts +++ b/v-next/example-project/hardhat.config.ts @@ -163,11 +163,7 @@ const config: HardhatUserConfig = { tests: { mocha: "test/mocha", nodeTest: "test/node", - solidity: [ - "contracts/Counter.sol", - "contracts/Counter.t.sol", - "contracts/WithForge.t.sol", - ], + solidity: "test/contracts", }, }, solidity: { diff --git a/v-next/example-project/contracts/WithForge.t.sol b/v-next/example-project/test/contracts/WithForge.sol similarity index 100% rename from v-next/example-project/contracts/WithForge.t.sol rename to v-next/example-project/test/contracts/WithForge.sol diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/helpers.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/helpers.ts index abdafc0448..807811926c 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/helpers.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/helpers.ts @@ -104,14 +104,12 @@ export async function getArtifacts( */ export async function getTestSuiteIds( artifacts: EdrArtifact[], - rootFilePaths: string[], + rootTestFilePaths: string[], projectRoot: string, ): Promise { - const testSources = rootFilePaths - .filter((p) => { - return p.endsWith(".t.sol"); - }) - .map((p) => path.relative(projectRoot, p)); + const testSources = rootTestFilePaths.map((p) => + path.relative(projectRoot, p), + ); return artifacts .map(({ id }) => id) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/hook-handlers/config.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/hook-handlers/config.ts index a3a96995b0..293b2efa9a 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/hook-handlers/config.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/hook-handlers/config.ts @@ -37,25 +37,12 @@ export default async (): Promise> => { resolveConfigurationVariable, ); - let testsPaths: string[]; + let testsPath = userConfig.paths?.tests; // TODO: use isObject when the type narrowing issue is fixed - if ( - typeof userConfig.paths?.tests === "object" && - userConfig.paths.tests.solidity !== undefined - ) { - if (Array.isArray(userConfig.paths.tests.solidity)) { - testsPaths = userConfig.paths.tests.solidity; - } else { - testsPaths = [userConfig.paths.tests.solidity]; - } - } else { - testsPaths = resolvedConfig.paths.sources.solidity; - } - - const resolvedPaths = testsPaths.map((p) => - resolveFromRoot(resolvedConfig.paths.root, p), - ); + testsPath = + typeof testsPath === "object" ? testsPath.solidity : testsPath; + testsPath ??= "test"; return { ...resolvedConfig, @@ -63,7 +50,7 @@ export default async (): Promise> => { ...resolvedConfig.paths, tests: { ...resolvedConfig.paths.tests, - solidity: resolvedPaths, + solidity: resolveFromRoot(resolvedConfig.paths.root, testsPath), }, }, }; diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/task-action.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/task-action.ts index be72c52439..23e5070ccd 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/task-action.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/task-action.ts @@ -5,11 +5,7 @@ import type { NewTaskActionFunction } from "../../../types/tasks.js"; import { finished } from "node:stream/promises"; -import { - getAllFilesMatching, - isDirectory, -} from "@ignored/hardhat-vnext-utils/fs"; -import { resolveFromRoot } from "@ignored/hardhat-vnext-utils/path"; +import { getAllFilesMatching } from "@ignored/hardhat-vnext-utils/fs"; import { createNonClosingWriter } from "@ignored/hardhat-vnext-utils/stream"; import { shouldMergeCompilationJobs } from "../solidity/build-profiles.js"; @@ -28,32 +24,25 @@ interface TestActionArguments { } const runSolidityTests: NewTaskActionFunction = async ( - { timeout, noCompile }, + { timeout }, hre, ) => { - const rootFilePaths = ( - await Promise.all( - hre.config.paths.tests.solidity - .map((p) => resolveFromRoot(hre.config.paths.root, p)) - .map(async (p) => { - // NOTE: The paths specified in the `paths.tests.solidity` array - // can be either directories or files. - if (await isDirectory(p)) { - return getAllFilesMatching(p, (f) => f.endsWith(".sol")); - } else if (p.endsWith(".sol") === true) { - return [p]; - } else { - return []; - } - }), - ) + const rootSourceFilePaths = await hre.solidity.getRootFilePaths(); + // NOTE: A test file is either a file with a `.sol` extension in the `tests.solidity` + // directory or a file with a `.t.sol` extension in the `sources.solidity` directory + const rootTestFilePaths = ( + await Promise.all([ + getAllFilesMatching(hre.config.paths.tests.solidity, (f) => + f.endsWith(".sol"), + ), + ...hre.config.paths.sources.solidity.map(async (dir) => { + return getAllFilesMatching(dir, (f) => f.endsWith(".t.sol")); + }), + ]) ).flat(1); const buildOptions: BuildOptions = { - // NOTE: The uncached sources will still be compiled event if `noCompile` - // is true. We could consider adding a `cacheOnly` option to support true - // `noCompile` behavior. - force: !noCompile, + force: false, buildProfile: hre.globalOptions.buildProfile, mergeCompilationJobs: shouldMergeCompilationJobs( hre.globalOptions.buildProfile, @@ -61,6 +50,8 @@ const runSolidityTests: NewTaskActionFunction = async ( quiet: false, }; + // NOTE: We compile all the sources together with the tests + const rootFilePaths = [...rootSourceFilePaths, ...rootTestFilePaths]; const results = await hre.solidity.build(rootFilePaths, buildOptions); throwIfSolidityBuildFailed(results); @@ -68,7 +59,7 @@ const runSolidityTests: NewTaskActionFunction = async ( const artifacts = await getArtifacts(results, hre.config.paths.artifacts); const testSuiteIds = await getTestSuiteIds( artifacts, - rootFilePaths, + rootTestFilePaths, hre.config.paths.root, ); diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/type-extensions.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/type-extensions.ts index 72564ccc48..85abcead6d 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/type-extensions.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/type-extensions.ts @@ -2,10 +2,10 @@ import "@ignored/hardhat-vnext/types/config"; declare module "@ignored/hardhat-vnext/types/config" { export interface TestPathsUserConfig { - solidity?: string | string[]; + solidity?: string; } export interface TestPathsConfig { - solidity: string[]; + solidity: string; } } diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts index d4e17181c9..cf5acc396e 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts @@ -78,7 +78,10 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { const localFilesToCompile = ( await Promise.all( this.#options.soliditySourcesPaths.map((dir) => - getAllFilesMatching(dir, (f) => f.endsWith(".sol")), + getAllFilesMatching( + dir, + (f) => f.endsWith(".sol") && !f.endsWith(".t.sol"), + ), ), ) ).flat(1); diff --git a/v-next/hardhat/test/internal/core/config-validation.ts b/v-next/hardhat/test/internal/core/config-validation.ts index 27849516bd..551af370db 100644 --- a/v-next/hardhat/test/internal/core/config-validation.ts +++ b/v-next/hardhat/test/internal/core/config-validation.ts @@ -1,5 +1,8 @@ import type { HardhatUserConfig } from "../../../src/config.js"; -import type { ProjectPathsUserConfig } from "../../../src/types/config.js"; +import type { + ProjectPathsUserConfig, + TestPathsUserConfig, +} from "../../../src/types/config.js"; import type { HardhatPlugin } from "../../../src/types/plugins.js"; import type { EmptyTaskDefinition, @@ -1247,7 +1250,8 @@ describe("config validation", function () { it("should work when the tests and sources properties are both objects", async function () { // Objects are not validated because they are customizable by the user const paths: ProjectPathsUserConfig = { - tests: { randomProperty: "randomValue" }, + /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- testing validations for js users who can bypass type checks */ + tests: { randomProperty: "randomValue" } as TestPathsUserConfig, sources: {}, }; diff --git a/v-next/hardhat/test/internal/core/hook-manager.ts b/v-next/hardhat/test/internal/core/hook-manager.ts index 648a0054de..7b57b3ade1 100644 --- a/v-next/hardhat/test/internal/core/hook-manager.ts +++ b/v-next/hardhat/test/internal/core/hook-manager.ts @@ -258,7 +258,7 @@ describe("HookManager", () => { givenHre: HardhatRuntimeEnvironment, ): Promise => { givenHre.config.paths.tests = { - solidity: ["./test-folder-from-plugin1"], + solidity: "./test-folder-from-plugin1", }; }, } as Partial; @@ -279,7 +279,7 @@ describe("HookManager", () => { givenHre: HardhatRuntimeEnvironment, ): Promise => { givenHre.config.paths.tests = { - solidity: ["./test-folder-from-overriding-plugin2"], + solidity: "./test-folder-from-overriding-plugin2", }; }, } as Partial; @@ -304,7 +304,7 @@ describe("HookManager", () => { assert.equal(result.length, 2); assert.equal( - hre.config.paths.tests.solidity[0], + hre.config.paths.tests.solidity, "./test-folder-from-overriding-plugin2", ); }); From c882e69421c7a86e8c0404e882bf1f0a1d5ed019 Mon Sep 17 00:00:00 2001 From: galargh Date: Tue, 7 Jan 2025 14:28:59 +0100 Subject: [PATCH 6/7] chore: move common helpers from solidity-test to solidity plugin --- .../builtin-plugins/solidity-test/helpers.ts | 92 ------------------ .../solidity-test/task-action.ts | 6 +- .../builtin-plugins/solidity/build-results.ts | 95 +++++++++++++++++++ .../builtin-plugins/solidity/tasks/compile.ts | 24 +---- 4 files changed, 100 insertions(+), 117 deletions(-) create mode 100644 v-next/hardhat/src/internal/builtin-plugins/solidity/build-results.ts diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/helpers.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/helpers.ts index 807811926c..2866988aca 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/helpers.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/helpers.ts @@ -1,12 +1,3 @@ -import type { - Artifact as HardhatArtifact, - BuildInfo, -} from "../../../types/artifacts.js"; -import type { - CompilationJobCreationError, - FailedFileBuildResult, - FileBuildResult, -} from "../../../types/solidity/build-system.js"; import type { ArtifactId as EdrArtifactId, Artifact as EdrArtifact, @@ -14,89 +5,6 @@ import type { import path from "node:path"; -import { HardhatError } from "@ignored/hardhat-vnext-errors"; -import { readJsonFile } from "@ignored/hardhat-vnext-utils/fs"; - -import { FileBuildResultType } from "../../../types/solidity/build-system.js"; - -type SolidityBuildResults = - | Map - | CompilationJobCreationError; -type SuccessfulSolidityBuildResults = Map< - string, - Exclude ->; - -/** - * This function asserts that the given Solidity build results are successful. - * It throws a HardhatError if the build results indicate that the compilation - * job failed. - */ -export function throwIfSolidityBuildFailed( - results: SolidityBuildResults, -): asserts results is SuccessfulSolidityBuildResults { - if ("reason" in results) { - throw new HardhatError( - HardhatError.ERRORS.SOLIDITY.COMPILATION_JOB_CREATION_ERROR, - { - reason: results.formattedReason, - rootFilePath: results.rootFilePath, - buildProfile: results.buildProfile, - }, - ); - } - - const sucessful = [...results.values()].every( - ({ type }) => - type === FileBuildResultType.CACHE_HIT || - type === FileBuildResultType.BUILD_SUCCESS, - ); - - if (!sucessful) { - throw new HardhatError(HardhatError.ERRORS.SOLIDITY.BUILD_FAILED); - } -} - -/** - * This function returns the artifacts generated during the compilation associated - * with the given Solidity build results. It relies on the fact that each successful - * build result has a corresponding artifact generated property. - */ -export async function getArtifacts( - results: SuccessfulSolidityBuildResults, - artifactsRootPath: string, -): Promise { - const artifacts: EdrArtifact[] = []; - - for (const [source, result] of results.entries()) { - for (const artifactPath of result.contractArtifactsGenerated) { - const artifact: HardhatArtifact = await readJsonFile(artifactPath); - const buildInfo: BuildInfo = await readJsonFile( - path.join(artifactsRootPath, "build-info", `${result.buildId}.json`), - ); - - const id = { - name: artifact.contractName, - solcVersion: buildInfo.solcVersion, - source, - }; - - const contract = { - abi: JSON.stringify(artifact.abi), - bytecode: artifact.bytecode, - deployedBytecode: artifact.deployedBytecode, - }; - - artifacts.push({ - id, - contract, - }); - } - } - - return artifacts; -} - /** * This function returns the test suite ids associated with the given artifacts. * The test suite ID is the relative path of the test file, relative to the diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/task-action.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/task-action.ts index 23e5070ccd..ad7703b74a 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/task-action.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/task-action.ts @@ -9,12 +9,12 @@ import { getAllFilesMatching } from "@ignored/hardhat-vnext-utils/fs"; import { createNonClosingWriter } from "@ignored/hardhat-vnext-utils/stream"; import { shouldMergeCompilationJobs } from "../solidity/build-profiles.js"; - import { getArtifacts, - getTestSuiteIds, throwIfSolidityBuildFailed, -} from "./helpers.js"; +} from "../solidity/build-results.js"; + +import { getTestSuiteIds } from "./helpers.js"; import { testReporter } from "./reporter.js"; import { run } from "./runner.js"; diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-results.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-results.ts new file mode 100644 index 0000000000..40520c4ba8 --- /dev/null +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-results.ts @@ -0,0 +1,95 @@ +import type { + Artifact as HardhatArtifact, + BuildInfo, +} from "../../../types/artifacts.js"; +import type { Artifact as EdrArtifact } from "@ignored/edr"; + +import path from "node:path"; + +import { HardhatError } from "@ignored/hardhat-vnext-errors"; +import { readJsonFile } from "@ignored/hardhat-vnext-utils/fs"; + +import { + FileBuildResultType, + type CompilationJobCreationError, + type FailedFileBuildResult, + type FileBuildResult, +} from "../../../types/solidity.js"; + +type SolidityBuildResults = + | Map + | CompilationJobCreationError; +type SuccessfulSolidityBuildResults = Map< + string, + Exclude +>; + +/** + * This function asserts that the given Solidity build results are successful. + * It throws a HardhatError if the build results indicate that the compilation + * job failed. + */ +export function throwIfSolidityBuildFailed( + results: SolidityBuildResults, +): asserts results is SuccessfulSolidityBuildResults { + if ("reason" in results) { + throw new HardhatError( + HardhatError.ERRORS.SOLIDITY.COMPILATION_JOB_CREATION_ERROR, + { + reason: results.formattedReason, + rootFilePath: results.rootFilePath, + buildProfile: results.buildProfile, + }, + ); + } + + const sucessful = [...results.values()].every( + ({ type }) => + type === FileBuildResultType.CACHE_HIT || + type === FileBuildResultType.BUILD_SUCCESS, + ); + + if (!sucessful) { + throw new HardhatError(HardhatError.ERRORS.SOLIDITY.BUILD_FAILED); + } +} + +/** + * This function returns the artifacts generated during the compilation associated + * with the given Solidity build results. It relies on the fact that each successful + * build result has a corresponding artifact generated property. + */ +export async function getArtifacts( + results: SuccessfulSolidityBuildResults, + artifactsRootPath: string, +): Promise { + const artifacts: EdrArtifact[] = []; + + for (const [source, result] of results.entries()) { + for (const artifactPath of result.contractArtifactsGenerated) { + const artifact: HardhatArtifact = await readJsonFile(artifactPath); + const buildInfo: BuildInfo = await readJsonFile( + path.join(artifactsRootPath, "build-info", `${result.buildId}.json`), + ); + + const id = { + name: artifact.contractName, + solcVersion: buildInfo.solcVersion, + source, + }; + + const contract = { + abi: JSON.stringify(artifact.abi), + bytecode: artifact.bytecode, + deployedBytecode: artifact.deployedBytecode, + }; + + artifacts.push({ + id, + contract, + }); + } + } + + return artifacts; +} diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/tasks/compile.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/tasks/compile.ts index 0bb657db05..b4e341f25d 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/tasks/compile.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/tasks/compile.ts @@ -1,10 +1,9 @@ import type { NewTaskActionFunction } from "../../../../types/tasks.js"; -import { HardhatError } from "@ignored/hardhat-vnext-errors"; import { resolveFromRoot } from "@ignored/hardhat-vnext-utils/path"; -import { FileBuildResultType } from "../../../../types/solidity.js"; import { shouldMergeCompilationJobs } from "../build-profiles.js"; +import { throwIfSolidityBuildFailed } from "../build-results.js"; import { isNpmRootPath } from "../build-system/root-paths-utils.js"; interface CompileActionArguments { @@ -37,26 +36,7 @@ const compileAction: NewTaskActionFunction = async ( quiet, }); - if ("reason" in results) { - throw new HardhatError( - HardhatError.ERRORS.SOLIDITY.COMPILATION_JOB_CREATION_ERROR, - { - reason: results.formattedReason, - rootFilePath: results.rootFilePath, - buildProfile: results.buildProfile, - }, - ); - } - - const sucessful = [...results.values()].every( - ({ type }) => - type === FileBuildResultType.CACHE_HIT || - type === FileBuildResultType.BUILD_SUCCESS, - ); - - if (!sucessful) { - throw new HardhatError(HardhatError.ERRORS.SOLIDITY.BUILD_FAILED); - } + throwIfSolidityBuildFailed(results); // If we recompiled the entire project we cleanup the artifacts if (files.length === 0) { From f222cd935c2c59d87bef77d0a310e570d8fa7526 Mon Sep 17 00:00:00 2001 From: galargh Date: Tue, 7 Jan 2025 14:46:32 +0100 Subject: [PATCH 7/7] feat: add force and quiet flags to solidity test task --- .../src/internal/builtin-plugins/solidity-test/index.ts | 8 ++++++-- .../builtin-plugins/solidity-test/task-action.ts | 9 +++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/index.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/index.ts index 4770891eb7..e3f63852fc 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/index.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/index.ts @@ -21,8 +21,12 @@ const hardhatPlugin: HardhatPlugin = { defaultValue: 60 * 60 * 1000, }) .addFlag({ - name: "noCompile", - description: "Don't compile the project before running the tests", + name: "force", + description: "Force compilation even if no files have changed", + }) + .addFlag({ + name: "quiet", + description: "Makes the compilation process less verbose", }) .build(), ], diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/task-action.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/task-action.ts index ad7703b74a..987e87a577 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity-test/task-action.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity-test/task-action.ts @@ -20,11 +20,12 @@ import { run } from "./runner.js"; interface TestActionArguments { timeout: number; - noCompile: boolean; + force: boolean; + quiet: boolean; } const runSolidityTests: NewTaskActionFunction = async ( - { timeout }, + { timeout, force, quiet }, hre, ) => { const rootSourceFilePaths = await hre.solidity.getRootFilePaths(); @@ -42,12 +43,12 @@ const runSolidityTests: NewTaskActionFunction = async ( ).flat(1); const buildOptions: BuildOptions = { - force: false, + force, buildProfile: hre.globalOptions.buildProfile, mergeCompilationJobs: shouldMergeCompilationJobs( hre.globalOptions.buildProfile, ), - quiet: false, + quiet, }; // NOTE: We compile all the sources together with the tests