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 all 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
24 changes: 9 additions & 15 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,14 @@ 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_).
## c8 report

For the best experience, try running with [a canary build of Node.js](https://github.com/v8/node).
run `c8 report` to regenerate reports after `c8` has already been run.

## 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).
## Disclaimer

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`.
33 changes: 19 additions & 14 deletions bin/c8.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,36 @@
const foreground = require('foreground-child')
const mkdirp = require('mkdirp')
const report = require('../lib/report')
const {resolve} = require('path')
const rimraf = require('rimraf')
const sw = require('spawn-wrap')
const {
hideInstrumenteeArgs,
hideInstrumenterArgs,
yargs
} = require('../lib/parse-args')

const instrumenterArgs = hideInstrumenteeArgs()
let argv = yargs.parse(instrumenterArgs)

const argv = yargs.parse(instrumenterArgs)
if (argv._[0] === 'report') {
argv = yargs.parse(process.argv) // support flag arguments after "report".
outputReport()
} else {
rimraf.sync(argv.tempDirectory)
mkdirp.sync(argv.tempDirectory)
process.env.NODE_V8_COVERAGE = argv.tempDirectory

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

sw([require.resolve('../lib/wrap')], {
C8_ARGV: JSON.stringify(argv)
})
foreground(hideInstrumenterArgs(argv), () => {
outputReport()
})
}

foreground(hideInstrumenterArgs(argv), (out) => {
function outputReport () {
report({
include: argv.include,
exclude: argv.exclude,
reporter: Array.isArray(argv.reporter) ? argv.reporter : [argv.reporter],
coverageDirectory: argv.coverageDirectory,
watermarks: argv.watermarks
tempDirectory: argv.tempDirectory,
watermarks: argv.watermarks,
resolve: argv.resolve
})
})
}
17 changes: 13 additions & 4 deletions lib/parse-args.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
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')

const configPath = findUp.sync(['.c8rc', '.c8rc.json'])
const config = configPath ? JSON.parse(readFileSync(configPath)) : {}

yargs
yargs()
.usage('$0 [opts] [script] [opts]')
.option('reporter', {
alias: 'r',
Expand All @@ -17,17 +17,26 @@ yargs
.option('exclude', {
alias: 'x',
default: Exclude.defaultExclude,
describe: 'a list of specific files and directories that should be excluded from coverage, glob patterns are supported.'
describe: 'a list of specific files and directories that should be excluded from coverage (glob patterns are supported)'
})
.option('include', {
alias: 'n',
default: [],
describe: 'a list of specific files that should be covered, glob patterns are supported'
describe: 'a list of specific files that should be covered (glob patterns are supported)'
})
.option('coverage-directory', {
default: './coverage',
describe: 'directory to output coverage JSON and reports'
})
.option('temp-directory', {
default: './coverage/tmp',
describe: 'directory V8 coverage data is written to and read from'
})
.option('resolve', {
default: '',
describe: 'resolve paths to alternate base directory'
})
.command('report', 'read V8 coverage data from temp and output report')
.pkgConf('c8')
.config(config)
.demandCommand(1)
Expand Down
52 changes: 43 additions & 9 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, isAbsolute } = require('path')
const v8CoverageMerge = require('v8-coverage-merge')
const v8toIstanbul = require('v8-to-istanbul')

class Report {
constructor ({reporter, coverageDirectory, watermarks}) {
constructor ({
exclude,
include,
reporter,
tempDirectory,
watermarks,
resolve
}) {
this.reporter = reporter
this.coverageDirectory = coverageDirectory
this.tempDirectory = tempDirectory
this.watermarks = watermarks
this.resolve = resolve
this.exclude = Exclude({
exclude: exclude,
include: include
})
}
run () {
const map = this._getCoverageMapFromAllCoverageFiles()
Expand All @@ -25,20 +40,39 @@ class Report {
}
_getCoverageMapFromAllCoverageFiles () {
const map = libCoverage.createCoverageMap({})
const mergedResults = {}
this._loadReports().forEach((report) => {
report.result.forEach((result) => {
if (this.exclude.shouldInstrument(result.url) &&
isAbsolute(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]
const path = resolve(this.resolve, result.url)
const script = v8toIstanbul(path)
script.applyCoverage(result.functions)
map.merge(script.toIstanbul())
})

return map
}
_loadReports () {
const tmpDirctory = resolve(this.coverageDirectory, './tmp')
const files = readdirSync(tmpDirctory)
const files = readdirSync(this.tempDirectory)

return files.map((f) => {
return JSON.parse(readFileSync(
resolve(tmpDirctory, f),
resolve(this.tempDirectory, f),
'utf8'
))
})
Expand Down
81 changes: 0 additions & 81 deletions lib/wrap.js

This file was deleted.

Loading