diff options
author | Leonid Logvinov <logvinov.leon@gmail.com> | 2018-03-10 13:07:55 +0800 |
---|---|---|
committer | Leonid Logvinov <logvinov.leon@gmail.com> | 2018-03-12 10:37:30 +0800 |
commit | bd7517cfd489a9789f81c247fb45329881274d15 (patch) | |
tree | 9542a28ddb23f971330cc5c2b6088a4e4e102792 /packages/sol-cov | |
parent | 9bffce9dc55030a3901b7be61a5dbbdc80d09a4b (diff) | |
download | dexon-sol-tools-bd7517cfd489a9789f81c247fb45329881274d15.tar.gz dexon-sol-tools-bd7517cfd489a9789f81c247fb45329881274d15.tar.zst dexon-sol-tools-bd7517cfd489a9789f81c247fb45329881274d15.zip |
Add support for async calls under coverage
Diffstat (limited to 'packages/sol-cov')
-rw-r--r-- | packages/sol-cov/package.json | 3 | ||||
-rw-r--r-- | packages/sol-cov/src/coverage_subprovider.ts | 52 |
2 files changed, 39 insertions, 16 deletions
diff --git a/packages/sol-cov/package.json b/packages/sol-cov/package.json index f80e186ca..6a0b61ce9 100644 --- a/packages/sol-cov/package.json +++ b/packages/sol-cov/package.json @@ -22,10 +22,11 @@ "dependencies": { "@0xproject/subproviders": "^0.7.0", "@0xproject/utils": "^0.3.4", - "glob": "^7.1.2", "ethereumjs-util": "^5.1.1", + "glob": "^7.1.2", "istanbul": "^0.4.5", "lodash": "^4.17.4", + "semaphore-async-await": "^1.5.1", "solidity-coverage": "^0.4.10", "solidity-parser-sc": "^0.4.4", "web3": "^0.20.0" diff --git a/packages/sol-cov/src/coverage_subprovider.ts b/packages/sol-cov/src/coverage_subprovider.ts index f91b95c79..c84211d3a 100644 --- a/packages/sol-cov/src/coverage_subprovider.ts +++ b/packages/sol-cov/src/coverage_subprovider.ts @@ -1,21 +1,32 @@ import { Callback, NextCallback, Subprovider } from '@0xproject/subproviders'; import { promisify } from '@0xproject/utils'; import * as _ from 'lodash'; +import { Lock } from 'semaphore-async-await'; import * as Web3 from 'web3'; import { constants } from './constants'; import { CoverageManager } from './coverage_manager'; import { TraceInfoExistingContract, TraceInfoNewContract } from './types'; +interface MaybeFakeTxData extends Web3.TxData { + isFakeTransaction?: boolean; +} + /* * This class implements the web3-provider-engine subprovider interface and collects traces of all transactions that were sent and all calls that were executed. + * Because there is no notion of call trace in the rpc - we collect them in rather non-obvious/hacky way. + * On each call - we create a snapshot, execute the call as a transaction, get the trace, revert the snapshot. + * That allows us to not influence the test behaviour. * Source: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js */ export class CoverageSubprovider extends Subprovider { + // Lock is used to not accept normal transactions while doing call/snapshot magic because they'll be reverted later otherwise + private _lock: Lock; private _coverageManager: CoverageManager; private _defaultFromAddress: string; constructor(artifactsPath: string, sourcesPath: string, networkId: number, defaultFromAddress: string) { super(); + this._lock = new Lock(); this._defaultFromAddress = defaultFromAddress; this._coverageManager = new CoverageManager( artifactsPath, @@ -50,11 +61,16 @@ export class CoverageSubprovider extends Subprovider { await this._coverageManager.writeCoverageAsync(); } private async _onTransactionSentAsync( - txData: Web3.TxData, + txData: MaybeFakeTxData, err: Error | null, - txHash?: string, - cb?: Callback, + txHash: string | undefined, + cb: Callback, ): Promise<void> { + if (!txData.isFakeTransaction) { + // This transaction is a usual ttransaction. Not a call executed as one. + // And we don't want it to be executed within a snapshotting period + await this._lock.acquire(); + } if (_.isNull(err)) { await this._recordTxTraceAsync(txData.to || constants.NEW_CONTRACT, txData.data, txHash as string); } else { @@ -72,9 +88,12 @@ export class CoverageSubprovider extends Subprovider { ); } } - if (!_.isUndefined(cb)) { - cb(); + if (!txData.isFakeTransaction) { + // This transaction is a usual ttransaction. Not a call executed as one. + // And we don't want it to be executed within a snapshotting period + await this._lock.release(); } + cb(); } private async _onCallExecutedAsync( callData: Partial<Web3.CallData>, @@ -115,22 +134,25 @@ export class CoverageSubprovider extends Subprovider { } } private async _recordCallTraceAsync(callData: Partial<Web3.CallData>, blockNumber: Web3.BlockParam): Promise<void> { + // We don't want other transactions to be exeucted during snashotting period, that's why we lock the + // transaction execution for all transactions except our fake ones. + await this._lock.acquire(); const snapshotId = Number((await this.emitPayloadAsync({ method: 'evm_snapshot' })).result); - const txData = callData; - if (_.isUndefined(txData.from)) { - txData.from = this._defaultFromAddress; - } - const txDataWithFromAddress = txData as Web3.TxData; + const fakeTxData: MaybeFakeTxData = { + from: this._defaultFromAddress, + isFakeTransaction: true, // This transaction (and only it) is allowed to come through when the lock is locked + ...callData, + }; try { - const txHash = (await this.emitPayloadAsync({ + await this.emitPayloadAsync({ method: 'eth_sendTransaction', - params: [txDataWithFromAddress], - })).result; - await this._onTransactionSentAsync(txDataWithFromAddress, null, txHash); + params: [fakeTxData], + }); } catch (err) { - await this._onTransactionSentAsync(txDataWithFromAddress, err, undefined); + // Even if this transaction failed - we've already recorded it's trace. } const jsonRPCResponse = await this.emitPayloadAsync({ method: 'evm_revert', params: [snapshotId] }); + await this._lock.release(); const didRevert = jsonRPCResponse.result; if (!didRevert) { throw new Error('Failed to revert the snapshot'); |