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  }