aboutsummaryrefslogtreecommitdiff
path: root/src/CoverageListing.js
blob: 72a5ea8ba67bbb3542fe312580efd9ccea8d61e4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/*
 * JaCoCo Report Viewer, a web-based coverage report viewer
 * Copyright (C) 2018  Pacien TRAN-GIRARD
 *                     Adam NAILI
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

import React, { Component } from 'react';

export class CoverageListing extends Component {
  constructor(props) {
    super(props);
    this.state = {
      listingContent: null,
      coverageMap: null
    };
  }

  componentDidMount() {
    this._updateState();
  }

  componentDidUpdate(prevProps) {
    if (this.props.fileName === prevProps.fileName &&
        this.props.sourceSet === prevProps.sourceSet &&
        this.props.coverage === prevProps.coverage) return;

    this._updateState();
  }

  _updateState() {
    if (!this.props.sourceSet || !(this.props.fileName in this.props.sourceSet)) {
      this.setState({ listingContent: null });
      return;
    }

    this._updateListingContent();
    this._updateCoverageMap();
  }

  _updateCoverageMap() {
    const coverageMap = this.props.coverage.reduce(function(map, line) {
      map[parseInt(line.$.nr)] = line.$;
      return map;
    }, {});

    this.setState({ coverageMap: coverageMap });
  }

  _updateListingContent() {
    this.props.sourceSet[this.props.fileName]
              .async('text')
              .then(content => this.setState({ listingContent: content }));
  }

  _lineCoverageString(coverage) {
    const instructionCount = parseInt(coverage.ci) + parseInt(coverage.mi);
    const branchCount = parseInt(coverage.cb) + parseInt(coverage.mb);
    return coverage.ci + '/' + instructionCount + ' instructions covered, ' +
           coverage.cb + '/' + branchCount + ' branches covered';
  }

  _renderNone() {
    return (<div>No source file provided.</div>);
  }

  _nonEmptyString(str) {
    return str ? str : ' '; // workaround for empty <pre> collapsing
  }

  _renderLine(lineContent, lineNumber) {
    if (!(lineNumber in this.state.coverageMap))
      return (<li key={lineNumber}><pre>{lineContent}</pre></li>);

    const coverage = this.state.coverageMap[lineNumber];
    const wellCovered = parseInt(coverage.mi) === 0 && parseInt(coverage.mb) === 0;

    return (
      <li key={lineNumber} well-covered={wellCovered.toString()} title={this._lineCoverageString(coverage)}>
        <pre>{lineContent}</pre>
      </li>
    );
  }

  _renderListing() {
    const lines = this.state.listingContent
                            .split('\n')
                            .map(this._nonEmptyString)
                            .map((line, index) => this._renderLine(line, index + 1));

    return (<div className="listing"><ol>{lines}</ol></div>);
  }

  render() {
    return this.state.listingContent ? this._renderListing() : this._renderNone();
  }
}