lcov.js

'use strict';

/**
 * @module util/lcov
 */

const fs = require('fs');

const lcov = {
  walkFile: function(str, cb) {
      let data = [];
      let item;

      ['end_of_record'].concat(str.split('\n')).forEach((line) => {
          const allparts = line.trim().split(':');
          const parts = [allparts.shift(), allparts.join(':')];
          let lines, fn;

          switch (parts[0].toUpperCase()) {
              case 'TN':
                  item.title = parts[1].trim();
                  break;
              case 'SF':
                  item.file = parts.slice(1).join(':').trim();
                  break;
              case 'FNF':
                  item.functions.found = Number(parts[1].trim());
                  break;
              case 'FNH':
                  item.functions.hit = Number(parts[1].trim());
                  break;
              case 'LF':
                  item.lines.found = Number(parts[1].trim());
                  break;
              case 'LH':
                  item.lines.hit = Number(parts[1].trim());
                  break;
              case 'DA':
                  lines = parts[1].split(',');
                  item.lines.details.push({
                      line: Number(lines[0]),
                      hit: Number(lines[1])
                  });
                  break;
              case 'FN':
                  fn = parts[1].split(',');
                  item.functions.details.push({
                      name: fn[1],
                      line: Number(fn[0])
                  });
                  break;
              case 'FNDA':
                  fn = parts[1].split(',');
                  item.functions.details.some((i, k) => {
                      if (i.name === fn[1] && i.hit === undefined) {
                          item.functions.details[k].hit = Number(fn[0]);
                          return true;
                      }
                  });
                  break;
              case 'BRDA':
                  fn = parts[1].split(',');
                  item.branches.details.push({
                      line: Number(fn[0]),
                      block: Number(fn[1]),
                      branch: Number(fn[2]),
                      taken: ((fn[3] === '-') ? 0 : Number(fn[3]))
                  });
                  break;
              case 'BRF':
                  item.branches.found = Number(parts[1]);
                  break;
              case 'BRH':
                  item.branches.hit = Number(parts[1]);
                  break;
          }

          if (line.indexOf('end_of_record') > -1) {
              data.push(item);
              item = {
                  lines: {
                      found: 0,
                      hit: 0,
                      details: []
                  },
                  functions: {
                      hit: 0,
                      found: 0,
                      details: []
                  },
                  branches: {
                      hit: 0,
                      found: 0,
                      details: []
                  }
              };
          }
      });

      data.shift();

      if (data.length) {
          cb(undefined, data);
      } else {
          cb('Failed to parse string');
      }
  },
  parse: (file) => {
      return new Promise(function(resolve, reject) {
          if(fs.existsSync(file)) {
            fs.readFile(file, 'utf8', (err, str) => {
                if(err) {
                  reject(err);
                }
                return lcov.walkFile(str, function(err, result) {
                  if(err) return reject(err);
                  resolve(result);
                });
            });
          } else {
            return lcov.walkFile(file, function(err, result) {
              if(err) return reject(err);
              resolve(result);
            });
          }
    });
  }
};

module.exports = lcov;