monocart-coverage-reports 2.11.6 → 2.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,439 +1,439 @@
1
- /**
2
- * The `mergeV8Coverage` was extracted from following repos for fixing several issues
3
- * https://github.com/bcoe/v8-coverage
4
- * https://github.com/demurgos/v8-coverage
5
- */
6
-
7
- const RangeTree = require('./range-tree.js');
8
-
9
-
10
- /**
11
- * Compares two range coverages.
12
- *
13
- * The ranges are first ordered by ascending `startOffset` and then by
14
- * descending `endOffset`.
15
- * This corresponds to a pre-order tree traversal.
16
- */
17
- function compareRangeCovs(a, b) {
18
- if (a.startOffset !== b.startOffset) {
19
- return a.startOffset - b.startOffset;
20
- }
21
- return b.endOffset - a.endOffset;
22
- }
23
-
24
- /**
25
- * Compares two function coverages.
26
- *
27
- * The result corresponds to the comparison of the root ranges.
28
- */
29
- function compareFunctionCovs(a, b) {
30
- return compareRangeCovs(a.ranges[0], b.ranges[0]);
31
- }
32
-
33
- /**
34
- * @precodition `ranges` are well-formed and pre-order sorted
35
- */
36
- function fromSortedRanges(ranges) {
37
- let root;
38
- // Stack of parent trees and parent counts.
39
- const stack = [];
40
- for (const range of ranges) {
41
- const node = new RangeTree(range.startOffset, range.endOffset, range.count, []);
42
- if (!root) {
43
- root = node;
44
- stack.push([node, range.count]);
45
- continue;
46
- }
47
- let parent;
48
- let parentCount;
49
- while (true) {
50
- [parent, parentCount] = stack[stack.length - 1];
51
- // assert: `top !== undefined` (the ranges are sorted)
52
- if (range.startOffset < parent.end) {
53
- break;
54
- } else {
55
- stack.pop();
56
- }
57
-
58
- // prevent crash when v8 incorrectly merges static_initializer's
59
- if (stack.length === 0) {
60
- break;
61
- }
62
- }
63
- node.delta -= parentCount;
64
- parent.children.push(node);
65
- stack.push([node, range.count]);
66
- }
67
- return root;
68
- }
69
-
70
- /**
71
- * Normalizes a function coverage.
72
- *
73
- * Sorts the ranges (pre-order sort).
74
- * TODO: Tree-based normalization of the ranges.
75
- *
76
- * @param funcCov Function coverage to normalize.
77
- */
78
- function normalizeFunctionCov(funcCov) {
79
- funcCov.ranges.sort(compareRangeCovs);
80
- const tree = fromSortedRanges(funcCov.ranges);
81
- tree.normalize();
82
- funcCov.ranges = tree.toRanges();
83
- }
84
-
85
- /**
86
- * Normalizes a script coverage.
87
- *
88
- * Sorts the function by root range (pre-order sort).
89
- * This does not normalize the function coverages.
90
- *
91
- * @param scriptCov Script coverage to normalize.
92
- */
93
- function normalizeScriptCov(scriptCov) {
94
- scriptCov.functions.sort(compareFunctionCovs);
95
- }
96
-
97
-
98
- /**
99
- * Normalizes a script coverage deeply.
100
- *
101
- * Normalizes the function coverages deeply, then normalizes the script coverage
102
- * itself.
103
- *
104
- * @param scriptCov Script coverage to normalize.
105
- */
106
- function deepNormalizeScriptCov(scriptCov) {
107
- for (const funcCov of scriptCov.functions) {
108
- normalizeFunctionCov(funcCov);
109
- }
110
- normalizeScriptCov(scriptCov);
111
- }
112
-
113
-
114
- /**
115
- * Returns a string representation of the root range of the function.
116
- *
117
- * This string can be used to match function with same root range.
118
- * The string is derived from the start and end offsets of the root range of
119
- * the function.
120
- * This assumes that `ranges` is non-empty (true for valid function coverages).
121
- *
122
- * @param funcCov Function coverage with the range to stringify
123
- * @internal
124
- */
125
- function stringifyFunctionRootRange(funcCov) {
126
- const rootRange = funcCov.ranges[0];
127
- return `${rootRange.startOffset.toString(10)};${rootRange.endOffset.toString(10)}`;
128
- }
129
-
130
-
131
- function insertChild(parentToNested, parentIndex, tree) {
132
- let nested = parentToNested.get(parentIndex);
133
- if (!nested) {
134
- nested = [];
135
- parentToNested.set(parentIndex, nested);
136
- }
137
- nested.push(tree);
138
- }
139
-
140
- function nextChild(openRange, parentToNested) {
141
- const matchingTrees = [];
142
-
143
- for (const nested of parentToNested.values()) {
144
- if (nested.length === 1 && nested[0].start === openRange.start && nested[0].end === openRange.end) {
145
- matchingTrees.push(nested[0]);
146
- } else {
147
- matchingTrees.push(new RangeTree(
148
- openRange.start,
149
- openRange.end,
150
- 0,
151
- nested
152
- ));
153
- }
154
- }
155
- parentToNested.clear();
156
- return mergeRangeTrees(matchingTrees);
157
- }
158
-
159
- class StartEventQueue {
160
-
161
- constructor(queue) {
162
- this.queue = queue;
163
- this.nextIndex = 0;
164
- this.pendingOffset = 0;
165
- this.pendingTrees = null;
166
- }
167
-
168
- setPendingOffset(offset) {
169
- this.pendingOffset = offset;
170
- }
171
-
172
- pushPendingTree(tree) {
173
- if (this.pendingTrees === null) {
174
- this.pendingTrees = [];
175
- }
176
- this.pendingTrees.push(tree);
177
- }
178
-
179
- next() {
180
- const pendingTrees = this.pendingTrees;
181
- const nextEvent = this.queue[this.nextIndex];
182
- if (pendingTrees === null) {
183
- this.nextIndex++;
184
- return nextEvent;
185
- }
186
- if (!nextEvent) {
187
- this.pendingTrees = null;
188
- return {
189
- offset: this.pendingOffset,
190
- trees: pendingTrees
191
- };
192
- }
193
- if (this.pendingOffset < nextEvent.offset) {
194
- this.pendingTrees = null;
195
- return {
196
- offset: this.pendingOffset,
197
- trees: pendingTrees
198
- };
199
- }
200
- if (this.pendingOffset === nextEvent.offset) {
201
- this.pendingTrees = null;
202
- for (const tree of pendingTrees) {
203
- nextEvent.trees.push(tree);
204
- }
205
- }
206
- this.nextIndex++;
207
- return nextEvent;
208
-
209
-
210
- }
211
- }
212
-
213
- function fromParentTrees(parentTrees) {
214
- const startToTrees = new Map();
215
- for (const [parentIndex, parentTree] of parentTrees.entries()) {
216
- for (const child of parentTree.children) {
217
- let trees = startToTrees.get(child.start);
218
- if (!trees) {
219
- trees = [];
220
- startToTrees.set(child.start, trees);
221
- }
222
- trees.push({
223
- parentIndex,
224
- tree: child
225
- });
226
- }
227
- }
228
- const queue = [];
229
- for (const [startOffset, trees] of startToTrees) {
230
- queue.push({
231
- offset: startOffset,
232
- trees
233
- });
234
- }
235
- queue.sort((a, b) => {
236
- return a.offset - b.offset;
237
- });
238
- return new StartEventQueue(queue);
239
- }
240
-
241
- // eslint-disable-next-line complexity
242
- function mergeRangeTreeChildren(parentTrees) {
243
- const result = [];
244
- const startEventQueue = fromParentTrees(parentTrees);
245
- const parentToNested = new Map();
246
- let openRange = null;
247
-
248
- while (true) {
249
- const event = startEventQueue.next();
250
- if (!event) {
251
- break;
252
- }
253
-
254
- if (openRange !== null && openRange.end <= event.offset) {
255
- result.push(nextChild(openRange, parentToNested));
256
- openRange = null;
257
- }
258
-
259
- if (openRange === null) {
260
- let openRangeEnd = event.offset + 1;
261
- for (const { parentIndex, tree } of event.trees) {
262
- openRangeEnd = Math.max(openRangeEnd, tree.end);
263
- insertChild(parentToNested, parentIndex, tree);
264
- }
265
- startEventQueue.setPendingOffset(openRangeEnd);
266
- openRange = {
267
- start: event.offset, end: openRangeEnd
268
- };
269
- } else {
270
- for (const { parentIndex, tree } of event.trees) {
271
- if (tree.end > openRange.end) {
272
- const right = tree.split(openRange.end);
273
- startEventQueue.pushPendingTree({
274
- parentIndex,
275
- tree: right
276
- });
277
- }
278
- insertChild(parentToNested, parentIndex, tree);
279
- }
280
- }
281
- }
282
- if (openRange !== null) {
283
- result.push(nextChild(openRange, parentToNested));
284
- }
285
-
286
- return result;
287
- }
288
-
289
- /**
290
- * @precondition Same `start` and `end` for all the trees
291
- */
292
- function mergeRangeTrees(trees) {
293
- if (trees.length <= 1) {
294
- return trees[0];
295
- }
296
- const first = trees[0];
297
- let delta = 0;
298
- for (const tree of trees) {
299
- delta += tree.delta;
300
- }
301
- const children = mergeRangeTreeChildren(trees);
302
- return new RangeTree(first.start, first.end, delta, children);
303
- }
304
-
305
- /**
306
- * Merges a list of matching function coverages.
307
- *
308
- * Functions are matching if their root ranges have the same span.
309
- * The result is normalized.
310
- * The input values may be mutated, it is not safe to use them after passing
311
- * them to this function.
312
- * The computation is synchronous.
313
- *
314
- * @param funcCovs Function coverages to merge.
315
- * @return Merged function coverage, or `undefined` if the input list was empty.
316
- */
317
- function mergeFunctionCovs(funcCovs) {
318
- if (funcCovs.length === 0) {
319
- return;
320
- }
321
-
322
- if (funcCovs.length === 1) {
323
- const merged = funcCovs[0];
324
- normalizeFunctionCov(merged);
325
- return merged;
326
- }
327
-
328
- const first = funcCovs[0];
329
- const functionName = first.functionName;
330
- // assert: `first.ranges.length > 0`
331
- const startOffset = first.ranges[0].startOffset;
332
- const endOffset = first.ranges[0].endOffset;
333
- let count = 0;
334
-
335
- const trees = [];
336
- for (const funcCov of funcCovs) {
337
- // assert: `funcCov.ranges.length > 0`
338
- // assert: `funcCov.ranges` is sorted
339
- count += typeof funcCov.count === 'number' ? funcCov.count : funcCov.ranges[0].count;
340
- if (funcCov.isBlockCoverage) {
341
- trees.push(fromSortedRanges(funcCov.ranges));
342
- }
343
- }
344
-
345
- let isBlockCoverage;
346
- let ranges;
347
- if (trees.length > 0) {
348
- isBlockCoverage = true;
349
- const mergedTree = mergeRangeTrees(trees);
350
- mergedTree.normalize();
351
- ranges = mergedTree.toRanges();
352
- } else {
353
- isBlockCoverage = false;
354
- ranges = [{
355
- startOffset,
356
- endOffset,
357
- count
358
- }];
359
- }
360
-
361
- const merged = {
362
- functionName,
363
- ranges,
364
- isBlockCoverage
365
- };
366
- if (count !== ranges[0].count) {
367
- merged.count = count;
368
- }
369
- // assert: `merged` is normalized
370
- return merged;
371
- }
372
-
373
-
374
- /**
375
- * Merges a list of matching script coverages.
376
- *
377
- * Scripts are matching if they have the same `url`.
378
- * The result is normalized.
379
- * The input values may be mutated, it is not safe to use them after passing
380
- * them to this function.
381
- * The computation is synchronous.
382
- *
383
- * @param scriptCovs Process coverages to merge.
384
- * @return Merged script coverage, or `undefined` if the input list was empty.
385
- */
386
-
387
- // eslint-disable-next-line complexity
388
- function mergeV8Coverage(scriptCovs) {
389
- if (!Array.isArray(scriptCovs)) {
390
- return {
391
- functions: []
392
- };
393
- }
394
-
395
- if (scriptCovs.length === 0) {
396
- return {
397
- functions: []
398
- };
399
- }
400
-
401
- if (scriptCovs.length === 1) {
402
- const merged = scriptCovs[0];
403
- deepNormalizeScriptCov(merged);
404
- return merged;
405
- }
406
-
407
-
408
- const rangeToFuncs = new Map();
409
- for (const scriptCov of scriptCovs) {
410
- for (const funcCov of scriptCov.functions) {
411
- const rootRange = stringifyFunctionRootRange(funcCov);
412
- let funcCovs = rangeToFuncs.get(rootRange);
413
- if (!funcCovs) {
414
- funcCovs = [];
415
- rangeToFuncs.set(rootRange, funcCovs);
416
- }
417
- funcCovs.push(funcCov);
418
- }
419
- }
420
-
421
- const functions = [];
422
- for (const funcCovs of rangeToFuncs.values()) {
423
- // assert: `funcCovs.length > 0`
424
- const block = mergeFunctionCovs(funcCovs);
425
- if (block) {
426
- functions.push(block);
427
- }
428
- }
429
-
430
- const merged = {
431
- functions
432
- };
433
- normalizeScriptCov(merged);
434
- return merged;
435
- }
436
-
437
- module.exports = {
438
- mergeV8Coverage
439
- };
1
+ /**
2
+ * The `mergeV8Coverage` was extracted from following repos for fixing several issues
3
+ * https://github.com/bcoe/v8-coverage
4
+ * https://github.com/demurgos/v8-coverage
5
+ */
6
+
7
+ const RangeTree = require('./range-tree.js');
8
+
9
+
10
+ /**
11
+ * Compares two range coverages.
12
+ *
13
+ * The ranges are first ordered by ascending `startOffset` and then by
14
+ * descending `endOffset`.
15
+ * This corresponds to a pre-order tree traversal.
16
+ */
17
+ function compareRangeCovs(a, b) {
18
+ if (a.startOffset !== b.startOffset) {
19
+ return a.startOffset - b.startOffset;
20
+ }
21
+ return b.endOffset - a.endOffset;
22
+ }
23
+
24
+ /**
25
+ * Compares two function coverages.
26
+ *
27
+ * The result corresponds to the comparison of the root ranges.
28
+ */
29
+ function compareFunctionCovs(a, b) {
30
+ return compareRangeCovs(a.ranges[0], b.ranges[0]);
31
+ }
32
+
33
+ /**
34
+ * @precodition `ranges` are well-formed and pre-order sorted
35
+ */
36
+ function fromSortedRanges(ranges) {
37
+ let root;
38
+ // Stack of parent trees and parent counts.
39
+ const stack = [];
40
+ for (const range of ranges) {
41
+ const node = new RangeTree(range.startOffset, range.endOffset, range.count, []);
42
+ if (!root) {
43
+ root = node;
44
+ stack.push([node, range.count]);
45
+ continue;
46
+ }
47
+ let parent;
48
+ let parentCount;
49
+ while (true) {
50
+ [parent, parentCount] = stack[stack.length - 1];
51
+ // assert: `top !== undefined` (the ranges are sorted)
52
+ if (range.startOffset < parent.end) {
53
+ break;
54
+ } else {
55
+ stack.pop();
56
+ }
57
+
58
+ // prevent crash when v8 incorrectly merges static_initializer's
59
+ if (stack.length === 0) {
60
+ break;
61
+ }
62
+ }
63
+ node.delta -= parentCount;
64
+ parent.children.push(node);
65
+ stack.push([node, range.count]);
66
+ }
67
+ return root;
68
+ }
69
+
70
+ /**
71
+ * Normalizes a function coverage.
72
+ *
73
+ * Sorts the ranges (pre-order sort).
74
+ * TODO: Tree-based normalization of the ranges.
75
+ *
76
+ * @param funcCov Function coverage to normalize.
77
+ */
78
+ function normalizeFunctionCov(funcCov) {
79
+ funcCov.ranges.sort(compareRangeCovs);
80
+ const tree = fromSortedRanges(funcCov.ranges);
81
+ tree.normalize();
82
+ funcCov.ranges = tree.toRanges();
83
+ }
84
+
85
+ /**
86
+ * Normalizes a script coverage.
87
+ *
88
+ * Sorts the function by root range (pre-order sort).
89
+ * This does not normalize the function coverages.
90
+ *
91
+ * @param scriptCov Script coverage to normalize.
92
+ */
93
+ function normalizeScriptCov(scriptCov) {
94
+ scriptCov.functions.sort(compareFunctionCovs);
95
+ }
96
+
97
+
98
+ /**
99
+ * Normalizes a script coverage deeply.
100
+ *
101
+ * Normalizes the function coverages deeply, then normalizes the script coverage
102
+ * itself.
103
+ *
104
+ * @param scriptCov Script coverage to normalize.
105
+ */
106
+ function deepNormalizeScriptCov(scriptCov) {
107
+ for (const funcCov of scriptCov.functions) {
108
+ normalizeFunctionCov(funcCov);
109
+ }
110
+ normalizeScriptCov(scriptCov);
111
+ }
112
+
113
+
114
+ /**
115
+ * Returns a string representation of the root range of the function.
116
+ *
117
+ * This string can be used to match function with same root range.
118
+ * The string is derived from the start and end offsets of the root range of
119
+ * the function.
120
+ * This assumes that `ranges` is non-empty (true for valid function coverages).
121
+ *
122
+ * @param funcCov Function coverage with the range to stringify
123
+ * @internal
124
+ */
125
+ function stringifyFunctionRootRange(funcCov) {
126
+ const rootRange = funcCov.ranges[0];
127
+ return `${rootRange.startOffset.toString(10)};${rootRange.endOffset.toString(10)}`;
128
+ }
129
+
130
+
131
+ function insertChild(parentToNested, parentIndex, tree) {
132
+ let nested = parentToNested.get(parentIndex);
133
+ if (!nested) {
134
+ nested = [];
135
+ parentToNested.set(parentIndex, nested);
136
+ }
137
+ nested.push(tree);
138
+ }
139
+
140
+ function nextChild(openRange, parentToNested) {
141
+ const matchingTrees = [];
142
+
143
+ for (const nested of parentToNested.values()) {
144
+ if (nested.length === 1 && nested[0].start === openRange.start && nested[0].end === openRange.end) {
145
+ matchingTrees.push(nested[0]);
146
+ } else {
147
+ matchingTrees.push(new RangeTree(
148
+ openRange.start,
149
+ openRange.end,
150
+ 0,
151
+ nested
152
+ ));
153
+ }
154
+ }
155
+ parentToNested.clear();
156
+ return mergeRangeTrees(matchingTrees);
157
+ }
158
+
159
+ class StartEventQueue {
160
+
161
+ constructor(queue) {
162
+ this.queue = queue;
163
+ this.nextIndex = 0;
164
+ this.pendingOffset = 0;
165
+ this.pendingTrees = null;
166
+ }
167
+
168
+ setPendingOffset(offset) {
169
+ this.pendingOffset = offset;
170
+ }
171
+
172
+ pushPendingTree(tree) {
173
+ if (this.pendingTrees === null) {
174
+ this.pendingTrees = [];
175
+ }
176
+ this.pendingTrees.push(tree);
177
+ }
178
+
179
+ next() {
180
+ const pendingTrees = this.pendingTrees;
181
+ const nextEvent = this.queue[this.nextIndex];
182
+ if (pendingTrees === null) {
183
+ this.nextIndex++;
184
+ return nextEvent;
185
+ }
186
+ if (!nextEvent) {
187
+ this.pendingTrees = null;
188
+ return {
189
+ offset: this.pendingOffset,
190
+ trees: pendingTrees
191
+ };
192
+ }
193
+ if (this.pendingOffset < nextEvent.offset) {
194
+ this.pendingTrees = null;
195
+ return {
196
+ offset: this.pendingOffset,
197
+ trees: pendingTrees
198
+ };
199
+ }
200
+ if (this.pendingOffset === nextEvent.offset) {
201
+ this.pendingTrees = null;
202
+ for (const tree of pendingTrees) {
203
+ nextEvent.trees.push(tree);
204
+ }
205
+ }
206
+ this.nextIndex++;
207
+ return nextEvent;
208
+
209
+
210
+ }
211
+ }
212
+
213
+ function fromParentTrees(parentTrees) {
214
+ const startToTrees = new Map();
215
+ for (const [parentIndex, parentTree] of parentTrees.entries()) {
216
+ for (const child of parentTree.children) {
217
+ let trees = startToTrees.get(child.start);
218
+ if (!trees) {
219
+ trees = [];
220
+ startToTrees.set(child.start, trees);
221
+ }
222
+ trees.push({
223
+ parentIndex,
224
+ tree: child
225
+ });
226
+ }
227
+ }
228
+ const queue = [];
229
+ for (const [startOffset, trees] of startToTrees) {
230
+ queue.push({
231
+ offset: startOffset,
232
+ trees
233
+ });
234
+ }
235
+ queue.sort((a, b) => {
236
+ return a.offset - b.offset;
237
+ });
238
+ return new StartEventQueue(queue);
239
+ }
240
+
241
+ // eslint-disable-next-line complexity
242
+ function mergeRangeTreeChildren(parentTrees) {
243
+ const result = [];
244
+ const startEventQueue = fromParentTrees(parentTrees);
245
+ const parentToNested = new Map();
246
+ let openRange = null;
247
+
248
+ while (true) {
249
+ const event = startEventQueue.next();
250
+ if (!event) {
251
+ break;
252
+ }
253
+
254
+ if (openRange !== null && openRange.end <= event.offset) {
255
+ result.push(nextChild(openRange, parentToNested));
256
+ openRange = null;
257
+ }
258
+
259
+ if (openRange === null) {
260
+ let openRangeEnd = event.offset + 1;
261
+ for (const { parentIndex, tree } of event.trees) {
262
+ openRangeEnd = Math.max(openRangeEnd, tree.end);
263
+ insertChild(parentToNested, parentIndex, tree);
264
+ }
265
+ startEventQueue.setPendingOffset(openRangeEnd);
266
+ openRange = {
267
+ start: event.offset, end: openRangeEnd
268
+ };
269
+ } else {
270
+ for (const { parentIndex, tree } of event.trees) {
271
+ if (tree.end > openRange.end) {
272
+ const right = tree.split(openRange.end);
273
+ startEventQueue.pushPendingTree({
274
+ parentIndex,
275
+ tree: right
276
+ });
277
+ }
278
+ insertChild(parentToNested, parentIndex, tree);
279
+ }
280
+ }
281
+ }
282
+ if (openRange !== null) {
283
+ result.push(nextChild(openRange, parentToNested));
284
+ }
285
+
286
+ return result;
287
+ }
288
+
289
+ /**
290
+ * @precondition Same `start` and `end` for all the trees
291
+ */
292
+ function mergeRangeTrees(trees) {
293
+ if (trees.length <= 1) {
294
+ return trees[0];
295
+ }
296
+ const first = trees[0];
297
+ let delta = 0;
298
+ for (const tree of trees) {
299
+ delta += tree.delta;
300
+ }
301
+ const children = mergeRangeTreeChildren(trees);
302
+ return new RangeTree(first.start, first.end, delta, children);
303
+ }
304
+
305
+ /**
306
+ * Merges a list of matching function coverages.
307
+ *
308
+ * Functions are matching if their root ranges have the same span.
309
+ * The result is normalized.
310
+ * The input values may be mutated, it is not safe to use them after passing
311
+ * them to this function.
312
+ * The computation is synchronous.
313
+ *
314
+ * @param funcCovs Function coverages to merge.
315
+ * @return Merged function coverage, or `undefined` if the input list was empty.
316
+ */
317
+ function mergeFunctionCovs(funcCovs) {
318
+ if (funcCovs.length === 0) {
319
+ return;
320
+ }
321
+
322
+ if (funcCovs.length === 1) {
323
+ const merged = funcCovs[0];
324
+ normalizeFunctionCov(merged);
325
+ return merged;
326
+ }
327
+
328
+ const first = funcCovs[0];
329
+ const functionName = first.functionName;
330
+ // assert: `first.ranges.length > 0`
331
+ const startOffset = first.ranges[0].startOffset;
332
+ const endOffset = first.ranges[0].endOffset;
333
+ let count = 0;
334
+
335
+ const trees = [];
336
+ for (const funcCov of funcCovs) {
337
+ // assert: `funcCov.ranges.length > 0`
338
+ // assert: `funcCov.ranges` is sorted
339
+ count += typeof funcCov.count === 'number' ? funcCov.count : funcCov.ranges[0].count;
340
+ if (funcCov.isBlockCoverage) {
341
+ trees.push(fromSortedRanges(funcCov.ranges));
342
+ }
343
+ }
344
+
345
+ let isBlockCoverage;
346
+ let ranges;
347
+ if (trees.length > 0) {
348
+ isBlockCoverage = true;
349
+ const mergedTree = mergeRangeTrees(trees);
350
+ mergedTree.normalize();
351
+ ranges = mergedTree.toRanges();
352
+ } else {
353
+ isBlockCoverage = false;
354
+ ranges = [{
355
+ startOffset,
356
+ endOffset,
357
+ count
358
+ }];
359
+ }
360
+
361
+ const merged = {
362
+ functionName,
363
+ ranges,
364
+ isBlockCoverage
365
+ };
366
+ if (count !== ranges[0].count) {
367
+ merged.count = count;
368
+ }
369
+ // assert: `merged` is normalized
370
+ return merged;
371
+ }
372
+
373
+
374
+ /**
375
+ * Merges a list of matching script coverages.
376
+ *
377
+ * Scripts are matching if they have the same `url`.
378
+ * The result is normalized.
379
+ * The input values may be mutated, it is not safe to use them after passing
380
+ * them to this function.
381
+ * The computation is synchronous.
382
+ *
383
+ * @param scriptCovs Process coverages to merge.
384
+ * @return Merged script coverage, or `undefined` if the input list was empty.
385
+ */
386
+
387
+ // eslint-disable-next-line complexity
388
+ function mergeV8Coverage(scriptCovs) {
389
+ if (!Array.isArray(scriptCovs)) {
390
+ return {
391
+ functions: []
392
+ };
393
+ }
394
+
395
+ if (scriptCovs.length === 0) {
396
+ return {
397
+ functions: []
398
+ };
399
+ }
400
+
401
+ if (scriptCovs.length === 1) {
402
+ const merged = scriptCovs[0];
403
+ deepNormalizeScriptCov(merged);
404
+ return merged;
405
+ }
406
+
407
+
408
+ const rangeToFuncs = new Map();
409
+ for (const scriptCov of scriptCovs) {
410
+ for (const funcCov of scriptCov.functions) {
411
+ const rootRange = stringifyFunctionRootRange(funcCov);
412
+ let funcCovs = rangeToFuncs.get(rootRange);
413
+ if (!funcCovs) {
414
+ funcCovs = [];
415
+ rangeToFuncs.set(rootRange, funcCovs);
416
+ }
417
+ funcCovs.push(funcCov);
418
+ }
419
+ }
420
+
421
+ const functions = [];
422
+ for (const funcCovs of rangeToFuncs.values()) {
423
+ // assert: `funcCovs.length > 0`
424
+ const block = mergeFunctionCovs(funcCovs);
425
+ if (block) {
426
+ functions.push(block);
427
+ }
428
+ }
429
+
430
+ const merged = {
431
+ functions
432
+ };
433
+ normalizeScriptCov(merged);
434
+ return merged;
435
+ }
436
+
437
+ module.exports = {
438
+ mergeV8Coverage
439
+ };