All files / solidity-coverage/lib coverage.js

100% Statements 53/53
100% Branches 12/12
100% Functions 4/4
100% Lines 41/41

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 110 111 112 113          1x         154x 154x                     138x                   138x   138x 400x     138x 138x 267x     138x 138x 158x 158x           138x 138x 278x                     111x   111x 1233x 1233x 1233x       1233x   1233x 390x 257x 270x 76x 90x 18x 66x 66x         111x   111x 132x   132x 158x     158x 62x               111x       1x  
/**
 * Converts instrumentation data accumulated a the vm steps to an instanbul spec coverage object.
 * @type {Coverage}
 */
 
const util = require('util');
 
class Coverage {
 
  constructor() {
    this.data = {};
    this.requireData = {};
  }
 
  /**
   * Initializes an entry in the coverage map for an instrumented contract. Tracks by
   * its canonical contract path, e.g. *not* by its location in the temp folder.
   * @param {Object} info          'info = instrumenter.instrument(contract, fileName, true)'
   * @param {String} contractPath   canonical path to contract file
   */
 
  addContract(info, contractPath) {
    this.data[contractPath] = {
      l: {},
      path: contractPath,
      s: {},
      b: {},
      f: {},
      fnMap: {},
      statementMap: {},
      branchMap: {},
    };
    this.requireData[contractPath] = { };
 
    info.runnableLines.forEach((item, idx) => {
      this.data[contractPath].l[info.runnableLines[idx]] = 0;
    });
 
    this.data[contractPath].fnMap = info.fnMap;
    for (let x = 1; x <= Object.keys(info.fnMap).length; x++) {
      this.data[contractPath].f[x] = 0;
    }
 
    this.data[contractPath].branchMap = info.branchMap;
    for (let x = 1; x <= Object.keys(info.branchMap).length; x++) {
      this.data[contractPath].b[x] = [0, 0];
      this.requireData[contractPath][x] = {
        preEvents: 0,
        postEvents: 0,
      };
    }
 
    this.data[contractPath].statementMap = info.statementMap;
    for (let x = 1; x <= Object.keys(info.statementMap).length; x++) {
      this.data[contractPath].s[x] = 0;
    }
  }
 
  /**
   * Populates an empty coverage map with values derived from a hash map of
   * data collected as the instrumented contracts are tested
   * @param  {Object} map of collected instrumentation data
   * @return {Object} coverage map.
   */
  generate(collectedData) {
    const hashes = Object.keys(collectedData);
 
    for (let hash of hashes){
      const data = collectedData[hash];
      const contractPath = collectedData[hash].contractPath;
      const id = collectedData[hash].id;
 
      // NB: Any branch using the injected fn which returns boolean will have artificially
      // doubled hits (because of something internal to Solidity about how the stack is managed)
      const hits = collectedData[hash].hits;
 
      switch(collectedData[hash].type){
        case 'line':       this.data[contractPath].l[id] = hits;                   break;
        case 'function':   this.data[contractPath].f[id] = hits;                   break;
        case 'statement':  this.data[contractPath].s[id] = hits;                   break;
        case 'branch':     this.data[contractPath].b[id][data.locationIdx] = hits; break;
        case 'and-true':   this.data[contractPath].b[id][data.locationIdx] = hits; break;
        case 'or-false':   this.data[contractPath].b[id][data.locationIdx] = hits; break;
        case 'requirePre':  this.requireData[contractPath][id].preEvents = hits;   break;
        case 'requirePost': this.requireData[contractPath][id].postEvents = hits;  break;
      }
    }
 
    // Finally, interpret the assert pre/post events
    const contractPaths = Object.keys(this.requireData);
 
    for (let contractPath of contractPaths){
      const contract = this.data[contractPath];
 
      for (let i = 1; i <= Object.keys(contract.b).length; i++) {
        const branch = this.requireData[contractPath][i];
 
        // Was it an assert branch?
        if (branch && branch.preEvents > 0){
          this.data[contractPath].b[i] = [
            branch.postEvents,
            branch.preEvents - branch.postEvents
          ]
        }
      }
    }
 
    return Object.assign({}, this.data);
  }
};
 
module.exports = Coverage;