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         153x 153x                     136x                   136x   136x 394x     136x 136x 260x     136x 136x 157x 157x           136x 136x 274x                     110x   110x 1214x 1214x 1214x       1214x   1214x 384x 250x 266x 76x 90x 18x 65x 65x         110x   110x 130x   130x 157x     157x 61x               110x       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;