Skip to content

Use Hardhat client and ethers in unit tests #847

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

Merged
merged 2 commits into from
Feb 4, 2024
Merged
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
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
"@nomiclabs/hardhat-truffle5": "^2.0.0",
"@nomiclabs/hardhat-waffle": "^2.0.1",
"@nomiclabs/hardhat-web3": "^2.0.0",
"@truffle/contract": "4.0.36",
"chai": "^4.3.4",
"decache": "^4.5.1",
"ethereum-waffle": "^3.4.0",
Expand Down
1 change: 1 addition & 0 deletions scripts/ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ SILENT=true node --max-old-space-size=4096 \
-- \
mocha \
test/units/* test/integration/* \
--require "test/util/mochaRootHook.js" \
--timeout 100000 \
--no-warnings \
--exit \
1 change: 1 addition & 0 deletions scripts/unit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ node --max-old-space-size=4096 \
--exclude '**/test/**/' \
-- \
mocha test/units/* \
--require "test/util/mochaRootHook.js" \
--timeout 100000 \
--no-warnings \
--exit
49 changes: 20 additions & 29 deletions test/units/assert.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,23 @@
const assert = require('assert');
const util = require('./../util/util.js');

const client = require('ganache-cli');
const Coverage = require('./../../lib/coverage');
const Api = require('./../../lib/api')

