github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/gopherage/cmd/html/static/parser.ts (about) 1 /* 2 Copyright 2018 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 import {filter, reduce} from './utils'; 18 19 export interface Pos { 20 line: number; 21 col: number; 22 } 23 24 export interface Block { 25 statements: number; 26 hits: number; 27 start: Pos; 28 end: Pos; 29 } 30 31 32 export class FileCoverage { 33 blocks: Block[] = []; 34 35 constructor(readonly filename: string, readonly fileNumber: number) {} 36 37 addBlock(block: Block) { 38 this.blocks.push(block); 39 } 40 41 get totalStatements(): number { 42 return this.blocks.reduce((acc, b) => acc + b.statements, 0); 43 } 44 45 get coveredStatements(): number { 46 return this.blocks.reduce( 47 (acc, b) => acc + (b.hits > 0 ? b.statements : 0), 0); 48 } 49 } 50 51 export class Coverage { 52 files = new Map<string, FileCoverage>(); 53 54 constructor(readonly mode: string, readonly prefix = '') {} 55 56 addFile(file: FileCoverage): void { 57 this.files.set(file.filename, file); 58 } 59 60 getFile(name: string): FileCoverage|undefined { 61 return this.files.get(name); 62 } 63 64 getFilesWithPrefix(prefix: string): Map<string, FileCoverage> { 65 return new Map(filter( 66 this.files.entries(), ([k]) => k.startsWith(this.prefix + prefix))); 67 } 68 69 getCoverageForPrefix(prefix: string): Coverage { 70 const subCoverage = new Coverage(this.mode, this.prefix + prefix); 71 for (const [filename, file] of this.files) { 72 if (filename.startsWith(this.prefix + prefix)) { 73 subCoverage.addFile(file); 74 } 75 } 76 return subCoverage; 77 } 78 79 get children(): Map<string, Coverage> { 80 const children = new Map(); 81 for (const path of this.files.keys()) { 82 let [dir, rest] = path.substr(this.prefix.length).split('/', 2); 83 if (!children.has(dir)) { 84 if (rest) { 85 dir += '/'; 86 } 87 children.set(dir, this.getCoverageForPrefix(dir)); 88 } 89 } 90 return children; 91 } 92 93 get basename(): string { 94 if (this.prefix.endsWith('/')) { 95 return this.prefix.substring(0, this.prefix.length - 1).split('/').pop() + 96 '/'; 97 } 98 return this.prefix.split('/').pop()!; 99 } 100 101 get totalStatements(): number { 102 return reduce(this.files.values(), (acc, f) => acc + f.totalStatements, 0); 103 } 104 105 get coveredStatements(): number { 106 return reduce( 107 this.files.values(), (acc, f) => acc + f.coveredStatements, 0); 108 } 109 110 get totalFiles(): number { 111 return this.files.size; 112 } 113 114 get coveredFiles(): number { 115 return reduce( 116 this.files.values(), 117 (acc, f) => acc + (f.coveredStatements > 0 ? 1 : 0), 0); 118 } 119 } 120 121 export function parseCoverage(content: string): Coverage { 122 const lines = content.split('\n'); 123 const modeLine = lines.shift()!; 124 const [modeLabel, mode] = modeLine.split(':').map(x => x.trim()); 125 if (modeLabel !== 'mode') { 126 throw new Error('Expected to start with mode line.'); 127 } 128 129 const coverage = new Coverage(mode); 130 let fileCounter = 0; 131 for (const line of lines) { 132 if (line === '') { 133 continue; 134 } 135 const {filename, ...block} = parseLine(line); 136 let file = coverage.getFile(filename); 137 if (!file) { 138 file = new FileCoverage(filename, fileCounter++); 139 coverage.addFile(file); 140 } 141 file.addBlock(block); 142 } 143 144 return coverage; 145 } 146 147 function parseLine(line: string): Block&{filename: string} { 148 const [filename, block] = line.split(':'); 149 const [positions, statements, hits] = block.split(' '); 150 const [start, end] = positions.split(','); 151 const [startLine, startCol] = start.split('.').map(parseInt); 152 const [endLine, endCol] = end.split('.').map(parseInt); 153 return { 154 filename, 155 statements: Number(statements), 156 hits: Math.max(0, Number(hits)), 157 start: { 158 line: startLine, 159 col: startCol, 160 }, 161 end: { 162 line: endLine, 163 col: endCol, 164 }, 165 }; 166 }