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

Add --parallel, --bail and --grep params to the test task #2359

Merged
merged 26 commits into from
Feb 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
b97808f
Upgrade mocha to latest
fvictorio Feb 9, 2022
de16fcc
Add --parallel flag
fvictorio Feb 9, 2022
f498fc7
Fix autocomplete test
fvictorio Feb 9, 2022
ee68191
Run tests in parallel in e2e tests
fvictorio Feb 15, 2022
368c8bf
Add --bail and --grep params to test task
fvictorio Feb 15, 2022
c972d67
Add docs on running tests in parallel
fvictorio Feb 15, 2022
983f554
Merge branch 'master' into parallel-tests
fvictorio Feb 15, 2022
345ebab
Create hip-planes-invent.md
fvictorio Feb 15, 2022
ac001a4
Fix autocomplete tests
fvictorio Feb 15, 2022
30c1b7d
Update docs/guides/waffle-testing.md
fvictorio Feb 18, 2022
057dcdf
Update docs/guides/waffle-testing.md
fvictorio Feb 18, 2022
fe479e0
Improve explanation about potential timeouts
fvictorio Feb 18, 2022
b399960
Update packages/hardhat-core/src/builtin-tasks/test.ts
fvictorio Feb 18, 2022
7feb7f3
Update packages/hardhat-core/src/builtin-tasks/test.ts
fvictorio Feb 18, 2022
19df511
Fix linter
fvictorio Feb 18, 2022
bf6417e
Move parallel tests docs to their own guide
fvictorio Feb 18, 2022
60def3a
Force type imports to be at the beginning
fvictorio Feb 18, 2022
b74ee2e
Merge branch 'master' into parallel-tests
fvictorio Feb 18, 2022
84b070c
Add tests for test task
fvictorio Feb 18, 2022
3917ebe
Fix issue with false flags overriding mocha config
fvictorio Feb 18, 2022
3047a83
Fix linter
fvictorio Feb 18, 2022
8ba2070
Fix autocomplete test
fvictorio Feb 21, 2022
36411fd
Change how parallel mode is tested
fvictorio Feb 21, 2022
2aa70d4
Clarify how the HRE is shared in parallel tests doc
fvictorio Feb 21, 2022
7e8208a
Make SourceReference serializable
fvictorio Feb 22, 2022
7a6d522
Merge branch 'master' into parallel-tests
fvictorio Feb 22, 2022
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
7 changes: 7 additions & 0 deletions .changeset/hip-planes-invent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"hardhat": minor
---

The `test` task now supports a `--parallel` flag to run tests in parallel. There are also two other new flags: `--bail`, to stop the execution after the first test failure, and `--grep`, to filter which tests should be run.

