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 MemoryStore = require('./store/memory'), |
7 |
|
LookupStore = require('./store/fslookup') |
8 |
1 |
utils = require('./object-utils'); |
9 |
|
|
10 |
|
/** |
11 |
|
* a mechanism to merge multiple coverage objects into one. Handles the use case |
12 |
|
* of overlapping coverage information for the same files in multiple coverage |
13 |
|
* objects and does not double-count in this situation. For example, if |
14 |
|
* you pass the same coverage object multiple times, the final merged object will be |
15 |
|
* no different that any of the objects passed in (except for execution counts). |
16 |
|
* |
17 |
|
* The `Collector` is built for scale to handle thousands of coverage objects. |
18 |
|
* By default, all processing is done in memory since the common use-case is of |
19 |
|
* one or a few coverage objects. You can work around memory |
20 |
|
* issues by passing in a `Store` implementation that stores temporary computations |
21 |
|
* on disk (the `tmp` store, for example). |
22 |
|
* |
23 |
|
* The `getFinalCoverage` method returns an object with merged coverage information |
24 |
|
* and is provided as a convenience for implementors working with coverage information |
25 |
|
* that can fit into memory. Reporters, in the interest of generality, should *not* use this method for |
26 |
|
* creating reports. |
27 |
|
* |
28 |
|
* Usage |
29 |
|
* ----- |
30 |
|
* |
31 |
|
* var collector = new require('istanbul').Collector(); |
32 |
|
* |
33 |
|
* files.forEach(function (f) { |
34 |
|
* //each coverage object can have overlapping information about multiple files |
35 |
|
* collector.add(JSON.parse(fs.readFileSync(f, 'utf8'))); |
36 |
|
* }); |
37 |
|
* |
38 |
|
* collector.files().forEach(function(file) { |
39 |
|
* var fileCoverage = collector.fileCoverageFor(file); |
40 |
|
* console.log('Coverage for ' + file + ' is:' + JSON.stringify(fileCoverage)); |
41 |
|
* }); |
42 |
|
* |
43 |
|
* // convenience method: do not use this when dealing with a large number of files |
44 |
|
* var finalCoverage = collector.getFinalCoverage(); |
45 |
|
* |
46 |
|
* @class Collector |
47 |
|
* @constructor |
48 |
|
* @param {Object} options Optional. Configuration options. |
49 |
|
* @param {Store} options.store - an implementation of `Store` to use for temporary |
50 |
|
* calculations. |
51 |
|
* @param {Store} options.sourceStore - an implementation of `Store` for finding |
52 |
|
* original sources. |
53 |
|
*/ |
54 |
1 |
function Collector(options) { |
55 |
8 |
options = options || {}; |
56 |
8 |
this.store = options.store || new MemoryStore(); |
57 |
8 |
this.sourceStore = options.sourceStore || new LookupStore(); |
58 |
8 |
this.embeddedSource = false; |
59 |
|
} |
60 |
|
|
61 |
1 |
Collector.prototype = { |
62 |
|
/** |
63 |
|
* adds a coverage object to the collector. |
64 |
|
* |
65 |
|
* @method add |
66 |
|
* @param {Object} coverage the coverage object. |
67 |
|
* @param {String} testName Optional. The name of the test used to produce the object. |
68 |
|
* This is currently not used. |
69 |
|
*/ |
70 |
|
add: function (coverage, testName) { |
71 |
9 |
var store = this.store; |
72 |
9 |
Object.keys(coverage).forEach(function (key) { |
73 |
29 |
var fileCoverage = coverage[key]; |
74 |
29 |
Iif (fileCoverage.code) { this.embeddedSource = true; } //keep track of the fact that we have seen at least one object with embedded sources |
75 |
29 |
if (store.hasKey(key)) { |
76 |
1 |
store.setObject(key, utils.mergeFileCoverage(fileCoverage, store.getObject(key))); |
77 |
|
} else { |
78 |
28 |
store.setObject(key, fileCoverage); |
79 |
|
} |
80 |
|
}); |
81 |
|
}, |
82 |
|
/** |
83 |
|
* returns a list of unique file paths for which coverage information has been added. |
84 |
|
* @method files |
85 |
|
* @return {Array} an array of file paths for which coverage information is present. |
86 |
|
*/ |
87 |
|
files: function () { |
88 |
12 |
return this.store.keys(); |
89 |
|
}, |
90 |
|
/** |
91 |
|
* return file coverage information for a single file |
92 |
|
* @method fileCoverageFor |
93 |
|
* @param {String} fileName the path for the file for which coverage information is |
94 |
|
* required. Must be one of the values returned in the `files()` method. |
95 |
|
* @return {Object} the coverage information for the specified file. |
96 |
|
*/ |
97 |
|
fileCoverageFor: function (fileName) { |
98 |
56 |
var ret = this.store.getObject(fileName); |
99 |
56 |
utils.addDerivedInfoForFile(ret); |
100 |
56 |
return ret; |
101 |
|
}, |
102 |
|
/** |
103 |
|
* return source code for a file |
104 |
|
* @method sourceFor |
105 |
|
* @param {String} fileName the path for the file for which source code is required. |
106 |
|
* Must be one of the values returned in the `files()` method. |
107 |
|
* @return {Object} the source code for the specified file. |
108 |
|
*/ |
109 |
|
sourceFor: function (fileName) { |
110 |
16 |
var obj; |
111 |
|
|
112 |
16 |
Iif (this.embeddedSource) { //get it from coverage object if possible |
113 |
|
obj = this.store.getObject(fileName); |
114 |
|
if (obj.code && Array.isArray(obj.code)) { |
115 |
|
return obj.code.join('\n'); |
116 |
|
} |
117 |
|
} |
118 |
16 |
return this.sourceStore.get(fileName); |
119 |
|
}, |
120 |
|
/** |
121 |
|
* returns file coverage information for all files. This has the same format as |
122 |
|
* any of the objects passed in to the `add` method. The number of keys in this |
123 |
|
* object will be a superset of all keys found in the objects passed to `add()` |
124 |
|
* @method getFinalCoverage |
125 |
|
* @return {Object} the merged coverage information |
126 |
|
*/ |
127 |
|
getFinalCoverage: function () { |
128 |
2 |
var ret = {}, |
129 |
|
that = this; |
130 |
2 |
this.files().forEach(function (file) { |
131 |
4 |
ret[file] = that.fileCoverageFor(file); |
132 |
|
}); |
133 |
2 |
return ret; |
134 |
|
}, |
135 |
|
/** |
136 |
|
* disposes this collector and reclaims temporary resources used in the |
137 |
|
* computation. Calls `dispose()` on the underlying store. |
138 |
|
* @method dispose |
139 |
|
*/ |
140 |
|
dispose: function () { |
141 |
2 |
this.store.dispose(); |
142 |
|
} |
143 |
|
}; |
144 |
|
|
145 |
1 |
module.exports = Collector; |