describe('asserts and requires', () => {
let coverage;
let api;

before(async () => {
api = new Api({silent: true});
await api.ganache(client);
})
before(async () => api = new Api({silent: true}));
beforeEach(() => coverage = new Coverage());
after(async() => await api.finish());

// Assert was covered as a branch up to v0.7.11. But since those
// conditions are never meant to be fullfilled (and assert is really for smt)
// people disliked this...
it('should *not* cover assert statements as branches (pass)', async function() {
const contract = await util.bootstrapCoverage('assert/Assert', api);
const contract = await util.bootstrapCoverage('assert/Assert', api, this.provider);
coverage.addContract(contract.instrumented, util.filePath);
await contract.instance.a(true);
await contract.instance.a(true, contract.gas);
const mapping = coverage.generate(contract.data, util.pathPrefix);

assert.deepEqual(mapping[util.filePath].l, {
Expand All @@ -37,31 +32,29 @@ describe('asserts and requires', () => {
});
});

// NB: truffle/contract replays failing txs as .calls to obtain the revert reason from the return
// data. Hence the 2X measurements.
it('should *not* cover assert statements as branches (fail)', async function() {
const contract = await util.bootstrapCoverage('assert/Assert', api);
const contract = await util.bootstrapCoverage('assert/Assert', api, this.provider);
coverage.addContract(contract.instrumented, util.filePath);

try { await contract.instance.a(false) } catch(err) { /* Invalid opcode */ }
try { await contract.instance.a(false, contract.gas) } catch(err) { /* Invalid opcode */ }

const mapping = coverage.generate(contract.data, util.pathPrefix);
assert.deepEqual(mapping[util.filePath].l, {
5: 2,
5: 1,
});
assert.deepEqual(mapping[util.filePath].b, {});
assert.deepEqual(mapping[util.filePath].s, {
1: 2,
1: 1,
});
assert.deepEqual(mapping[util.filePath].f, {
1: 2,
1: 1,
});
});

it('should cover multi-line require stmts as `if` statements when they pass', async function() {
const contract = await util.bootstrapCoverage('assert/RequireMultiline', api);
const contract = await util.bootstrapCoverage('assert/RequireMultiline', api, this.provider);
coverage.addContract(contract.instrumented, util.filePath);
await contract.instance.a(true, true, true);
await contract.instance.a(true, true, true, contract.gas);
const mapping = coverage.generate(contract.data, util.pathPrefix);

assert.deepEqual(mapping[util.filePath].l, {
Expand All @@ -78,34 +71,32 @@ describe('asserts and requires', () => {
});
});

// NB: Truffle replays failing txs as .calls to obtain the revert reason from the return
// data. Hence the 2X measurements.
it('should cover multi-line require stmts as `if` statements when they fail', async function() {
const contract = await util.bootstrapCoverage('assert/RequireMultiline', api);
const contract = await util.bootstrapCoverage('assert/RequireMultiline', api, this.provider);
coverage.addContract(contract.instrumented, util.filePath);

try { await contract.instance.a(true, true, false) } catch(err) { /* Revert */ }
try { await contract.instance.a(true, true, false, contract.gas) } catch(err) { /* Revert */ }

const mapping = coverage.generate(contract.data, util.pathPrefix);

assert.deepEqual(mapping[util.filePath].l, {
5: 2,
5: 1,
});
assert.deepEqual(mapping[util.filePath].b, {
1: [0, 2],
1: [0, 1],
});
assert.deepEqual(mapping[util.filePath].s, {
1: 2,
1: 1,
});
assert.deepEqual(mapping[util.filePath].f, {
1: 2,
1: 1,
});
});

it('should cover require statements with method arguments', async function() {
const contract = await util.bootstrapCoverage('assert/Require-fn', api);
const contract = await util.bootstrapCoverage('assert/Require-fn', api, this.provider);
coverage.addContract(contract.instrumented, util.filePath);
await contract.instance.a(true);
await contract.instance.a(true, contract.gas);
const mapping = coverage.generate(contract.data, util.pathPrefix);

assert.deepEqual(mapping[util.filePath].l, {
Expand All @@ -123,9 +114,9 @@ describe('asserts and requires', () => {
});

it('should cover require statements with method arguments & reason string', async function() {
const contract = await util.bootstrapCoverage('assert/Require-fn-reason', api);
const contract = await util.bootstrapCoverage('assert/Require-fn-reason', api, this.provider);
coverage.addContract(contract.instrumented, util.filePath);
await contract.instance.a(true);
await contract.instance.a(true, contract.gas);
const mapping = coverage.generate(contract.data, util.pathPrefix);

assert.deepEqual(mapping[util.filePath].l, {
Expand Down
37 changes: 16 additions & 21 deletions test/units/conditional.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,25 @@
const assert = require('assert');
const util = require('./../util/util.js');

const client = require('ganache-cli');
const Coverage = require('./../../lib/coverage');
const Api = require('./../../lib/api')

describe('ternary conditionals', () => {
let coverage;
let api;

before(async () => {
api = new Api({silent: true});
await api.ganache(client);
})
before(async () => api = new Api({silent: true}));
beforeEach(() => coverage = new Coverage());
after(async() => await api.finish());

async function setupAndRun(solidityFile){
const contract = await util.bootstrapCoverage(solidityFile, api);
async function setupAndRun(solidityFile, provider){
const contract = await util.bootstrapCoverage(solidityFile, api, provider);
coverage.addContract(contract.instrumented, util.filePath);
await contract.instance.a();
await contract.instance.a(contract.gas);
return coverage.generate(contract.data, util.pathPrefix);
}

it('should cover a conditional that reaches the consequent (same-line)', async function() {
const mapping = await setupAndRun('conditional/sameline-consequent');
const mapping = await setupAndRun('conditional/sameline-consequent', this.provider);

assert.deepEqual(mapping[util.filePath].l, {
5: 1, 6: 1, 7: 1,
Expand All @@ -41,7 +36,7 @@ describe('ternary conditionals', () => {
});

it('should cover an unbracketed conditional that reaches the consequent (same-line)', async function() {
const mapping = await setupAndRun('conditional/unbracketed-condition');
const mapping = await setupAndRun('conditional/unbracketed-condition', this.provider);

assert.deepEqual(mapping[util.filePath].l, {
5: 1, 6: 1, 7: 1,
Expand All @@ -58,7 +53,7 @@ describe('ternary conditionals', () => {
});

it('should cover a multi-part conditional (&&) that reaches the consequent', async function() {
const mapping = await setupAndRun('conditional/and-condition');
const mapping = await setupAndRun('conditional/and-condition', this.provider);

assert.deepEqual(mapping[util.filePath].l, {
5: 1, 6: 1, 7: 1,
Expand All @@ -75,7 +70,7 @@ describe('ternary conditionals', () => {
});

it('should cover a multi-part conditional (||) that reaches the consequent', async function() {
const mapping = await setupAndRun('conditional/or-condition');
const mapping = await setupAndRun('conditional/or-condition', this.provider);

assert.deepEqual(mapping[util.filePath].l, {
5: 1, 6: 1, 7: 1,
Expand All @@ -92,7 +87,7 @@ describe('ternary conditionals', () => {
});

it('should cover a multi-part unbracketed conditional (||) that reaches the consequent', async function() {
const mapping = await setupAndRun('conditional/unbracketed-or-condition');
const mapping = await setupAndRun('conditional/unbracketed-or-condition', this.provider);

assert.deepEqual(mapping[util.filePath].l, {
5: 1, 6: 1, 7: 1,
Expand All @@ -109,7 +104,7 @@ describe('ternary conditionals', () => {
});

it('should cover an always-false multi-part unbracketed conditional (||)', async function() {
const mapping = await setupAndRun('conditional/or-always-false-condition');
const mapping = await setupAndRun('conditional/or-always-false-condition', this.provider);

assert.deepEqual(mapping[util.filePath].l, {
5: 1, 6: 1, 7: 1,
Expand All @@ -126,7 +121,7 @@ describe('ternary conditionals', () => {
});

it('should cover a conditional that reaches the alternate (same-line)', async function() {
const mapping = await setupAndRun('conditional/sameline-alternate');
const mapping = await setupAndRun('conditional/sameline-alternate', this.provider);

assert.deepEqual(mapping[util.filePath].l, {
5: 1, 6: 1, 7: 1,
Expand All @@ -143,7 +138,7 @@ describe('ternary conditionals', () => {
});

it('should cover a conditional that reaches the consequent (multi-line)', async function() {
const mapping = await setupAndRun('conditional/multiline-consequent');
const mapping = await setupAndRun('conditional/multiline-consequent', this.provider);

assert.deepEqual(mapping[util.filePath].l, {
5: 1, 6: 1, 7: 1,
Expand All @@ -160,7 +155,7 @@ describe('ternary conditionals', () => {
});

it('should cover a conditional that reaches the alternate (multi-line)', async function() {
const mapping = await setupAndRun('conditional/multiline-alternate');
const mapping = await setupAndRun('conditional/multiline-alternate', this.provider);

assert.deepEqual(mapping[util.filePath].l, {
5: 1, 6: 1, 7: 1,
Expand All @@ -178,7 +173,7 @@ describe('ternary conditionals', () => {

// Runs bool z = (x) ? false : true;
it('should cover a definition assignment by conditional that reaches the alternate', async function() {
const mapping = await setupAndRun('conditional/declarative-exp-assignment-alternate');
const mapping = await setupAndRun('conditional/declarative-exp-assignment-alternate', this.provider);

assert.deepEqual(mapping[util.filePath].l, {
5: 1, 6: 1, 7: 1,
Expand All @@ -196,7 +191,7 @@ describe('ternary conditionals', () => {

// Runs z = (x) ? false : true;
it('should cover an identifier assignment by conditional that reaches the alternate', async function() {
const mapping = await setupAndRun('conditional/identifier-assignment-alternate');
const mapping = await setupAndRun('conditional/identifier-assignment-alternate', this.provider);

assert.deepEqual(mapping[util.filePath].l, {
5: 1, 6: 1, 7: 1, 8: 1,
Expand All @@ -213,7 +208,7 @@ describe('ternary conditionals', () => {
});

it('should cover an assignment to a member expression (reaches the alternate)', async function() {
const mapping = await setupAndRun('conditional/mapping-assignment');
const mapping = await setupAndRun('conditional/mapping-assignment', this.provider);

assert.deepEqual(mapping[util.filePath].l, {
11: 1, 12: 1,
Expand Down
15 changes: 5 additions & 10 deletions test/units/function.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
const assert = require('assert');
const util = require('./../util/util.js');

const client = require('ganache-cli');
const Coverage = require('./../../lib/coverage');
const Api = require('./../../lib/api')

describe('function declarations', () => {
let coverage;
let api;

before(async () => {
api = new Api({silent: true});
await api.ganache(client);
})
before(async () => api = new Api({silent: true}));
beforeEach(() => coverage = new Coverage());
after(async() => await api.finish());

Expand All @@ -37,9 +32,9 @@ describe('function declarations', () => {
});

it('should cover a simple invoked function call', async function() {
const contract = await util.bootstrapCoverage('function/function-call', api);
const contract = await util.bootstrapCoverage('function/function-call', api, this.provider);
coverage.addContract(contract.instrumented, util.filePath);
await contract.instance.a();
await contract.instance.a(contract.gas);
const mapping = coverage.generate(contract.data, util.pathPrefix);

assert.deepEqual(mapping[util.filePath].l, {
Expand All @@ -56,9 +51,9 @@ describe('function declarations', () => {
});

it('should cover a modifier used on a function', async function() {
const contract = await util.bootstrapCoverage('function/modifier', api);
const contract = await util.bootstrapCoverage('function/modifier', api, this.provider);
coverage.addContract(contract.instrumented, util.filePath);
await contract.instance.a(0);
await contract.instance.a(0, contract.gas);
const mapping = coverage.generate(contract.data, util.pathPrefix);

assert.deepEqual(mapping[util.filePath].l, {
Expand Down
Loading