...

Text file src/edge-infra.dev/third_party/gopherage/cmd/html/ts/parser.ts

Documentation: edge-infra.dev/third_party/gopherage/cmd/html/ts

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

View as plain text