Add node modules and new code for release
This commit is contained in:
parent
a10d84bc2e
commit
90f63cb10d
7655 changed files with 1763577 additions and 14 deletions
343
node_modules/@bcoe/v8-coverage/dist/lib/_src/merge.ts
generated
vendored
Normal file
343
node_modules/@bcoe/v8-coverage/dist/lib/_src/merge.ts
generated
vendored
Normal file
|
@ -0,0 +1,343 @@
|
|||
import {
|
||||
deepNormalizeScriptCov,
|
||||
normalizeFunctionCov,
|
||||
normalizeProcessCov,
|
||||
normalizeRangeTree,
|
||||
normalizeScriptCov,
|
||||
} from "./normalize";
|
||||
import { RangeTree } from "./range-tree";
|
||||
import { FunctionCov, ProcessCov, Range, RangeCov, ScriptCov } from "./types";
|
||||
|
||||
/**
|
||||
* Merges a list of process coverages.
|
||||
*
|
||||
* The result is normalized.
|
||||
* The input values may be mutated, it is not safe to use them after passing
|
||||
* them to this function.
|
||||
* The computation is synchronous.
|
||||
*
|
||||
* @param processCovs Process coverages to merge.
|
||||
* @return Merged process coverage.
|
||||
*/
|
||||
export function mergeProcessCovs(processCovs: ReadonlyArray<ProcessCov>): ProcessCov {
|
||||
if (processCovs.length === 0) {
|
||||
return {result: []};
|
||||
}
|
||||
|
||||
const urlToScripts: Map<string, ScriptCov[]> = new Map();
|
||||
for (const processCov of processCovs) {
|
||||
for (const scriptCov of processCov.result) {
|
||||
let scriptCovs: ScriptCov[] | undefined = urlToScripts.get(scriptCov.url);
|
||||
if (scriptCovs === undefined) {
|
||||
scriptCovs = [];
|
||||
urlToScripts.set(scriptCov.url, scriptCovs);
|
||||
}
|
||||
scriptCovs.push(scriptCov);
|
||||
}
|
||||
}
|
||||
|
||||
const result: ScriptCov[] = [];
|
||||
for (const scripts of urlToScripts.values()) {
|
||||
// assert: `scripts.length > 0`
|
||||
result.push(mergeScriptCovs(scripts)!);
|
||||
}
|
||||
const merged: ProcessCov = {result};
|
||||
|
||||
normalizeProcessCov(merged);
|
||||
return merged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges a list of matching script coverages.
|
||||
*
|
||||
* Scripts are matching if they have the same `url`.
|
||||
* The result is normalized.
|
||||
* The input values may be mutated, it is not safe to use them after passing
|
||||
* them to this function.
|
||||
* The computation is synchronous.
|
||||
*
|
||||
* @param scriptCovs Process coverages to merge.
|
||||
* @return Merged script coverage, or `undefined` if the input list was empty.
|
||||
*/
|
||||
export function mergeScriptCovs(scriptCovs: ReadonlyArray<ScriptCov>): ScriptCov | undefined {
|
||||
if (scriptCovs.length === 0) {
|
||||
return undefined;
|
||||
} else if (scriptCovs.length === 1) {
|
||||
const merged: ScriptCov = scriptCovs[0];
|
||||
deepNormalizeScriptCov(merged);
|
||||
return merged;
|
||||
}
|
||||
|
||||
const first: ScriptCov = scriptCovs[0];
|
||||
const scriptId: string = first.scriptId;
|
||||
const url: string = first.url;
|
||||
|
||||
const rangeToFuncs: Map<string, FunctionCov[]> = new Map();
|
||||
for (const scriptCov of scriptCovs) {
|
||||
for (const funcCov of scriptCov.functions) {
|
||||
const rootRange: string = stringifyFunctionRootRange(funcCov);
|
||||
let funcCovs: FunctionCov[] | undefined = rangeToFuncs.get(rootRange);
|
||||
|
||||
if (funcCovs === undefined ||
|
||||
// if the entry in rangeToFuncs is function-level granularity and
|
||||
// the new coverage is block-level, prefer block-level.
|
||||
(!funcCovs[0].isBlockCoverage && funcCov.isBlockCoverage)) {
|
||||
funcCovs = [];
|
||||
rangeToFuncs.set(rootRange, funcCovs);
|
||||
} else if (funcCovs[0].isBlockCoverage && !funcCov.isBlockCoverage) {
|
||||
// if the entry in rangeToFuncs is block-level granularity, we should
|
||||
// not append function level granularity.
|
||||
continue;
|
||||
}
|
||||
funcCovs.push(funcCov);
|
||||
}
|
||||
}
|
||||
|
||||
const functions: FunctionCov[] = [];
|
||||
for (const funcCovs of rangeToFuncs.values()) {
|
||||
// assert: `funcCovs.length > 0`
|
||||
functions.push(mergeFunctionCovs(funcCovs)!);
|
||||
}
|
||||
|
||||
const merged: ScriptCov = {scriptId, url, functions};
|
||||
normalizeScriptCov(merged);
|
||||
return merged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the root range of the function.
|
||||
*
|
||||
* This string can be used to match function with same root range.
|
||||
* The string is derived from the start and end offsets of the root range of
|
||||
* the function.
|
||||
* This assumes that `ranges` is non-empty (true for valid function coverages).
|
||||
*
|
||||
* @param funcCov Function coverage with the range to stringify
|
||||
* @internal
|
||||
*/
|
||||
function stringifyFunctionRootRange(funcCov: Readonly<FunctionCov>): string {
|
||||
const rootRange: RangeCov = funcCov.ranges[0];
|
||||
return `${rootRange.startOffset.toString(10)};${rootRange.endOffset.toString(10)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges a list of matching function coverages.
|
||||
*
|
||||
* Functions are matching if their root ranges have the same span.
|
||||
* The result is normalized.
|
||||
* The input values may be mutated, it is not safe to use them after passing
|
||||
* them to this function.
|
||||
* The computation is synchronous.
|
||||
*
|
||||
* @param funcCovs Function coverages to merge.
|
||||
* @return Merged function coverage, or `undefined` if the input list was empty.
|
||||
*/
|
||||
export function mergeFunctionCovs(funcCovs: ReadonlyArray<FunctionCov>): FunctionCov | undefined {
|
||||
if (funcCovs.length === 0) {
|
||||
return undefined;
|
||||
} else if (funcCovs.length === 1) {
|
||||
const merged: FunctionCov = funcCovs[0];
|
||||
normalizeFunctionCov(merged);
|
||||
return merged;
|
||||
}
|
||||
|
||||
const functionName: string = funcCovs[0].functionName;
|
||||
|
||||
const trees: RangeTree[] = [];
|
||||
for (const funcCov of funcCovs) {
|
||||
// assert: `fn.ranges.length > 0`
|
||||
// assert: `fn.ranges` is sorted
|
||||
trees.push(RangeTree.fromSortedRanges(funcCov.ranges)!);
|
||||
}
|
||||
|
||||
// assert: `trees.length > 0`
|
||||
const mergedTree: RangeTree = mergeRangeTrees(trees)!;
|
||||
normalizeRangeTree(mergedTree);
|
||||
const ranges: RangeCov[] = mergedTree.toRanges();
|
||||
const isBlockCoverage: boolean = !(ranges.length === 1 && ranges[0].count === 0);
|
||||
|
||||
const merged: FunctionCov = {functionName, ranges, isBlockCoverage};
|
||||
// assert: `merged` is normalized
|
||||
return merged;
|
||||
}
|
||||
|
||||
/**
|
||||
* @precondition Same `start` and `end` for all the trees
|
||||
*/
|
||||
function mergeRangeTrees(trees: ReadonlyArray<RangeTree>): RangeTree | undefined {
|
||||
if (trees.length <= 1) {
|
||||
return trees[0];
|
||||
}
|
||||
const first: RangeTree = trees[0];
|
||||
let delta: number = 0;
|
||||
for (const tree of trees) {
|
||||
delta += tree.delta;
|
||||
}
|
||||
const children: RangeTree[] = mergeRangeTreeChildren(trees);
|
||||
return new RangeTree(first.start, first.end, delta, children);
|
||||
}
|
||||
|
||||
class RangeTreeWithParent {
|
||||
readonly parentIndex: number;
|
||||
readonly tree: RangeTree;
|
||||
|
||||
constructor(parentIndex: number, tree: RangeTree) {
|
||||
this.parentIndex = parentIndex;
|
||||
this.tree = tree;
|
||||
}
|
||||
}
|
||||
|
||||
class StartEvent {
|
||||
readonly offset: number;
|
||||
readonly trees: RangeTreeWithParent[];
|
||||
|
||||
constructor(offset: number, trees: RangeTreeWithParent[]) {
|
||||
this.offset = offset;
|
||||
this.trees = trees;
|
||||
}
|
||||
|
||||
static compare(a: StartEvent, b: StartEvent): number {
|
||||
return a.offset - b.offset;
|
||||
}
|
||||
}
|
||||
|
||||
class StartEventQueue {
|
||||
private readonly queue: StartEvent[];
|
||||
private nextIndex: number;
|
||||
private pendingOffset: number;
|
||||
private pendingTrees: RangeTreeWithParent[] | undefined;
|
||||
|
||||
private constructor(queue: StartEvent[]) {
|
||||
this.queue = queue;
|
||||
this.nextIndex = 0;
|
||||
this.pendingOffset = 0;
|
||||
this.pendingTrees = undefined;
|
||||
}
|
||||
|
||||
static fromParentTrees(parentTrees: ReadonlyArray<RangeTree>): StartEventQueue {
|
||||
const startToTrees: Map<number, RangeTreeWithParent[]> = new Map();
|
||||
for (const [parentIndex, parentTree] of parentTrees.entries()) {
|
||||
for (const child of parentTree.children) {
|
||||
let trees: RangeTreeWithParent[] | undefined = startToTrees.get(child.start);
|
||||
if (trees === undefined) {
|
||||
trees = [];
|
||||
startToTrees.set(child.start, trees);
|
||||
}
|
||||
trees.push(new RangeTreeWithParent(parentIndex, child));
|
||||
}
|
||||
}
|
||||
const queue: StartEvent[] = [];
|
||||
for (const [startOffset, trees] of startToTrees) {
|
||||
queue.push(new StartEvent(startOffset, trees));
|
||||
}
|
||||
queue.sort(StartEvent.compare);
|
||||
return new StartEventQueue(queue);
|
||||
}
|
||||
|
||||
setPendingOffset(offset: number): void {
|
||||
this.pendingOffset = offset;
|
||||
}
|
||||
|
||||
pushPendingTree(tree: RangeTreeWithParent): void {
|
||||
if (this.pendingTrees === undefined) {
|
||||
this.pendingTrees = [];
|
||||
}
|
||||
this.pendingTrees.push(tree);
|
||||
}
|
||||
|
||||
next(): StartEvent | undefined {
|
||||
const pendingTrees: RangeTreeWithParent[] | undefined = this.pendingTrees;
|
||||
const nextEvent: StartEvent | undefined = this.queue[this.nextIndex];
|
||||
if (pendingTrees === undefined) {
|
||||
this.nextIndex++;
|
||||
return nextEvent;
|
||||
} else if (nextEvent === undefined) {
|
||||
this.pendingTrees = undefined;
|
||||
return new StartEvent(this.pendingOffset, pendingTrees);
|
||||
} else {
|
||||
if (this.pendingOffset < nextEvent.offset) {
|
||||
this.pendingTrees = undefined;
|
||||
return new StartEvent(this.pendingOffset, pendingTrees);
|
||||
} else {
|
||||
if (this.pendingOffset === nextEvent.offset) {
|
||||
this.pendingTrees = undefined;
|
||||
for (const tree of pendingTrees) {
|
||||
nextEvent.trees.push(tree);
|
||||
}
|
||||
}
|
||||
this.nextIndex++;
|
||||
return nextEvent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function mergeRangeTreeChildren(parentTrees: ReadonlyArray<RangeTree>): RangeTree[] {
|
||||
const result: RangeTree[] = [];
|
||||
const startEventQueue: StartEventQueue = StartEventQueue.fromParentTrees(parentTrees);
|
||||
const parentToNested: Map<number, RangeTree[]> = new Map();
|
||||
let openRange: Range | undefined;
|
||||
|
||||
while (true) {
|
||||
const event: StartEvent | undefined = startEventQueue.next();
|
||||
if (event === undefined) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (openRange !== undefined && openRange.end <= event.offset) {
|
||||
result.push(nextChild(openRange, parentToNested));
|
||||
openRange = undefined;
|
||||
}
|
||||
|
||||
if (openRange === undefined) {
|
||||
let openRangeEnd: number = event.offset + 1;
|
||||
for (const {parentIndex, tree} of event.trees) {
|
||||
openRangeEnd = Math.max(openRangeEnd, tree.end);
|
||||
insertChild(parentToNested, parentIndex, tree);
|
||||
}
|
||||
startEventQueue.setPendingOffset(openRangeEnd);
|
||||
openRange = {start: event.offset, end: openRangeEnd};
|
||||
} else {
|
||||
for (const {parentIndex, tree} of event.trees) {
|
||||
if (tree.end > openRange.end) {
|
||||
const right: RangeTree = tree.split(openRange.end);
|
||||
startEventQueue.pushPendingTree(new RangeTreeWithParent(parentIndex, right));
|
||||
}
|
||||
insertChild(parentToNested, parentIndex, tree);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (openRange !== undefined) {
|
||||
result.push(nextChild(openRange, parentToNested));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function insertChild(parentToNested: Map<number, RangeTree[]>, parentIndex: number, tree: RangeTree): void {
|
||||
let nested: RangeTree[] | undefined = parentToNested.get(parentIndex);
|
||||
if (nested === undefined) {
|
||||
nested = [];
|
||||
parentToNested.set(parentIndex, nested);
|
||||
}
|
||||
nested.push(tree);
|
||||
}
|
||||
|
||||
function nextChild(openRange: Range, parentToNested: Map<number, RangeTree[]>): RangeTree {
|
||||
const matchingTrees: RangeTree[] = [];
|
||||
|
||||
for (const nested of parentToNested.values()) {
|
||||
if (nested.length === 1 && nested[0].start === openRange.start && nested[0].end === openRange.end) {
|
||||
matchingTrees.push(nested[0]);
|
||||
} else {
|
||||
matchingTrees.push(new RangeTree(
|
||||
openRange.start,
|
||||
openRange.end,
|
||||
0,
|
||||
nested,
|
||||
));
|
||||
}
|
||||
}
|
||||
parentToNested.clear();
|
||||
return mergeRangeTrees(matchingTrees)!;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue