@@ -6,6 +6,7 @@ const istanbul = require('istanbul');
66const util = require ( 'util' ) ;
77const assert = require ( 'assert' ) ;
88const detect = require ( 'detect-port' ) ;
9+ const _ = require ( 'lodash/lang' ) ;
910
1011const ConfigValidator = require ( './validator' ) ;
1112const Instrumenter = require ( './instrumenter' ) ;
@@ -17,7 +18,7 @@ const AppUI = require('./ui').AppUI;
1718 * Coverage Runner
1819 */
1920class API {
20- constructor ( config ) {
21+ constructor ( config = { } ) {
2122 this . coverage = new Coverage ( ) ;
2223 this . instrumenter = new Instrumenter ( ) ;
2324 this . validator = new ConfigValidator ( )
@@ -54,7 +55,8 @@ class API {
5455 this . gasLimitString = "0xfffffffffff" ; // block gas limit for ganache (higher than "gas sent")
5556 this . gasPrice = 0x01 ;
5657
57- this . istanbulReporter = config . istanbulReporter || [ 'html' , 'lcov' , 'text' ] ;
58+ this . istanbulFolder = config . istanbulFolder || false ;
59+ this . istanbulReporter = config . istanbulReporter || [ 'html' , 'lcov' , 'text' , 'json' ] ;
5860
5961 this . setLoggingLevel ( config . silent ) ;
6062 this . ui = new AppUI ( this . log ) ;
@@ -65,21 +67,12 @@ class API {
6567 * Instruments a set of sources to prepare them for running under coverage
6668 * @param {Object[] } targets (see below)
6769 * @return {Object[] } (see below)
68- * @example :
69- *
70- * targets:
71- * [{
72- * canonicalPath: <absolute-path>
73- * relativePath: <relative-path>
74- * source: <source-file>
75- *
76- * },...]
77- *
78- * outputs:
79- * [{
80- * canonicalPath: <path>
81- * source: <instrumented-source-file>
82- * }...]
70+ * @example of input/output array:
71+ * [{
72+ * source: (required) <solidity-source>,
73+ * canonicalPath: (required) <absolute path to source file>
74+ * relativePath: (optional) <rel path to source file for logging>
75+ * }]
8376 */
8477 instrument ( targets = [ ] ) {
8578 let currentFile ; // Keep track of filename in case we crash...
@@ -95,7 +88,7 @@ class API {
9588 this . ui . report ( 'instr-start' ) ;
9689 }
9790
98- this . ui . report ( 'instr-item' , [ target . relativePath ] ) ;
91+ this . ui . report ( 'instr-item' , [ currentFile ] ) ;
9992
10093 const instrumented = this . instrumenter . instrument (
10194 target . source ,
@@ -119,22 +112,35 @@ class API {
119112 return outputs ;
120113 }
121114
115+ /**
116+ * Returns a copy of the hit map created during instrumentation.
117+ * Useful if you'd like to delegate coverage collection to multiple processes.
118+ * @return {Object } instrumentationData
119+ */
120+ getInstrumentationData ( ) {
121+ return _ . cloneDeep ( this . instrumenter . instrumentationData )
122+ }
123+
124+ /**
125+ * Sets the hit map object generated during instrumentation. Useful if you'd like
126+ * to collect data for a pre-existing instrumentation.
127+ * @param {Object } data
128+ */
129+ setInstrumentationData ( data = { } ) {
130+ this . instrumenter . instrumentationData = _ . cloneDeep ( data ) ;
131+ }
132+
122133 /**
123134 * Launches an in-process ethereum client server, hooking the DataCollector to its VM.
124135 * @param {Object } client ganache client
125136 * @return {String } address of server to connect to
126137 */
127138 async ganache ( client ) {
128- let retry = false ;
129- let address = `http://${ this . host } :${ this . port } ` ;
130-
131139 // Check for port-in-use
132140 if ( await detect ( this . port ) !== this . port ) {
133141 throw new Error ( this . ui . generate ( 'server-fail' , [ this . port ] ) )
134142 }
135143
136- if ( ! this . client ) this . client = client ; // Prefer client from options
137-
138144 this . collector = new DataCollector ( this . instrumenter . instrumentationData ) ;
139145
140146 this . providerOptions . gasLimit = this . gasLimitString ;
@@ -143,16 +149,17 @@ class API {
143149 // Launch server and attach to vm step of supplied client
144150 try {
145151 if ( this . config . forceBackupServer ) throw new Error ( )
146- await this . attachToVM ( )
152+ await this . attachToVM ( client )
147153 }
148154
149- // Fallback to ganache-core-sc (eq: ganache-core 2.7.0 )
155+ // Fallback to ganache-cli )
150156 catch ( err ) {
151- this . ui . report ( 'vm-fail' , [ ] ) ;
152- this . client = require ( 'ganache-core-sc' ) ;
153- await this . attachToVM ( ) ;
157+ const _ganache = require ( 'ganache-cli' ) ;
158+ this . ui . report ( 'vm-fail' , [ _ganache . version ] ) ;
159+ await this . attachToVM ( _ganache ) ;
154160 }
155161
162+ const address = `http://${ this . host } :${ this . port } ` ;
156163 this . ui . report ( 'server' , [ address ] ) ;
157164 return address ;
158165 }
@@ -162,7 +169,7 @@ class API {
162169 */
163170 async report ( ) {
164171 const collector = new istanbul . Collector ( ) ;
165- const reporter = new istanbul . Reporter ( ) ;
172+ const reporter = new istanbul . Reporter ( false , this . istanbulFolder ) ;
166173
167174 return new Promise ( ( resolve , reject ) => {
168175 try {
@@ -177,7 +184,8 @@ class API {
177184
178185 // Pify doesn't like this one...
179186 reporter . write ( collector , true , ( err ) => {
180- if ( err ) throw err ;
187+ if ( err ) return reject ( err ) ;
188+
181189 this . ui . report ( 'istanbul' ) ;
182190 resolve ( ) ;
183191 } ) ;
@@ -204,9 +212,11 @@ class API {
204212 // ========
205213 // Provider
206214 // ========
207- async attachToVM ( ) {
215+ async attachToVM ( client ) {
208216 const self = this ;
209217
218+ // Prefer client from options
219+ if ( ! this . client ) this . client = client ;
210220 this . server = this . client . server ( this . providerOptions ) ;
211221
212222 this . assertHasBlockchain ( this . server . provider ) ;
@@ -225,7 +235,6 @@ class API {
225235 return vm ;
226236 }
227237
228- // NB: EADDRINUSE errors are uncatch-able?
229238 await pify ( this . server . listen ) ( this . port ) ;
230239 }
231240
0 commit comments