UNPKG

4.7 kBPlain TextView Raw
1import libCoverage from "istanbul-lib-coverage";
2import { Position, SourceLocation } from "./index";
3
4export class CoverageMapBuilder {
5 private readonly files: Map<string, FileCoverageBuilder>;
6
7 constructor() {
8 this.files = new Map();
9 }
10
11 addStatement(url: string, loc: SourceLocation, count: number): void {
12 this.getOrCreateFileCoverageBuilder(url).addStatement(loc, count);
13 }
14
15 addFunction(url: string, decl: SourceLocation, loc: SourceLocation, count: number, name: string): void {
16 this.getOrCreateFileCoverageBuilder(url).addFunction(decl, loc, count, name);
17 }
18
19 addBranch(url: string, mainLoc: SourceLocation, arms: Iterable<[SourceLocation, number]>, type: string): void {
20 this.getOrCreateFileCoverageBuilder(url).addBranch(mainLoc, arms, type);
21 }
22
23 build(): libCoverage.CoverageMapData {
24 const data: libCoverage.CoverageMapData = Object.create(null);
25 for (const [url, file] of this.files) {
26 data[url] = libCoverage.createFileCoverage(file.build(url));
27 }
28 return data;
29 }
30
31 private getOrCreateFileCoverageBuilder(url: string): FileCoverageBuilder {
32 let builder: FileCoverageBuilder | undefined = this.files.get(url);
33 if (builder === undefined) {
34 builder = new FileCoverageBuilder();
35 this.files.set(url, builder);
36 }
37 return builder;
38 }
39}
40
41interface Branch {
42 readonly loc: SourceLocation;
43 readonly arms: ReadonlyArray<BranchArm>;
44 readonly type: string;
45}
46
47interface BranchArm {
48 readonly loc: SourceLocation;
49 count: number;
50}
51
52interface Function {
53 readonly decl: SourceLocation;
54 readonly loc: SourceLocation;
55 name?: string;
56 count: number;
57}
58
59interface Statement {
60 readonly loc: SourceLocation;
61 count: number;
62}
63
64export class FileCoverageBuilder {
65 private readonly branches: Map<string, Branch>;
66 private readonly functions: Map<string, Function>;
67 private readonly statements: Map<string, Statement>;
68
69 constructor() {
70 this.branches = new Map();
71 this.functions = new Map();
72 this.statements = new Map();
73 }
74
75 addStatement(loc: SourceLocation, count: number): void {
76 const hash: string = hashSourceLocation(loc);
77 let statement: Statement | undefined = this.statements.get(hash);
78 if (statement === undefined) {
79 statement = {loc, count: 0};
80 this.statements.set(hash, statement);
81 }
82 statement.count += count;
83 }
84
85 addFunction(decl: SourceLocation, loc: SourceLocation, count: number, name?: string): void {
86 const hash: string = hashSourceLocation(decl);
87 let func: Function | undefined = this.functions.get(hash);
88 if (func === undefined) {
89 func = {decl, loc, name, count: 0};
90 this.functions.set(hash, func);
91 }
92 func.count += count;
93 if (func.name === undefined) {
94 func.name = name;
95 }
96 }
97
98 addBranch(mainLoc: SourceLocation, arms: Iterable<[SourceLocation, number]>, type: string): void {
99 const armHashes: string[] = [];
100 for (const [loc, _] of arms) {
101 armHashes.push(hashSourceLocation(loc));
102 }
103 const hash: string = `${hashSourceLocation(mainLoc)}(${armHashes.join(",")})`;
104 let branch: Branch | undefined = this.branches.get(hash);
105 if (branch === undefined) {
106 const newArms: BranchArm[] = [];
107 for (const [loc, _] of arms) {
108 newArms.push({loc, count: 0});
109 }
110 branch = {type, loc: mainLoc, arms: newArms};
111 this.branches.set(hash, branch);
112 }
113 let i: number = 0;
114 for (const [_, count] of arms) {
115 branch.arms[i].count += count;
116 i++;
117 }
118 }
119
120 build(url: string): libCoverage.FileCoverageData {
121 const statementMap: Record<string, any> = Object.create(null);
122 const s: Record<string, number> = Object.create(null);
123 let sid: number = 0;
124 for (const {loc, count} of this.statements.values()) {
125 const key: string = `s${sid}`;
126 sid++;
127 statementMap[key] = loc;
128 s[key] = count;
129 }
130
131 const fnMap: Record<string, any> = Object.create(null);
132 const f: Record<string, number> = Object.create(null);
133 let fid: number = 0;
134 for (const {decl, loc, name, count} of this.functions.values()) {
135 const key: string = `f${fid}`;
136 fid++;
137 fnMap[key] = {decl, loc, name: name === undefined ? `unknown_${key}` : name};
138 f[key] = count;
139 }
140
141 const branchMap: Record<string, any> = Object.create(null);
142 const b: Record<string, number[]> = Object.create(null);
143
144 return {path: url, statementMap, s, fnMap, f, branchMap, b};
145 }
146}
147
148export function hashSourceLocation(loc: SourceLocation): string {
149 return `${hashPosition(loc.start)}-${hashPosition(loc.end)}`;
150}
151
152export function hashPosition(pos: Position): string {
153 return `${pos.line}:${pos.column}`;
154}