-
Notifications
You must be signed in to change notification settings - Fork 493
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
Solidity code coverage #692
Conversation
39291e5
to
22d809b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💯 very nice, just a few comments about code style:
- Usage of forEach instead of reduce (there is others than just my 2 comments)
- Usage of
var
instead oflet
} | ||
|
||
// +1 here factors in newline characters. | ||
this.lineOffsets[line] = this.lineOffsets[line-1] + this.lineLengths[line-1] + 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could it be: this.lineOffsets[line] = 2 * this.lineLengths[line-1] + 1;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good eyes, just realized that!
this.lineCount = this.lineLengths.length; | ||
|
||
this.lineOffsets = []; | ||
this.lineLengths.forEach((length, line) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you could use reduce
here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reduce, as far as I know, only returns one value, right?
lib/modules/solidity/index.js
Outdated
@@ -90,6 +107,10 @@ class Solidity { | |||
} | |||
} | |||
} | |||
|
|||
//self.plugins.emitAndRunActionsForEvent('contracts:compiled:solc', output); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for this comment right?
lib/modules/coverage/index.js
Outdated
compileSolc(input) { | ||
var sources = {}; | ||
|
||
Object.keys(input.sources).forEach((path) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could also use reduce
here I believe
test/coverage.js
Outdated
|
||
var trace = JSON.parse(loadFixture('geth-debugtrace-output-h-5.json')); | ||
var coverage = cs.generateCodeCoverage(trace); | ||
// dumpToFile(coverage, '/tmp/coverage.json'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about this comment?
Thanks for the review @alaibe! I think |
|
||
this.lineOffsets = []; | ||
this.lineLengths.forEach((length, line) => { | ||
if(line == 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Friendly reminder that we try to always use ===. I added it to eslint... but only for area-51 🤦♂️
break; | ||
} | ||
|
||
for(var i = locations.start.line; i < this.lineCount; i++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't you merge these two loops together and add mre ifs to detect the end and start?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did this originally but it was hard to make sense of what was going on. You'll notice that I offset the second loop so that we're not doing any unnecessary iterations anyway, so this is as optimized as it gets.
// or surpass the offset for last character. | ||
if(!locations.end) { | ||
var lastLine = this.lineCount - 1; | ||
locations.end = {line: lastLine, column: this.lineLengths[lastLine]}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can just do this.lineLengths[this.lineCount - 1]
The lastLine variable is a bit redundant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use it twice so I figured I'd declare a variable above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, sorry, my bad.
locations.end = {line: lastLine, column: this.lineLengths[lastLine]}; | ||
} | ||
|
||
// Istanbul likes lines to be 1-indexed, so we'll increment here before returning. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's weird, do you know why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, as per the coverage.json spec:
Location objects are a
{start: {line, column}, end: {line, column}}
object that describes the start and end of a piece of code. Note that line is 1-based, but column is 0-based.
} | ||
} | ||
|
||
/*eslint complexity: ["error", 27]*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Holy, that's a big function. Is there a way you could split it?
parseSolcOutput(output) { | ||
for(var file in output.contracts) { | ||
var basename = path.basename(file); | ||
var contractSource = this.files[basename]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
basename is redundant. Can just do this.files[path.basename(file)];
|
||
for(var file in this.files) { | ||
var contractSource = this.files[file]; | ||
coverageReport[file] = contractSource.generateCodeCoverage(trace); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for contractSource
. Can just do this.files[file].generateCodeCoverage(trace);
|
||
process.on('exit', (_code) => { | ||
const coverageReportPath = fs.dappPath('.embark', 'coverage.json'); | ||
fs.writeFileSync(coverageReportPath, JSON.stringify(self.coverageReport)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't this create a race condition where your file write might not finish before the exit?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not if it's sync. It'll wait for that to finish.
lib/modules/coverage/source_map.js
Outdated
|
||
subtract(sourceMap) { | ||
var length = sourceMap.offset - this.offset; | ||
return new SourceMap(this.offset, length); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Redundant variable length
@@ -120,6 +120,7 @@ class Test { | |||
ipcRole: 'client' | |||
}); | |||
this.events.request('deploy:setGasLimit', 6000000); | |||
this.engine.startService("codeCoverage"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Picky comment. Can you move this one line up. That way all service starts are in one block.
Really nice stuff. I feel like we could/should create an individual repository for the solidity code coverage part and publish it. I feel like it could be a really good standalone tool. |
Overview
TL;DR
Added an output of a code coverage report for Solidity
Details
This PR brings in code coverage for Solidity in Embark. It does so by registering a new module ("coverage") that will listen to compiler, and block events, and request traces from Geth with the executed paths. For this effect, it outputs
<dapp_root>/.embark/coverage.json
after a dApp's tests run.One thing to note is that, due to the lack of information from the transaction and trace request, we have to do some "matching" of the bytecode to the actual contract/method being executed. This is a bit of a naive approach for now, so I'll keep an eye out on a better approach to reach this goal.
PR with
embark coverage
command will follow.