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