Skip to content
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

feat: switch to using Node's built in coverage #22

Merged
merged 5 commits into from
Sep 10, 2018
Merged
Show file tree
Hide file tree
Changes from 3 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
22 changes: 6 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# c8 - native v8 code-coverage
# c8 - native V8 code-coverage

Code-coverage using [v8's Inspector](https://nodejs.org/dist/latest-v8.x/docs/api/inspector.html)
Code-coverage using [V8's Inspector](https://nodejs.org/dist/latest-v8.x/docs/api/inspector.html)
that's compatible with [Istanbul's reporters](https://istanbul.js.org/docs/advanced/alternative-reporters/).

Like [nyc](https://github.com/istanbuljs/nyc), c8 just magically works:
Expand All @@ -10,20 +10,10 @@ npm i c8 -g
c8 node foo.js
```

The above example will collect coverage for `foo.js` using v8's inspector.
The above example will collect coverage for `foo.js` using V8's inspector.

## Disclaimer

c8 uses bleeding edge v8 features (_it's an ongoing experiment, testing
what will eventually be possible in the realm of test coverage in Node.js_).

For the best experience, try running with [a canary build of Node.js](https://github.com/v8/node).

## How it Works

Before running your application c8 creates [an inspector session](https://nodejs.org/api/inspector.html) in v8 and enables v8's
[built in coverage reporting](https://v8project.blogspot.com/2017/12/javascript-code-coverage.html).

Just before your application exits, c8 fetches the coverage information from
v8 and writes it to disk in a format compatible with
[Istanbul's reporters](https://istanbul.js.org/).
c8 uses
[bleeding edge Node.js features](https://github.com/nodejs/node/pull/22527),
make sure you're running Node.js `>= 10.10.0`.
15 changes: 6 additions & 9 deletions bin/c8.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,28 @@
const foreground = require('foreground-child')
const mkdirp = require('mkdirp')
const report = require('../lib/report')
const {resolve} = require('path')
const { resolve } = require('path')
const rimraf = require('rimraf')
const sw = require('spawn-wrap')
const {
hideInstrumenteeArgs,
hideInstrumenterArgs,
yargs
} = require('../lib/parse-args')

const instrumenterArgs = hideInstrumenteeArgs()

const argv = yargs.parse(instrumenterArgs)

const tmpDirctory = resolve(argv.coverageDirectory, './tmp')
rimraf.sync(tmpDirctory)
mkdirp.sync(tmpDirctory)

sw([require.resolve('../lib/wrap')], {
C8_ARGV: JSON.stringify(argv)
})
process.env.NODE_V8_COVERAGE = tmpDirctory

foreground(hideInstrumenterArgs(argv), (out) => {
report({
include: argv.include,
exclude: argv.exclude,
reporter: Array.isArray(argv.reporter) ? argv.reporter : [argv.reporter],
coverageDirectory: argv.coverageDirectory,
watermarks: argv.watermarks
watermarks: argv.watermarks,
resolve: argv.resolve
})
})
6 changes: 5 additions & 1 deletion lib/parse-args.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const Exclude = require('test-exclude')
const findUp = require('find-up')
const {readFileSync} = require('fs')
const { readFileSync } = require('fs')
const yargs = require('yargs')
const parser = require('yargs-parser')

Expand Down Expand Up @@ -28,6 +28,10 @@ yargs
default: './coverage',
describe: 'directory to output coverage JSON and reports'
})
.option('resolve', {
default: '',
describe: 'resolve paths to alternate base directory'
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will be required for the Node.js codebase itself.

})
.pkgConf('c8')
.config(config)
.demandCommand(1)
Expand Down
50 changes: 45 additions & 5 deletions lib/report.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
const Exclude = require('test-exclude')
const libCoverage = require('istanbul-lib-coverage')
const libReport = require('istanbul-lib-report')
const reports = require('istanbul-reports')
const {readdirSync, readFileSync} = require('fs')
const {resolve} = require('path')
const { readdirSync, readFileSync } = require('fs')
const { resolve } = require('path')
const v8CoverageMerge = require('v8-coverage-merge')
const v8toIstanbul = require('v8-to-istanbul')

class Report {
constructor ({reporter, coverageDirectory, watermarks}) {
constructor ({
exclude,
include,
reporter,
coverageDirectory,
watermarks,
resolve
}) {
this.reporter = reporter
this.coverageDirectory = coverageDirectory
this.watermarks = watermarks
this.resolve = resolve
this.exclude = Exclude({
exclude: exclude,
include: include
})
}
run () {
const map = this._getCoverageMapFromAllCoverageFiles()
Expand All @@ -25,9 +40,34 @@ class Report {
}
_getCoverageMapFromAllCoverageFiles () {
const map = libCoverage.createCoverageMap({})
const mergedResults = {}
this._loadReports().forEach((report) => {
report.result.forEach((result) => {
if (this.exclude.shouldInstrument(result.url)) {
if (mergedResults[result.url]) {
mergedResults[result.url] = v8CoverageMerge(
mergedResults[result.url],
result
)
} else {
mergedResults[result.url] = result
}
}
})
})

this._loadReports().forEach(function (report) {
map.merge(report)
Object.keys(mergedResults).forEach((url) => {
const result = mergedResults[url]
// console.info(JSON.stringify(result, null, 2))
try {
const path = resolve(this.resolve, result.url)
const script = v8toIstanbul(path)
script.applyCoverage(result.functions)
map.merge(script.toIstanbul())
} catch (err) {
// most likely this was an internal Node.js library.
if (err.code !== 'ENOENT' && err.code !== 'EISDIR') throw err
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TimothyGu any thoughts about how to easily tell whether the coverage outputted was for one of Node.js' built in libraries, e.g., internal/inspector.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about checking whether the URL was an absolute path or not?

}
})

return map
Expand Down
81 changes: 0 additions & 81 deletions lib/wrap.js

This file was deleted.

Loading