123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- /*
- Copyright 2012-2015, Yahoo Inc.
- Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
- */
- 'use strict';
- const util = require('util');
- const coverage = require('istanbul-lib-coverage');
- const Path = require('./path');
- const tree = require('./tree');
- const BaseNode = tree.Node;
- const BaseTree = tree.Tree;
- function ReportNode(path, fileCoverage) {
- this.path = path;
- this.parent = null;
- this.fileCoverage = fileCoverage;
- this.children = [];
- }
- util.inherits(ReportNode, BaseNode);
- ReportNode.prototype.addChild = function(child) {
- child.parent = this;
- this.children.push(child);
- };
- ReportNode.prototype.asRelative = function(p) {
- /* istanbul ignore if */
- if (p.substring(0, 1) === '/') {
- return p.substring(1);
- }
- return p;
- };
- ReportNode.prototype.getQualifiedName = function() {
- return this.asRelative(this.path.toString());
- };
- ReportNode.prototype.getRelativeName = function() {
- const parent = this.getParent();
- const myPath = this.path;
- let relPath;
- let i;
- const parentPath = parent ? parent.path : new Path([]);
- if (parentPath.ancestorOf(myPath)) {
- relPath = new Path(myPath.elements());
- for (i = 0; i < parentPath.length; i += 1) {
- relPath.shift();
- }
- return this.asRelative(relPath.toString());
- }
- return this.asRelative(this.path.toString());
- };
- ReportNode.prototype.getParent = function() {
- return this.parent;
- };
- ReportNode.prototype.getChildren = function() {
- return this.children;
- };
- ReportNode.prototype.isSummary = function() {
- return !this.fileCoverage;
- };
- ReportNode.prototype.getFileCoverage = function() {
- return this.fileCoverage;
- };
- ReportNode.prototype.getCoverageSummary = function(filesOnly) {
- const cacheProp = 'c_' + (filesOnly ? 'files' : 'full');
- let summary;
- if (this.hasOwnProperty(cacheProp)) {
- return this[cacheProp];
- }
- if (!this.isSummary()) {
- summary = this.getFileCoverage().toSummary();
- } else {
- let count = 0;
- summary = coverage.createCoverageSummary();
- this.getChildren().forEach(child => {
- if (filesOnly && child.isSummary()) {
- return;
- }
- count += 1;
- summary.merge(child.getCoverageSummary(filesOnly));
- });
- if (count === 0 && filesOnly) {
- summary = null;
- }
- }
- this[cacheProp] = summary;
- return summary;
- };
- function treeFor(root, childPrefix) {
- const tree = new BaseTree();
- const maybePrefix = function(node) {
- if (childPrefix && !node.isRoot()) {
- node.path.unshift(childPrefix);
- }
- };
- tree.getRoot = function() {
- return root;
- };
- const visitor = {
- onDetail(node) {
- maybePrefix(node);
- },
- onSummary(node) {
- maybePrefix(node);
- node.children.sort((a, b) => {
- const astr = a.path.toString();
- const bstr = b.path.toString();
- return astr < bstr
- ? -1
- : astr > bstr
- ? 1
- : /* istanbul ignore next */ 0;
- });
- }
- };
- tree.visit(visitor);
- return tree;
- }
- function findCommonParent(paths) {
- if (paths.length === 0) {
- return new Path([]);
- }
- let common = paths[0];
- let i;
- for (i = 1; i < paths.length; i += 1) {
- common = common.commonPrefixPath(paths[i]);
- if (common.length === 0) {
- break;
- }
- }
- return common;
- }
- function toInitialList(coverageMap) {
- const ret = [];
- coverageMap.files().forEach(filePath => {
- const p = new Path(filePath);
- const coverage = coverageMap.fileCoverageFor(filePath);
- ret.push({
- filePath,
- path: p,
- fileCoverage: coverage
- });
- });
- const commonParent = findCommonParent(ret.map(o => o.path.parent()));
- if (commonParent.length > 0) {
- ret.forEach(o => {
- o.path.splice(0, commonParent.length);
- });
- }
- return {
- list: ret,
- commonParent
- };
- }
- function toDirParents(list) {
- const nodeMap = Object.create(null);
- const parentNodeList = [];
- list.forEach(o => {
- const node = new ReportNode(o.path, o.fileCoverage);
- const parentPath = o.path.parent();
- let parent = nodeMap[parentPath.toString()];
- if (!parent) {
- parent = new ReportNode(parentPath);
- nodeMap[parentPath.toString()] = parent;
- parentNodeList.push(parent);
- }
- parent.addChild(node);
- });
- return parentNodeList;
- }
- function foldIntoParents(nodeList) {
- const ret = [];
- let i;
- let j;
- // sort by longest length first
- nodeList.sort((a, b) => -1 * Path.compare(a.path, b.path));
- for (i = 0; i < nodeList.length; i += 1) {
- const first = nodeList[i];
- let inserted = false;
- for (j = i + 1; j < nodeList.length; j += 1) {
- const second = nodeList[j];
- if (second.path.ancestorOf(first.path)) {
- second.addChild(first);
- inserted = true;
- break;
- }
- }
- if (!inserted) {
- ret.push(first);
- }
- }
- return ret;
- }
- function createRoot() {
- return new ReportNode(new Path([]));
- }
- function createNestedSummary(coverageMap) {
- const flattened = toInitialList(coverageMap);
- const dirParents = toDirParents(flattened.list);
- const topNodes = foldIntoParents(dirParents);
- if (topNodes.length === 0) {
- return treeFor(new ReportNode(new Path([])));
- }
- if (topNodes.length === 1) {
- return treeFor(topNodes[0]);
- }
- const root = createRoot();
- topNodes.forEach(node => {
- root.addChild(node);
- });
- return treeFor(root);
- }
- function createPackageSummary(coverageMap) {
- const flattened = toInitialList(coverageMap);
- const dirParents = toDirParents(flattened.list);
- const common = flattened.commonParent;
- let prefix;
- let root;
- if (dirParents.length === 1) {
- root = dirParents[0];
- } else {
- root = createRoot();
- // if one of the dirs is itself the root,
- // then we need to create a top-level dir
- dirParents.forEach(dp => {
- if (dp.path.length === 0) {
- prefix = 'root';
- }
- });
- if (prefix && common.length > 0) {
- prefix = common.elements()[common.elements().length - 1];
- }
- dirParents.forEach(node => {
- root.addChild(node);
- });
- }
- return treeFor(root, prefix);
- }
- function createFlatSummary(coverageMap) {
- const flattened = toInitialList(coverageMap);
- const list = flattened.list;
- const root = createRoot();
- list.forEach(o => {
- const node = new ReportNode(o.path, o.fileCoverage);
- root.addChild(node);
- });
- return treeFor(root);
- }
- module.exports = {
- createNestedSummary,
- createPackageSummary,
- createFlatSummary
- };
|