summarizer.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. /*
  2. Copyright 2012-2015, Yahoo Inc.
  3. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
  4. */
  5. "use strict";
  6. var Path = require('./path'),
  7. util = require('util'),
  8. tree = require('./tree'),
  9. coverage = require('istanbul-lib-coverage'),
  10. BaseNode = tree.Node,
  11. BaseTree = tree.Tree;
  12. function ReportNode(path, fileCoverage) {
  13. this.path = path;
  14. this.parent = null;
  15. this.fileCoverage = fileCoverage;
  16. this.children = [];
  17. }
  18. util.inherits(ReportNode, BaseNode);
  19. ReportNode.prototype.addChild = function (child) {
  20. child.parent = this;
  21. this.children.push(child);
  22. };
  23. ReportNode.prototype.asRelative = function (p) {
  24. /* istanbul ignore if */
  25. if (p.substring(0,1) === '/') {
  26. return p.substring(1);
  27. }
  28. return p;
  29. };
  30. ReportNode.prototype.getQualifiedName = function () {
  31. return this.asRelative(this.path.toString());
  32. };
  33. ReportNode.prototype.getRelativeName = function () {
  34. var parent = this.getParent(),
  35. myPath = this.path,
  36. relPath,
  37. i,
  38. parentPath = parent ? parent.path : new Path([]);
  39. if (parentPath.ancestorOf(myPath)) {
  40. relPath = new Path(myPath.elements());
  41. for (i = 0; i < parentPath.length; i += 1) {
  42. relPath.shift();
  43. }
  44. return this.asRelative(relPath.toString());
  45. }
  46. return this.asRelative(this.path.toString());
  47. };
  48. ReportNode.prototype.getParent = function () {
  49. return this.parent;
  50. };
  51. ReportNode.prototype.getChildren = function () {
  52. return this.children;
  53. };
  54. ReportNode.prototype.isSummary = function () {
  55. return !this.fileCoverage;
  56. };
  57. ReportNode.prototype.getFileCoverage = function () {
  58. return this.fileCoverage;
  59. };
  60. ReportNode.prototype.getCoverageSummary = function (filesOnly) {
  61. var cacheProp = 'c_' + (filesOnly ? 'files' : 'full'),
  62. summary;
  63. if (this.hasOwnProperty(cacheProp)) {
  64. return this[cacheProp];
  65. }
  66. if (!this.isSummary()) {
  67. summary = this.getFileCoverage().toSummary();
  68. } else {
  69. var count = 0;
  70. summary = coverage.createCoverageSummary();
  71. this.getChildren().forEach(function (child) {
  72. if (filesOnly && child.isSummary()) {
  73. return;
  74. }
  75. count += 1;
  76. summary.merge(child.getCoverageSummary(filesOnly));
  77. });
  78. if (count === 0 && filesOnly) {
  79. summary = null;
  80. }
  81. }
  82. this[cacheProp] = summary;
  83. return summary;
  84. };
  85. function treeFor(root, childPrefix) {
  86. var tree = new BaseTree(),
  87. visitor,
  88. maybePrefix = function (node) {
  89. if (childPrefix && !node.isRoot()) {
  90. node.path.unshift(childPrefix);
  91. }
  92. };
  93. tree.getRoot = function () {
  94. return root;
  95. };
  96. visitor = {
  97. onDetail: function (node) {
  98. maybePrefix(node);
  99. },
  100. onSummary: function (node) {
  101. maybePrefix(node);
  102. node.children.sort(function (a, b) {
  103. var astr = a.path.toString(),
  104. bstr = b.path.toString();
  105. return astr < bstr ? -1 : astr > bstr ? 1: /* istanbul ignore next */ 0;
  106. });
  107. }
  108. };
  109. tree.visit(visitor);
  110. return tree;
  111. }
  112. function findCommonParent(paths) {
  113. if (paths.length === 0) {
  114. return new Path([]);
  115. }
  116. var common = paths[0],
  117. i;
  118. for (i = 1; i < paths.length; i += 1) {
  119. common = common.commonPrefixPath(paths[i]);
  120. if (common.length === 0) {
  121. break;
  122. }
  123. }
  124. return common;
  125. }
  126. function toInitialList(coverageMap) {
  127. var ret = [],
  128. commonParent;
  129. coverageMap.files().forEach(function (filePath) {
  130. var p = new Path(filePath),
  131. coverage = coverageMap.fileCoverageFor(filePath);
  132. ret.push({
  133. filePath: filePath,
  134. path: p,
  135. fileCoverage: coverage
  136. });
  137. });
  138. commonParent = findCommonParent(ret.map(function (o) { return o.path.parent(); }));
  139. if (commonParent.length > 0) {
  140. ret.forEach(function (o) {
  141. o.path.splice(0, commonParent.length);
  142. });
  143. }
  144. return {
  145. list: ret,
  146. commonParent: commonParent
  147. };
  148. }
  149. function toDirParents(list) {
  150. var nodeMap = {},
  151. parentNodeList = [];
  152. list.forEach(function (o) {
  153. var node = new ReportNode(o.path, o.fileCoverage),
  154. parentPath = o.path.parent(),
  155. parent = nodeMap[parentPath.toString()];
  156. if (!parent) {
  157. parent = new ReportNode(parentPath);
  158. nodeMap[parentPath.toString()] = parent;
  159. parentNodeList.push(parent);
  160. }
  161. parent.addChild(node);
  162. });
  163. return parentNodeList;
  164. }
  165. function foldIntoParents(nodeList) {
  166. var ret = [], i, j;
  167. // sort by longest length first
  168. nodeList.sort(function (a, b) {
  169. return -1 * Path.compare(a.path , b.path);
  170. });
  171. for (i = 0; i < nodeList.length; i += 1) {
  172. var first = nodeList[i],
  173. inserted = false;
  174. for (j = i + 1; j < nodeList.length; j += 1) {
  175. var second = nodeList[j];
  176. if (second.path.ancestorOf(first.path)) {
  177. second.addChild(first);
  178. inserted = true;
  179. break;
  180. }
  181. }
  182. if (!inserted) {
  183. ret.push(first);
  184. }
  185. }
  186. return ret;
  187. }
  188. function createRoot() {
  189. return new ReportNode(new Path([]));
  190. }
  191. function createNestedSummary(coverageMap) {
  192. var flattened = toInitialList(coverageMap),
  193. dirParents = toDirParents(flattened.list),
  194. topNodes = foldIntoParents(dirParents),
  195. root;
  196. if (topNodes.length === 0) {
  197. return treeFor(new ReportNode([]));
  198. }
  199. if (topNodes.length === 1) {
  200. return treeFor(topNodes[0]);
  201. }
  202. root = createRoot();
  203. topNodes.forEach(function (node) {
  204. root.addChild(node);
  205. });
  206. return treeFor(root);
  207. }
  208. function createPackageSummary(coverageMap) {
  209. var flattened = toInitialList(coverageMap),
  210. dirParents = toDirParents(flattened.list),
  211. common = flattened.commonParent,
  212. prefix,
  213. root;
  214. if (dirParents.length === 1) {
  215. root = dirParents[0];
  216. } else {
  217. root = createRoot();
  218. // if one of the dirs is itself the root,
  219. // then we need to create a top-level dir
  220. dirParents.forEach(function (dp) {
  221. if (dp.path.length === 0) {
  222. prefix = 'root';
  223. }
  224. });
  225. if (prefix && common.length > 0) {
  226. prefix = common.elements()[common.elements().length - 1];
  227. }
  228. dirParents.forEach(function (node) {
  229. root.addChild(node);
  230. });
  231. }
  232. return treeFor(root, prefix);
  233. }
  234. function createFlatSummary(coverageMap) {
  235. var flattened = toInitialList(coverageMap),
  236. list = flattened.list,
  237. root;
  238. root = createRoot();
  239. list.forEach(function (o) {
  240. var node = new ReportNode(o.path, o.fileCoverage);
  241. root.addChild(node);
  242. });
  243. return treeFor(root);
  244. }
  245. module.exports = {
  246. createNestedSummary: createNestedSummary,
  247. createPackageSummary: createPackageSummary,
  248. createFlatSummary: createFlatSummary
  249. };