Skip to content

Commit 070868e

Browse files
committed
implement coverage reports
1 parent 7de2eb3 commit 070868e

6 files changed

Lines changed: 181 additions & 86 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
.DS_Store
22
node_modules
3+
coverage

lib/child.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
var QUnit = require('../support/qunit/qunit/qunit.js'),
22
path = require('path'),
33
_ = require('underscore'),
4-
trace = require('tracejs').trace;
4+
trace = require('tracejs').trace,
5+
coverage = require('./coverage');
56

67
// cycle.js: This file contains two functions, JSON.decycle and JSON.retrocycle,
78
// which make it possible to encode cyclical structures and dags in JSON, and to
@@ -134,4 +135,4 @@ function run() {
134135
});
135136
}
136137

137-
options.coverage ? require('./coverage').cover(options, run) : run();
138+
options.coverage ? coverage.instrument(options, run) : run();

lib/coverage.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,25 @@ exports.setup = function() {
1111
collector = new istanbul.Collector();
1212
};
1313

14-
exports.append = function(coverage) {
14+
exports.add = function(coverage) {
1515
collector && coverage && collector.add(coverage);
1616
};
1717

18+
exports.get = function() {
19+
if (collector) {
20+
var summaries = [];
21+
collector.files().forEach(function (file) {
22+
summaries.push(istanbul.utils.summarizeFileCoverage(collector.fileCoverageFor(file)));
23+
});
24+
return istanbul.utils.mergeSummaryObjects.apply(null, summaries);
25+
}
26+
};
27+
1828
exports.report = function() {
1929
if (collector) {
2030
var opts = { dir: path.resolve('coverage') },
2131
Report = istanbul.Report,
22-
reports = [ Report.create('lcov', opts), Report.create('json', opts), Report.create('text-summary', opts) ];
32+
reports = [ Report.create('lcov', opts), Report.create('json', opts) ];
2333
reports.forEach(function (rep) {
2434
rep.writeReport(collector, true);
2535
});
@@ -33,15 +43,15 @@ function resolvePaths(files) {
3343
return [files.path];
3444
}
3545

36-
exports.cover = function(options, run) {
46+
exports.instrument = function(options, cb) {
3747
istanbul.matcherFor({
3848
includes: resolvePaths(options.code),
3949
excludes: resolvePaths(options.tests)
4050
}, function (err, matcher) {
4151
if (err) { throw err; }
4252
var instrumenter = new istanbul.Instrumenter();
4353
istanbul.hook.hookRequire(matcher, instrumenter.instrumentSync.bind(instrumenter));
44-
run();
54+
cb();
4555
});
4656
};
4757

lib/log.js

Lines changed: 85 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,34 @@
11
var Table = require('cli-table');
22

33
var data,
4-
log = console.log;
4+
log = console.log,
5+
fileColWidth = 50;
56

67
data = {
78
assertions: [],
89
tests: [],
9-
summaries: []
10+
summaries: [],
11+
coverages: []
1012
};
1113

12-
exports.assertion = function(d) {
14+
exports.add = function(t, d) {
1315
if (d) {
14-
data.assertions.push(d);
16+
data[t].push(d);
1517
}
16-
17-
return data.assertions;
18-
};
19-
20-
exports.test = function(d) {
21-
if (d) {
22-
data.tests.push(d);
23-
}
24-
25-
return data.tests;
26-
};
27-
28-
exports.summary = function(d) {
29-
if (d) {
30-
data.summaries.push(d);
31-
}
32-
33-
return data.summaries;
18+
return data[t];
3419
};
3520

3621
/**
3722
* Get global tests stats in unified format
3823
*/
3924
exports.stats = function() {
4025
var stats = {
41-
files: 0,
42-
assertions: 0,
43-
failed: 0,
44-
passed: 0,
45-
runtime: 0
46-
};
26+
files: 0,
27+
assertions: 0,
28+
failed: 0,
29+
passed: 0,
30+
runtime: 0
31+
};
4732

4833
data.summaries.forEach(function(file) {
4934
stats.files++;
@@ -55,6 +40,26 @@ exports.stats = function() {
5540

5641
stats.tests = data.tests.length;
5742

43+
stats.coverage = {
44+
files: 0,
45+
statements: { covered: 0, total: 0 },
46+
branches: { covered: 0, total: 0 },
47+
functions: { covered: 0, total: 0 },
48+
lines: { covered: 0, total: 0 }
49+
};
50+
51+
data.coverages.forEach(function(file) {
52+
stats.coverage.files++;
53+
stats.coverage.statements.covered += file.statements.covered;
54+
stats.coverage.statements.total += file.statements.total;
55+
stats.coverage.branches.covered += file.branches.covered;
56+
stats.coverage.branches.total += file.branches.total;
57+
stats.coverage.functions.covered += file.functions.covered;
58+
stats.coverage.functions.total += file.functions.total;
59+
stats.coverage.lines.covered += file.lines.covered;
60+
stats.coverage.lines.total += file.lines.total;
61+
});
62+
5863
return stats;
5964
};
6065

@@ -65,7 +70,8 @@ exports.reset = function() {
6570
data = {
6671
assertions: [],
6772
tests: [],
68-
summaries: []
73+
summaries: [],
74+
coverages: []
6975
};
7076
};
7177

@@ -159,23 +165,22 @@ print.tests = function() {
159165
log('\nTests:\n' + table.toString());
160166
};
161167

162-
print.summary = function() {
163-
var table, fileColWidth = 50;
168+
// truncate file name
169+
function truncfile(code) {
170+
if (code && code.length > fileColWidth) {
171+
code = '...' + code.slice(code.length - fileColWidth + 3);
172+
}
173+
return code;
174+
}
164175

165-
table = new Table({
176+
print.summary = function() {
177+
var table = new Table({
166178
head: ['File', 'Failed', 'Passed', 'Total', 'Runtime'],
167179
colWidths: [fileColWidth + 2, 10, 10, 10, 10]
168180
});
169181

170182
data.summaries.forEach(function(data) {
171-
var code = data.code;
172-
173-
// truncate file name
174-
if (code.length > fileColWidth) {
175-
code = '...' + code.slice(code.length - fileColWidth + 3);
176-
}
177-
178-
table.push([code, data.failed, data.passed, data.total, data.runtime]);
183+
table.push([truncfile(data.code), data.failed, data.passed, data.total, data.runtime]);
179184
});
180185

181186
log('\nSummary:\n' + table.toString());
@@ -195,3 +200,43 @@ print.globalSummary = function() {
195200

196201
log('\nGlobal summary:\n' + table.toString());
197202
};
203+
204+
function getMet(metric) {
205+
function percent(covered, total) {
206+
var tmp;
207+
if (total > 0) {
208+
tmp = 1000 * 100 * covered / total + 5;
209+
return Math.floor(tmp / 10) / 100;
210+
} else {
211+
return 100.00;
212+
}
213+
}
214+
if (!metric.pct) metric.pct = percent(metric.covered, metric.total);
215+
return metric.pct + '% (' + metric.covered + '/' + metric.total + ')';
216+
}
217+
218+
print.coverage = function() {
219+
var table = new Table({
220+
head: ['File', 'Statements', 'Branches', 'Functions', 'Lines'],
221+
colWidths: [fileColWidth + 2, 14, 14, 14, 14]
222+
});
223+
224+
data.coverages.forEach(function(data) {
225+
table.push([truncfile(data.code), getMet(data.statements), getMet(data.branches), getMet(data.functions), getMet(data.lines)]);
226+
});
227+
228+
log('\nCoverage:\n' + table.toString());
229+
};
230+
231+
print.globalCoverage = function() {
232+
var data = exports.stats().coverage;
233+
234+
var table = new Table({
235+
head: ['Files', 'Statements', 'Branches', 'Functions', 'Lines'],
236+
colWidths: [8, 14, 14, 14, 14]
237+
});
238+
239+
table.push([data.files, getMet(data.statements), getMet(data.branches), getMet(data.functions), getMet(data.lines)]);
240+
241+
log('\nGlobal coverage:\n' + table.toString());
242+
};

lib/testrunner.js

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ options = exports.options = {
2929
// log global summary (all files)
3030
globalSummary: true,
3131

32+
// log coverage
33+
coverage: true,
34+
35+
// log global coverage (all files)
36+
globalCoverage: true,
37+
3238
// log currently testing code file
3339
testing: true
3440
},
@@ -64,12 +70,18 @@ function runOne(opts, callback) {
6470

6571
child.on('message', function(msg) {
6672
if (msg.event === 'assertionDone') {
67-
log.assertion(msg.data);
73+
log.add('assertions', msg.data);
6874
} else if (msg.event === 'testDone') {
69-
log.test(msg.data);
75+
log.add('tests', msg.data);
7076
} else if (msg.event === 'done') {
7177
msg.data.code = opts.code.path;
72-
log.summary(msg.data);
78+
log.add('summaries', msg.data);
79+
if (opts.coverage) {
80+
coverage.add(msg.data.coverage);
81+
msg.data.coverage = coverage.get();
82+
msg.data.coverage.code = msg.data.code;
83+
log.add('coverages', msg.data.coverage);
84+
}
7385
if (opts.log.testing) {
7486
util.print('done');
7587
}
@@ -152,7 +164,6 @@ exports.run = function(files, callback) {
152164
if (err) {
153165
return callback(err, log.stats());
154166
}
155-
stat && coverage.append(stat.coverage);
156167

157168
filesCount++;
158169

@@ -162,7 +173,7 @@ exports.run = function(files, callback) {
162173
log.print[name]();
163174
}
164175
});
165-
176+
// write coverage report(s)
166177
coverage.report();
167178
callback(null, log.stats());
168179
}

0 commit comments

Comments
 (0)