Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80679 views
1
var path = require('path'),
2
util = require('util'),
3
Report = require('./index'),
4
FileWriter = require('../util/file-writer'),
5
TreeSummarizer = require('../util/tree-summarizer'),
6
utils = require('../object-utils');
7
8
/**
9
* a `Report` implementation that produces a clover-style XML file.
10
*
11
* Usage
12
* -----
13
*
14
* var report = require('istanbul').Report.create('clover');
15
*
16
* @class CloverReport
17
* @module report
18
* @extends Report
19
* @constructor
20
* @param {Object} opts optional
21
* @param {String} [opts.dir] the directory in which to the clover.xml will be written
22
* @param {String} [opts.file] the file name, defaulted to config attribute or 'clover.xml'
23
*/
24
function CloverReport(opts) {
25
Report.call(this);
26
opts = opts || {};
27
this.projectRoot = process.cwd();
28
this.dir = opts.dir || this.projectRoot;
29
this.file = opts.file || this.getDefaultConfig().file;
30
this.opts = opts;
31
}
32
33
CloverReport.TYPE = 'clover';
34
util.inherits(CloverReport, Report);
35
36
function asJavaPackage(node) {
37
return node.displayShortName().
38
replace(/\//g, '.').
39
replace(/\\/g, '.').
40
replace(/\.$/, '');
41
}
42
43
function asClassName(node) {
44
/*jslint regexp: true */
45
return node.fullPath().replace(/.*[\\\/]/, '');
46
}
47
48
function quote(thing) {
49
return '"' + thing + '"';
50
}
51
52
function attr(n, v) {
53
return ' ' + n + '=' + quote(v) + ' ';
54
}
55
56
function branchCoverageByLine(fileCoverage) {
57
var branchMap = fileCoverage.branchMap,
58
branches = fileCoverage.b,
59
ret = {};
60
Object.keys(branchMap).forEach(function (k) {
61
var line = branchMap[k].line,
62
branchData = branches[k];
63
ret[line] = ret[line] || [];
64
ret[line].push.apply(ret[line], branchData);
65
});
66
Object.keys(ret).forEach(function (k) {
67
var dataArray = ret[k],
68
covered = dataArray.filter(function (item) { return item > 0; }),
69
coverage = covered.length / dataArray.length * 100;
70
ret[k] = { covered: covered.length, total: dataArray.length, coverage: coverage };
71
});
72
return ret;
73
}
74
75
function addClassStats(node, fileCoverage, writer) {
76
var metrics = node.metrics,
77
branchByLine = branchCoverageByLine(fileCoverage),
78
fnMap,
79
lines;
80
81
writer.println('\t\t\t<file' +
82
attr('name', asClassName(node)) +
83
attr('path', node.fullPath()) +
84
'>');
85
86
writer.println('\t\t\t\t<metrics' +
87
attr('statements', metrics.lines.total) +
88
attr('coveredstatements', metrics.lines.covered) +
89
attr('conditionals', metrics.branches.total) +
90
attr('coveredconditionals', metrics.branches.covered) +
91
attr('methods', metrics.functions.total) +
92
attr('coveredmethods', metrics.functions.covered) +
93
'/>');
94
95
fnMap = fileCoverage.fnMap;
96
lines = fileCoverage.l;
97
Object.keys(lines).forEach(function (k) {
98
var str = '\t\t\t\t<line' +
99
attr('num', k) +
100
attr('count', lines[k]),
101
branchDetail = branchByLine[k];
102
103
if (!branchDetail) {
104
str += ' type="stmt" ';
105
} else {
106
str += ' type="cond" ' +
107
attr('truecount', branchDetail.covered) +
108
attr('falsecount', (branchDetail.total - branchDetail.covered));
109
}
110
writer.println(str + '/>');
111
});
112
113
writer.println('\t\t\t</file>');
114
}
115
116
function walk(node, collector, writer, level, projectRoot) {
117
var metrics,
118
totalFiles = 0,
119
totalPackages = 0,
120
totalLines = 0,
121
tempLines = 0;
122
if (level === 0) {
123
metrics = node.metrics;
124
writer.println('<?xml version="1.0" encoding="UTF-8"?>');
125
writer.println('<coverage' +
126
attr('generated', Date.now()) +
127
'clover="3.2.0">');
128
129
writer.println('\t<project' +
130
attr('timestamp', Date.now()) +
131
attr('name', 'All Files') +
132
'>');
133
134
node.children.filter(function (child) { return child.kind === 'dir'; }).
135
forEach(function (child) {
136
totalPackages += 1;
137
child.children.filter(function (child) { return child.kind !== 'dir'; }).
138
forEach(function (child) {
139
Object.keys(collector.fileCoverageFor(child.fullPath()).l).forEach(function (k){
140
tempLines = k;
141
});
142
totalLines += Number(tempLines);
143
totalFiles += 1;
144
});
145
});
146
147
writer.println('\t\t<metrics' +
148
attr('statements', metrics.lines.total) +
149
attr('coveredstatements', metrics.lines.covered) +
150
attr('conditionals', metrics.branches.total) +
151
attr('coveredconditionals', metrics.branches.covered) +
152
attr('methods', metrics.functions.total) +
153
attr('coveredmethods', metrics.functions.covered) +
154
attr('elements', metrics.lines.total + metrics.branches.total + metrics.functions.total) +
155
attr('coveredelements', metrics.lines.covered + metrics.branches.covered + metrics.functions.covered) +
156
attr('complexity', 0) +
157
attr('packages', totalPackages) +
158
attr('files', totalFiles) +
159
attr('classes', totalFiles) +
160
attr('loc', totalLines) +
161
attr('ncloc', totalLines) +
162
'/>');
163
}
164
if (node.packageMetrics) {
165
metrics = node.packageMetrics;
166
writer.println('\t\t<package' +
167
attr('name', asJavaPackage(node)) +
168
'>');
169
170
writer.println('\t\t\t<metrics' +
171
attr('statements', metrics.lines.total) +
172
attr('coveredstatements', metrics.lines.covered) +
173
attr('conditionals', metrics.branches.total) +
174
attr('coveredconditionals', metrics.branches.covered) +
175
attr('methods', metrics.functions.total) +
176
attr('coveredmethods', metrics.functions.covered) +
177
'/>');
178
179
node.children.filter(function (child) { return child.kind !== 'dir'; }).
180
forEach(function (child) {
181
addClassStats(child, collector.fileCoverageFor(child.fullPath()), writer);
182
});
183
writer.println('\t\t</package>');
184
}
185
node.children.filter(function (child) { return child.kind === 'dir'; }).
186
forEach(function (child) {
187
walk(child, collector, writer, level + 1, projectRoot);
188
});
189
190
if (level === 0) {
191
writer.println('\t</project>');
192
writer.println('</coverage>');
193
}
194
}
195
196
Report.mix(CloverReport, {
197
synopsis: function () {
198
return 'XML coverage report that can be consumed by the clover tool';
199
},
200
getDefaultConfig: function () {
201
return { file: 'clover.xml' };
202
},
203
writeReport: function (collector, sync) {
204
var summarizer = new TreeSummarizer(),
205
outputFile = path.join(this.dir, this.file),
206
writer = this.opts.writer || new FileWriter(sync),
207
projectRoot = this.projectRoot,
208
that = this,
209
tree,
210
root;
211
212
collector.files().forEach(function (key) {
213
summarizer.addFileCoverageSummary(key, utils.summarizeFileCoverage(collector.fileCoverageFor(key)));
214
});
215
tree = summarizer.getTreeSummary();
216
root = tree.root;
217
writer.on('done', function () { that.emit('done'); });
218
writer.writeFile(outputFile, function (contentWriter) {
219
walk(root, collector, contentWriter, 0, projectRoot);
220
writer.done();
221
});
222
}
223
});
224
225
module.exports = CloverReport;
226
227