Skip to content

Commit d0f188f

Browse files
authored
Add autoLauchServer option / update config validator (#437)
1 parent 367ef81 commit d0f188f

File tree

5 files changed

+85
-16
lines changed

5 files changed

+85
-16
lines changed

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ truffle run coverage [command-options]
4141
## Command Options
4242
| Option <img width=200/> | Example <img width=750/>| Description <img width=1000/> |
4343
|--------------|------------------------------------|--------------------------------|
44-
| file (Truffle) | `--file="test/registry/*.js"` | Filename or glob describing a subset of JS tests to run. (Globs must be enclosed by quotes.)|
45-
| testFiles (Buidler) | `--testFiles test/file.js` | JS test file(s) to run.|
44+
| file | `--file="test/registry/*.js"` | (Truffle) Filename or glob describing a subset of JS tests to run. (Globs must be enclosed by quotes.)|
45+
| testFiles | `--testFiles test/file.js` | (Buidler) JS test file(s) to run.|
4646
| solcoverjs | `--solcoverjs ./../.solcover.js` | Relative path from working directory to config. Useful for monorepo packages that share settings. (Path must be "./" prefixed) |
4747
| network | `--network development` | Use network settings defined in the Truffle or Buidler config |
4848
| temp[<sup>*</sup>][14] | `--temp build` | :warning: **Caution** :warning: Path to a *disposable* folder to store compilation artifacts in. Useful when your test setup scripts include hard-coded paths to a build directory. [More...][14] |
@@ -69,12 +69,12 @@ module.exports = {
6969
| providerOptions | *Object* | `{ }` | [ganache-core options][1] |
7070
| skipFiles | *Array* | `['Migrations.sol']` | Array of contracts or folders (with paths expressed relative to the `contracts` directory) that should be skipped when doing instrumentation. |
7171
| istanbulFolder | *String* | `./coverage` | Folder location for Istanbul coverage reports. |
72-
| istanbulReporter | *Array* | `['html', 'lcov', 'text']` | [Istanbul coverage reporters][2] |
72+
| istanbulReporter | *Array* | `['html', 'lcov', 'text', 'json']` | [Istanbul coverage reporters][2] |
7373
| mocha | *Object* | `{ }` | [Mocha options][3] to merge into existing mocha config. `grep` and `invert` are useful for skipping certain tests under coverage using tags in the test descriptions.|
7474
| onServerReady[<sup>*</sup>][14] | *Function* | | Hook run *after* server is launched, *before* the tests execute. Useful if you need to use the Oraclize bridge or have setup scripts which rely on the server's availability. [More...][23] |
7575
| onCompileComplete[<sup>*</sup>][14] | *Function* | | Hook run *after* compilation completes, *before* tests are run. Useful if you have secondary compilation steps or need to modify built artifacts. [More...][23]|
76-
| onTestsComplete[<sup>*</sup>][14] | *Function* | | Hook run *after* the tests complete, *before* Istanbul reports are generated.|
77-
| onIstanbulComplete[<sup>*</sup>][14] | *Function* | | Hook run *after* the Istanbul reports are generated, *before* the ganache server is shut down. Useful if you need to clean resources up.|
76+
| onTestsComplete[<sup>*</sup>][14] | *Function* | | Hook run *after* the tests complete, *before* Istanbul reports are generated. [More...][23]|
77+
| onIstanbulComplete[<sup>*</sup>][14] | *Function* | | Hook run *after* the Istanbul reports are generated, *before* the ganache server is shut down. Useful if you need to clean resources up. [More...][23]|
7878

7979
[<sup>*</sup> Advanced use][14]
8080

lib/api.js

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class API {
4646
this.port = config.port || this.defaultPort;
4747
this.host = config.host || "127.0.0.1";
4848
this.providerOptions = config.providerOptions || {};
49+
this.autoLaunchServer = config.autoLaunchServer === false ? false : true;
4950

5051
this.skipFiles = config.skipFiles || [];
5152

@@ -60,7 +61,6 @@ class API {
6061

6162
this.setLoggingLevel(config.silent);
6263
this.ui = new AppUI(this.log);
63-
6464
}
6565

6666
/**
@@ -131,11 +131,15 @@ class API {
131131
}
132132

133133
/**
134-
* Launches an in-process ethereum client server, hooking the DataCollector to its VM.
135-
* @param {Object} client ganache client
136-
* @return {String} address of server to connect to
134+
* Enables coverage collection on in-process ethereum client server, hooking the DataCollector
135+
* to its VM. By default, method will return a url after server has begun listening on the port
136+
* specified in the config. When `autoLaunchServer` is false, method returns`ganache.server` so
137+
* the consumer can control the 'server.listen' invocation themselves.
138+
* @param {Object} client ganache client
139+
* @param {Boolean} autoLaunchServer boolean
140+
* @return {String | Server} address of server to connect to, or initialized, unlaunched server.
137141
*/
138-
async ganache(client){
142+
async ganache(client, autoLaunchServer){
139143
// Check for port-in-use
140144
if (await detect(this.port) !== this.port){
141145
throw new Error(this.ui.generate('server-fail', [this.port]))
@@ -146,7 +150,7 @@ class API {
146150
this.providerOptions.gasLimit = this.gasLimitString;
147151
this.providerOptions.allowUnlimitedContractSize = true;
148152

149-
// Launch server and attach to vm step of supplied client
153+
// Attach to vm step of supplied client
150154
try {
151155
if (this.config.forceBackupServer) throw new Error()
152156
await this.attachToVM(client)
@@ -159,6 +163,11 @@ class API {
159163
await this.attachToVM(_ganache);
160164
}
161165

166+
if (autoLaunchServer === false || this.autoLaunchServer === false){
167+
return this.server;
168+
}
169+
170+
await pify(this.server.listen)(this.port);
162171
const address = `http://${this.host}:${this.port}`;
163172
this.ui.report('server', [address]);
164173
return address;
@@ -234,8 +243,6 @@ class API {
234243
vm.on('step', self.collector.step.bind(self.collector));
235244
return vm;
236245
}
237-
238-
await pify(this.server.listen)(this.port);
239246
}
240247

241248
assertHasBlockchain(provider){

lib/validator.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@ const configSchema = {
1818
port: {type: "number"},
1919
providerOptions: {type: "object"},
2020
silent: {type: "boolean"},
21+
autoLaunchServer: {type: "boolean"},
22+
istanbulFolder: {type: "string"},
2123

2224
// Hooks:
2325
onServerReady: {type: "function", format: "isFunction"},
26+
onCompileComplete: {type: "function", format: "isFunction"},
2427
onTestComplete: {type: "function", format: "isFunction"},
2528
onIstanbulComplete: {type: "function", format: "isFunction"},
2629

test/units/api.js

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
const assert = require('assert');
22
const util = require('./../util/util.js');
33
const API = require('./../../lib/api.js');
4+
const detect = require('detect-port');
5+
const Ganache = require('ganache-cli');
46

57
describe('api', () => {
6-
const opts = {silent: true};
8+
let opts;
9+
10+
beforeEach(() => opts = {silent: true})
711

812
it('getInstrumentationData', function(){
913
const api = new API(opts);
@@ -51,4 +55,34 @@ describe('api', () => {
5155
const cloneC = api.getInstrumentationData();
5256
assert(cloneC[hash].hits === 5);
5357
});
58+
59+
it('ganache: autoLaunchServer === false', async function(){
60+
const api = new API(opts);
61+
const port = api.port;
62+
const server = await api.ganache(Ganache, false);
63+
64+
assert(typeof port === 'number')
65+
assert(typeof server === 'object');
66+
assert(typeof server.listen === 'function');
67+
68+
const freePort = await detect(port);
69+
70+
assert(freePort === port);
71+
});
72+
73+
it('config: autoLaunchServer: false', async function(){
74+
opts.autoLaunchServer = false;
75+
76+
const api = new API(opts);
77+
const port = api.port;
78+
const server = await api.ganache(Ganache);
79+
80+
assert(typeof port === 'number')
81+
assert(typeof server === 'object');
82+
assert(typeof server.listen === 'function');
83+
84+
const freePort = await detect(port);
85+
86+
assert(freePort === port);
87+
})
5488
})

test/units/validator.js

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ describe('config validation', () => {
2222
const options = [
2323
"cwd",
2424
"host",
25+
"istanbulFolder"
2526
]
2627

2728
options.forEach(name => {
@@ -41,6 +42,29 @@ describe('config validation', () => {
4142
});
4243
});
4344

45+
it('validates the "boolean" options', function(){
46+
const options = [
47+
"silent",
48+
"autoLaunchServer",
49+
]
50+
51+
options.forEach(name => {
52+
// Pass
53+
solcoverjs = {};
54+
solcoverjs[name] = false;
55+
assert(validator.validate(solcoverjs), `${name} boolean should be valid`)
56+
57+
// Fail
58+
solcoverjs[name] = "false";
59+
try {
60+
validator.validate(solcoverjs);
61+
assert.fail()
62+
} catch (err){
63+
assert(err.message.includes(`"${name}" is not of a type(s) boolean`), err.message);
64+
}
65+
});
66+
});
67+
4468
it('validates the "object" options', function(){
4569
const options = [
4670
"client",
@@ -86,7 +110,7 @@ describe('config validation', () => {
86110
});
87111
});
88112

89-
it('validates string array options', function(){
113+
it('validates the "string[]" options', function(){
90114
const options = [
91115
"skipFiles",
92116
"istanbulReporter",
@@ -109,9 +133,10 @@ describe('config validation', () => {
109133
});
110134
});
111135

112-
it('validates function options', function(){
136+
it('validates the "function" options', function(){
113137

114138
const options = [
139+
"onCompileComplete",
115140
"onServerReady",
116141
"onTestComplete",
117142
"onIstanbulComplete",

0 commit comments

Comments
 (0)