Skip to content

Commit

Permalink
THIS IS A WIP COMMIT FOR REVIEW ONLY, DO NOT MERGE
Browse files Browse the repository at this point in the history
Docs and tests still TK.  Future commit message follows.

---

feat: add processinfo index, add externalId

If a NYC_PROCESSINFO_EXTERNAL_ID environment variable is set, then it is
saved in the processinfo as `externalId`.

BREAKING CHANGE: This adds a file named 'index.json' to the
.nyc_output/processinfo directory, which has a different format from the
other files in this dir.

Furthermore, when this file is generated, some additional helpful
metadata is memoized to the processinfo json files, to minimize the cost
of repeated generation.  (This isn't necessarily a breaking change, but
it is an update to the de facto schema for those files.)

As soon as possible, index generation and process tree display should be
migrated out to a new 'istanbul-lib-processinfo' library.

This opens the door to add features in the v14 release family to improve
support for partial/resumed test runs and file watching.

- When a process is run with --clean=false and a previously seen
  externalId, clear away all the coverage files in the set for that
  externalId.
- When a file is changed, a test runner can use the index to determine
  which tests (by externalId) ought to be re-run.
  • Loading branch information
isaacs committed Apr 4, 2019
1 parent 68d6333 commit e92d40e
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 1 deletion.
4 changes: 4 additions & 0 deletions bin/nyc.js
Expand Up @@ -65,6 +65,10 @@ if ([
), function (done) {
var mainChildExitCode = process.exitCode

if (argv.showProcessTree || argv.buildProcessTree) {
nyc.writeProcessIndex()
}

if (argv.checkCoverage) {
checkCoverage(argv)
process.exitCode = process.exitCode || mainChildExitCode
Expand Down
4 changes: 4 additions & 0 deletions bin/wrap.js
Expand Up @@ -12,6 +12,10 @@ config._processInfo = {
ppid: parentPid,
root: process.env.NYC_ROOT_ID
}
if (process.env.NYC_PROCESSINFO_EXTERNAL_ID) {
config._processInfo.externalId = process.env.NYC_PROCESSINFO_EXTERNAL_ID
delete process.env.NYC_PROCESSINFO_EXTERNAL_ID
}

;(new NYC(config)).wrap()

Expand Down
101 changes: 100 additions & 1 deletion index.js
Expand Up @@ -413,6 +413,105 @@ NYC.prototype.report = function () {
}
}

// XXX(@isaacs) Index generation should move to istanbul-lib-processinfo
NYC.prototype.writeProcessIndex = function () {
const dir = this.processInfoDirectory()
const pidToUid = new Map()
const infoByUid = new Map()
const eidToUid = new Map()
const infos = fs.readdirSync(dir).filter(f => f !== 'index.json').map(f => {
try {
const info = JSON.parse(fs.readFileSync(path.resolve(dir, f), 'utf-8'))
// on thiis first read pass, also map the pids to uuids
info.uuid = path.basename(f, '.json')
pidToUid.set(info.uuid, info.pid)
pidToUid.set(info.pid, info.uuid)
infoByUid.set(info.uuid, info)
if (info.externalId) {
eidToUid.set(info.externalId, info.uuid)
}
return info
} catch (er) {
return null
}
}).filter(Boolean)

// create all the parent-child links and write back the updated info
const needsUpdate = new Set()
infos.forEach(info => {
if (info.ppid && info.ppid !== '0' && !info.parent) {
info.parent = pidToUid.get(info.ppid)
needsUpdate.add(info)
}
if (info.parent) {
const parentInfo = infoByUid.get(info.parent)
if (parentInfo.children.indexOf(info.uuid) === -1) {
parentInfo.children.push(info.uuid)
needsUpdate.add(parentInfo)
}
}
})

// figure out which files were touched by each process.
const files = infos.reduce((files, info) => {
if (!info.files) {
try {
info.files = Object.keys(JSON.parse(fs.readFileSync(
path.resolve(this.tempDirectory(), info.coverageFilename),
'utf-8'
)))
} catch (er) {
return files
}
needsUpdate.add(info)
}
info.files.forEach(f => {
files[f] = files[f] || []
files[f].push(info.uuid)
})
return files
}, {})

// build the actual index!
const index = infos.reduce((index, info) => {
index.processes[info.uuid] = {}
if (info.parent) {
index.processes[info.uuid].parent = info.parent
}
if (info.externalId) {
index.processes[info.uuid].externalId = info.externalId
index.externalIds[info.externalId] = {
root: info.uuid,
children: info.children
}
}
if (info.children && info.children.length) {
index.processes[info.uuid].children = Array.from(info.children)
}
return index
}, { processes: {}, files: files, externalIds: {} })

// flatten the descendant sets of all the externalId procs
Object.keys(index.externalIds).forEach(eid => {
const { children } = index.externalIds[eid]
// push the next generation onto the list so we accumulate them all
for (let i = 0; i < children.length; i++) {
const nextGen = index.processes[children[i]].children
if (nextGen && nextGen.length) {
children.push(...nextGen.filter(uuid => children.indexOf(uuid) === -1))
}
}
})

needsUpdate.forEach(info => {
fs.writeFileSync(
path.resolve(dir, info.uuid + '.json'), JSON.stringify(info)
)
})

fs.writeFileSync(path.resolve(dir, 'index.json'), JSON.stringify(index))
}

NYC.prototype.showProcessTree = function () {
var processTree = ProcessInfo.buildProcessTree(this._loadProcessInfos())

Expand Down Expand Up @@ -452,7 +551,7 @@ NYC.prototype._loadProcessInfos = function () {
var _this = this
var files = fs.readdirSync(this.processInfoDirectory())

return files.map(function (f) {
return files.filter(f => f !== 'index.json').map(function (f) {
try {
return new ProcessInfo(JSON.parse(fs.readFileSync(
path.resolve(_this.processInfoDirectory(), f),
Expand Down
1 change: 1 addition & 0 deletions lib/process.js
Expand Up @@ -13,6 +13,7 @@ function ProcessInfo (defaults) {
this.root = null
this.coverageFilename = null
this.nodes = [] // list of children, filled by buildProcessTree()
this.children = [] // just uuids, not full nodes

this._coverageMap = null

Expand Down

0 comments on commit e92d40e

Please sign in to comment.