Code coverage report for istanbul/lib/util/tree-summarizer.js

Statements: 99.02% (101 / 102)      Branches: 92.11% (35 / 38)      Functions: 100% (26 / 26)      Lines: 98.99% (98 / 99)     

All files » istanbul/lib/util/ » tree-summarizer.js
1 /*
2 Copyright (c) 2012, Yahoo! Inc. All rights reserved.
3 Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4 */
5
6 1 var path = require('path'),
7 SEP = path.sep || '/',
8 utils = require('../object-utils');
9
10 1 function commonArrayPrefix(first, second) {
11 20 var len = first.length < second.length ? first.length : second.length,
12 i,
13 ret = [];
14 20 for (i = 0; i < len; i += 1) {
15 132 if (first[i] === second[i]) {
16 120 ret.push(first[i]);
17 } else {
18 12 break;
19 }
20 }
21 20 return ret;
22 }
23
24 1 function findCommonArrayPrefix(args) {
25 9 if (args.length === 0) {
26 1 return [];
27 }
28
29 28 var separated = args.map(function (arg) { return arg.split(SEP); }),
30 ret = separated.pop();
31
32 8 return separated.reduce(commonArrayPrefix, ret);
33 }
34
35 1 function Node(fullName, kind, metrics) {
36 57 this.name = fullName;
37 57 this.fullName = fullName;
38 57 this.kind = kind;
39 57 this.metrics = metrics || null;
40 57 this.parent = null;
41 57 this.children = [];
42 }
43
44 1 Node.prototype = {
45 displayShortName: function () {
46 66 return this.relativeName;
47 },
48 fullPath: function () {
49 42 return this.fullName;
50 },
51 addChild: function (child) {
52 54 this.children.push(child);
53 54 child.parent = this;
54 },
55 toJSON: function () {
56 6 return {
57 name: this.name,
58 relativeName: this.relativeName,
59 fullName: this.fullName,
60 kind: this.kind,
61 metrics: this.metrics,
62 parent: this.parent === null ? null : this.parent.name,
63 5 children: this.children.map(function (node) { return node.toJSON(); })
64 };
65 }
66 };
67
68 1 function TreeSummary(summaryMap, commonPrefix) {
69 9 this.prefix = commonPrefix;
70 9 this.convertToTree(summaryMap, commonPrefix);
71 }
72
73 1 TreeSummary.prototype = {
74 getNode: function (shortName) {
75 18 return this.map[shortName];
76 },
77 convertToTree: function (summaryMap, arrayPrefix) {
78 9 var nodes = [],
79 rootPath = arrayPrefix.join(SEP) + SEP,
80 root = new Node(rootPath, 'dir'),
81 tmp,
82 tmpChildren,
83 seen = {},
84 filesUnderRoot = false;
85
86 9 seen[rootPath] = root;
87 9 Object.keys(summaryMap).forEach(function (key) {
88 28 var metrics = summaryMap[key],
89 node,
90 parentPath,
91 parent;
92 28 node = new Node(key, 'file', metrics);
93 28 seen[key] = node;
94 28 nodes.push(node);
95 28 parentPath = path.dirname(key) + SEP;
96 28 if (parentPath === SEP + SEP) {
97 2 parentPath = SEP + '__root__' + SEP;
98 }
99 28 parent = seen[parentPath];
100 28 if (!parent) {
101 18 parent = new Node(parentPath, 'dir');
102 18 root.addChild(parent);
103 18 seen[parentPath] = parent;
104 }
105 28 parent.addChild(node);
106 28 if (parent === root) { filesUnderRoot = true; }
107 });
108
109 9 if (filesUnderRoot && arrayPrefix.length > 0) {
110 2 arrayPrefix.pop(); //start at one level above
111 2 tmp = root;
112 2 tmpChildren = tmp.children;
113 2 tmp.children = [];
114 2 root = new Node(arrayPrefix.join(SEP) + SEP, 'dir');
115 2 root.addChild(tmp);
116 2 tmpChildren.forEach(function (child) {
117 6 if (child.kind === 'dir') {
118 2 root.addChild(child);
119 } else {
120 4 tmp.addChild(child);
121 }
122 });
123 }
124 9 this.fixupNodes(root, arrayPrefix.join(SEP) + SEP);
125 9 this.calculateMetrics(root);
126 9 this.root = root;
127 9 this.map = {};
128 9 this.indexAndSortTree(root, this.map);
129 },
130
131 fixupNodes: function (node, prefix, parent) {
132 57 var that = this;
133 57 Eif (node.name.indexOf(prefix) === 0) {
134 57 node.name = node.name.substring(prefix.length);
135 }
136 57 Iif (node.name.charAt(0) === SEP) {
137 node.name = node.name.substring(1);
138 }
139 57 if (parent) {
140 48 if (parent.name !== '__root__/') {
141 46 node.relativeName = node.name.substring(parent.name.length);
142 } else {
143 2 node.relativeName = node.name;
144 }
145 } else {
146 9 node.relativeName = node.name.substring(prefix.length);
147 }
148 57 node.children.forEach(function (child) {
149 48 that.fixupNodes(child, prefix, node);
150 });
151 },
152 calculateMetrics: function (entry) {
153 57 var that = this,
154 metrics = null;
155 57 if (entry.kind !== 'dir') {return; }
156 29 entry.children.forEach(function (child) {
157 48 that.calculateMetrics(child);
158 });
159 29 entry.metrics = utils.mergeSummaryObjects.apply(
160 null,
161 48 entry.children.map(function (child) { return child.metrics; })
162 );
163 },
164 indexAndSortTree: function (node, map) {
165 57 var that = this;
166 57 map[node.name] = node;
167 57 node.children.sort(function (a, b) {
168 24 a = a.relativeName;
169 24 b = b.relativeName;
170 24 return a < b ? -1 : a > b ? 1 : 0;
171 });
172 57 node.children.forEach(function (child) {
173 48 that.indexAndSortTree(child, map);
174 });
175 },
176 toJSON: function () {
177 1 return {
178 prefix: this.prefix,
179 root: this.root.toJSON()
180 };
181 }
182 };
183
184 1 function TreeSummarizer() {
185 9 this.summaryMap = {};
186 }
187
188 1 TreeSummarizer.prototype = {
189 addFileCoverageSummary: function (filePath, metrics) {
190 28 this.summaryMap[filePath] = metrics;
191 },
192 getTreeSummary: function () {
193 9 var commonArrayPrefix = findCommonArrayPrefix(Object.keys(this.summaryMap));
194 9 return new TreeSummary(this.summaryMap, commonArrayPrefix);
195 }
196 };
197
198 1 module.exports = TreeSummarizer;
199