To support running tests in parallel, the version of `mocha` used by Hardhat was upgraded to its latest version. This should be a mostly backward-compatible change, but there could be some edge cases where this breaks existing tests.
1 change: 1 addition & 0 deletions config/eslint/eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ module.exports = {
"error",
{
groups: [
"type",
"object",
["builtin", "external"],
"parent",
Expand Down
1 change: 1 addition & 0 deletions docs/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ module.exports = {
["/guides/compile-contracts.md", "Compiling your contracts", 0],
["/guides/waffle-testing.md", "Testing with ethers.js & Waffle", 0],
["/guides/truffle-testing.md", "Testing with Web3.js & Truffle", 0],
["/guides/parallel-tests.md", "Running tests in parallel", 0],
["/guides/truffle-migration.md", "Migrating from Truffle", 0],
["/guides/deploying.md", "Deploying your contracts", 0],
["/guides/scripts.md", "Writing scripts", 0],
Expand Down
16 changes: 16 additions & 0 deletions docs/guides/parallel-tests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Running tests in parallel

You can run your tests in parallel by using the `--parallel` flag:

```
$ npx hardhat test --parallel
```

Most of the time, running your tests serially or in parallel should produce the same results, but there are some scenarios where tests run in parallel will behave differently:

- In serial mode all the test files share the same instance of the [Hardhat Runtime Environment](/advanced/hardhat-runtime-environment.html), but in parallel mode this is not always the case. Mocha uses a pool of workers to execute the tests, and each worker starts with its own instance of the HRE. This means that if one test file deploys a contract, then that deployment will exist in some of the other test files and it won't in others.
- The `.only` modifier doesn't work in parallel mode. As an alternative, you can use [`--grep`](https://mochajs.org/#-grep-regexp-g-regexp) to run specific tests.
- Because parallel mode uses more system resources, the duration of individual tests might be longer, so there's a chance that some tests start timing out for that reason. If you run into this problem, you can increase the tests timeout in the [Mocha section of your Hardhat config](/config/#mocha-configuration) or using [`this.timeout()`](https://mochajs.org/#timeouts) in your tests.
- The order in which tests are executed is non-deterministic.

There are some other limitations related to parallel mode. You can read more about them in [Mocha's docs](https://mochajs.org/#parallel-tests). And if you are running into some issue when using parallel mode, you can check their [Troubleshooting parallel mode](https://mochajs.org/#troubleshooting-parallel-mode) section.
4 changes: 2 additions & 2 deletions packages/e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"dependencies": {
"@types/chai": "^4.2.0",
"@types/fs-extra": "^5.1.0",
"@types/mocha": "^9.0.0",
"@types/mocha": "^9.1.0",
"@types/node": "^12.0.0",
"@types/shelljs": "^0.8.6",
"@typescript-eslint/eslint-plugin": "4.29.2",
Expand All @@ -32,7 +32,7 @@
"eslint-plugin-import": "2.24.1",
"eslint-plugin-prettier": "3.4.0",
"fs-extra": "^7.0.1",
"mocha": "^7.2.0",
"mocha": "^9.2.0",
"prettier": "2.4.1",
"rimraf": "^3.0.2",
"shelljs": "^0.8.5",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
describe("simple", () => {
it("should pass", () => {});
});
21 changes: 20 additions & 1 deletion packages/e2e/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ describe("e2e tests", function () {
// check stdout

// check we get passing runs
assert.match(stdout, /1 passing/);
assert.match(stdout, /2 passing/);
// check we get no runs without tests
assert.notMatch(
stdout,
Expand Down Expand Up @@ -84,6 +84,25 @@ describe("e2e tests", function () {
);
assert.equal(testRunCode2, 0);
});

it("should run tests in parallel", function () {
// hh clean
const { code: hhCleanCode1 } = shell.exec(`${hardhatBinary} clean`);
assert.equal(hhCleanCode1, 0);

// hh test --parallel
const { code: hhCompileCode, stdout } = shell.exec(
`${hardhatBinary} test --parallel`
);
assert.equal(hhCompileCode, 0);

// check we get passing runs
assert.match(stdout, /2 passing/);

// hh clean
const { code: hhCleanCode2 } = shell.exec(`${hardhatBinary} clean`);
assert.equal(hhCleanCode2, 0);
});
});

describe("sample projects", function () {
Expand Down
6 changes: 3 additions & 3 deletions packages/hardhat-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
"@types/fs-extra": "^5.1.0",
"@types/glob": "^7.1.1",
"@types/lodash": "^4.14.123",
"@types/mocha": "^9.0.0",
"@types/mocha": "^9.1.0",
"@types/node": "^12.0.0",
"@types/node-fetch": "^2.3.7",
"@types/qs": "^6.5.3",
Expand All @@ -81,7 +81,7 @@
"eslint-plugin-import": "2.24.1",
"eslint-plugin-prettier": "3.4.0",
"ethers": "^5.0.0",
"mocha": "^7.2.0",
"mocha": "^9.2.0",
"prettier": "2.4.1",
"proxy": "^1.0.2",
"rimraf": "^3.0.2",
Expand Down Expand Up @@ -124,7 +124,7 @@
"lodash": "^4.17.11",
"merkle-patricia-tree": "^4.2.2",
"mnemonist": "^0.38.0",
"mocha": "^7.2.0",
"mocha": "^9.2.0",
"node-fetch": "^2.6.0",
"qs": "^6.7.0",
"raw-body": "^2.4.1",
Expand Down
3 changes: 2 additions & 1 deletion packages/hardhat-core/src/builtin-tasks/node.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type EthereumjsUtilT from "ethereumjs-util";

import chalk from "chalk";
import debug from "debug";
import type EthereumjsUtilT from "ethereumjs-util";
import fsExtra from "fs-extra";

import { HARDHAT_NETWORK_NAME } from "../internal/constants";
Expand Down
74 changes: 64 additions & 10 deletions packages/hardhat-core/src/builtin-tasks/test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { MochaOptions } from "mocha";
alcuadrado marked this conversation as resolved.
Show resolved Hide resolved

import chalk from "chalk";
import path from "path";

Expand Down Expand Up @@ -47,24 +49,61 @@ subtask(TASK_TEST_GET_TEST_FILES)
subtask(TASK_TEST_SETUP_TEST_ENVIRONMENT, async () => {});

subtask(TASK_TEST_RUN_MOCHA_TESTS)
.addFlag("parallel", "Run tests in parallel")
.addFlag("bail", "Stop running tests after the first test failure")
.addOptionalParam(
"grep",
"Only run tests matching the given string or regexp"
)
.addOptionalVariadicPositionalParam(
"testFiles",
"An optional list of files to test",
[]
)
.setAction(async ({ testFiles }: { testFiles: string[] }, { config }) => {
const { default: Mocha } = await import("mocha");
const mocha = new Mocha(config.mocha);
testFiles.forEach((file) => mocha.addFile(file));
.setAction(
async (
taskArgs: {
bail: boolean;
parallel: boolean;
testFiles: string[];
grep?: string;
},
{ config }
) => {
const { default: Mocha } = await import("mocha");

const testFailures = await new Promise<number>((resolve) => {
mocha.run(resolve);
});
const mochaConfig: MochaOptions = {
...config.mocha,
grep: taskArgs.grep,
};

mocha.dispose();
if (taskArgs.bail) {
mochaConfig.bail = true;
}
if (taskArgs.parallel) {
mochaConfig.parallel = true;
}

return testFailures;
});
if (mochaConfig.parallel === true) {
const mochaRequire = mochaConfig.require ?? [];
if (!mochaRequire.includes("hardhat/register")) {
mochaRequire.push("hardhat/register");
}
mochaConfig.require = mochaRequire;
}

const mocha = new Mocha(mochaConfig);
taskArgs.testFiles.forEach((file) => mocha.addFile(file));

const testFailures = await new Promise<number>((resolve) => {
mocha.run(resolve);
});

mocha.dispose();

return testFailures;
}
);

subtask(TASK_TEST_RUN_SHOW_FORK_RECOMMENDATIONS).setAction(
async (_, { config, network }) => {
Expand All @@ -84,14 +123,26 @@ task(TASK_TEST, "Runs mocha tests")
[]
)
.addFlag("noCompile", "Don't compile before running this task")
.addFlag("parallel", "Run tests in parallel")
.addFlag("bail", "Stop running tests after the first test failure")
.addOptionalParam(
"grep",
"Only run tests matching the given string or regexp"
)
.setAction(
async (
{
testFiles,
noCompile,
parallel,
bail,
grep,
}: {
testFiles: string[];
noCompile: boolean;
parallel: boolean;
bail: boolean;
grep?: string;
},
{ run, network }
) => {
Expand All @@ -107,6 +158,9 @@ task(TASK_TEST, "Runs mocha tests")

const testFailures = await run(TASK_TEST_RUN_MOCHA_TESTS, {
testFiles: files,
parallel,
bail,
grep,
});

if (network.name === HARDHAT_NETWORK_NAME) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import type { LoDashStatic } from "lodash";
import type { ProjectPathsConfig, SolcConfig } from "../../types";

import debug from "debug";
import fsExtra from "fs-extra";
import * as t from "io-ts";
import type { LoDashStatic } from "lodash";
import * as path from "path";

import { SOLIDITY_FILES_CACHE_FILENAME } from "../../internal/constants";
import type { ProjectPathsConfig, SolcConfig } from "../../types";

const log = debug("hardhat:core:tasks:compile:cache");

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type StackTraceParserT from "stacktrace-parser";

import chalk from "chalk";
import debug from "debug";
import fsExtra from "fs-extra";
import path from "path";
import semver from "semver";
import type StackTraceParserT from "stacktrace-parser";

import { HardhatArguments, HardhatConfig } from "../../../types";
import { HardhatContext } from "../../context";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {
NetworkConfig,
ProjectPathsConfig,
} from "../../../types";

import { HARDHAT_NETWORK_NAME } from "../../constants";
import { ModulesLogger } from "../../hardhat-network/provider/modules/logger";
import {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type WsT from "ws";

import debug from "debug";
import http, { Server } from "http";
import { AddressInfo } from "net";
import type WsT from "ws";

import {
EIP1193Provider,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { ReturnData } from "./return-data";

import { Block } from "@ethereumjs/block";
import { RunBlockResult } from "@ethereumjs/vm/dist/runBlock";
import { BN } from "ethereumjs-util";
Expand All @@ -6,8 +8,6 @@ import { HARDHAT_MEMPOOL_SUPPORTED_ORDERS } from "../../constants";
import { BuildInfo, HardhatNetworkChainsConfig } from "../../../types";
import { MessageTrace } from "../stack-traces/message-trace";

import type { ReturnData } from "./return-data";

export type NodeConfig = LocalNodeConfig | ForkedNodeConfig;

export function isForkedNodeConfig(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
import Common from "@ethereumjs/common";
import chalk from "chalk";
import debug from "debug";
import { BN } from "ethereumjs-util";
import { EventEmitter } from "events";
import fsExtra from "fs-extra";
import semver from "semver";

import type {
Artifacts,
BoundExperimentalHardhatNetworkMessageTraceHook,
Expand All @@ -14,6 +6,15 @@ import type {
HardhatNetworkChainsConfig,
RequestArguments,
} from "../../../types";

import Common from "@ethereumjs/common";
import chalk from "chalk";
import debug from "debug";
import { BN } from "ethereumjs-util";
import { EventEmitter } from "events";
import fsExtra from "fs-extra";
import semver from "semver";

import {
HARDHAT_NETWORK_RESET_EVENT,
HARDHAT_NETWORK_REVERT_SNAPSHOT_EVENT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ function flattenSourceReference(sourceReference?: SourceReference) {

return {
...sourceReference,
file: sourceReference.file.sourceName,
file: sourceReference.sourceName,
};
}

Expand Down
Loading