run-check-coverage.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. /*
  2. Copyright 2012-2015, Yahoo Inc.
  3. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
  4. */
  5. var path = require('path'),
  6. fs = require('fs'),
  7. filesFor = require('./file-matcher').filesFor,
  8. libCoverage = require('istanbul-lib-coverage'),
  9. inputError = require('./input-error'),
  10. isAbsolute = path.isAbsolute || function (file) {
  11. return path.resolve(file) === path.normalize(file);
  12. };
  13. function removeFiles(origMap, root, files) {
  14. var filesObj = {},
  15. ret = libCoverage.createCoverageMap();
  16. // Create lookup table.
  17. files.forEach(function (file) {
  18. filesObj[file] = true;
  19. });
  20. origMap.files().forEach(function (key) {
  21. // Exclude keys will always be relative, but covObj keys can be absolute or relative
  22. var excludeKey = isAbsolute(key) ? path.relative(root, key) : key;
  23. // Also normalize for files that start with `./`, etc.
  24. excludeKey = path.normalize(excludeKey);
  25. if (filesObj[excludeKey] !== true) {
  26. ret.addFileCoverage(origMap.fileCoverageFor(key));
  27. }
  28. });
  29. return ret;
  30. }
  31. function run(config, opts, callback) {
  32. if (!callback && typeof(opts) === 'function') {
  33. callback = opts;
  34. opts = {};
  35. }
  36. opts = opts || {};
  37. var root = opts.root || config.instrumentation.root() || process.cwd(),
  38. includePattern = opts.include || '**/coverage*.json',
  39. errors = [],
  40. check,
  41. makeMap,
  42. processFiles;
  43. check = function (name, thresholds, actuals) {
  44. [
  45. "statements",
  46. "branches",
  47. "lines",
  48. "functions"
  49. ].forEach(function (key) {
  50. var actual = actuals[key].pct,
  51. actualUncovered = actuals[key].total - actuals[key].covered,
  52. threshold = thresholds[key];
  53. if (threshold < 0) {
  54. if (threshold * -1 < actualUncovered) {
  55. errors.push('ERROR: Uncovered count for ' + key + ' (' + actualUncovered +
  56. ') exceeds ' + name + ' threshold (' + -1 * threshold + ')');
  57. }
  58. } else {
  59. if (actual < threshold) {
  60. errors.push('ERROR: Coverage for ' + key + ' (' + actual +
  61. '%) does not meet ' + name + ' threshold (' + threshold + '%)');
  62. }
  63. }
  64. });
  65. };
  66. makeMap = function (files, callback) {
  67. var coverageMap = libCoverage.createCoverageMap();
  68. if (files.length === 0) {
  69. return callback(inputError.create('ERROR: No coverage files found.'));
  70. }
  71. files.forEach(function (file) {
  72. var coverageObject = JSON.parse(fs.readFileSync(file, 'utf8'));
  73. coverageMap.merge(coverageObject);
  74. });
  75. return callback(null, coverageMap);
  76. };
  77. processFiles = function (coverageMap, callback) {
  78. var thresholds = {
  79. global: {
  80. statements: config.check.global.statements || 0,
  81. branches: config.check.global.branches || 0,
  82. lines: config.check.global.lines || 0,
  83. functions: config.check.global.functions || 0,
  84. excludes: config.check.global.excludes || []
  85. },
  86. each: {
  87. statements: config.check.each.statements || 0,
  88. branches: config.check.each.branches || 0,
  89. lines: config.check.each.lines || 0,
  90. functions: config.check.each.functions || 0,
  91. excludes: config.check.each.excludes || []
  92. }
  93. },
  94. globalResults = removeFiles(coverageMap, root, thresholds.global.excludes),
  95. eachResults = removeFiles(coverageMap, root, thresholds.each.excludes),
  96. finalError;
  97. if (config.verbose) {
  98. console.error('Compare actuals against thresholds');
  99. console.error(JSON.stringify({
  100. global: globalResults,
  101. each: eachResults,
  102. thresholds: thresholds
  103. }, undefined, 4));
  104. }
  105. check("global", thresholds.global, globalResults.getCoverageSummary());
  106. eachResults.files().forEach(function (key) {
  107. var summary = eachResults.fileCoverageFor(key).toSummary();
  108. check("per-file" + " (" + key + ") ", thresholds.each, summary);
  109. });
  110. finalError = errors.length === 0 ? null : errors.join("\n");
  111. return callback(finalError);
  112. };
  113. filesFor({
  114. root: root,
  115. includes: [includePattern]
  116. }, function (err, files) {
  117. if (err) {
  118. return callback(err);
  119. }
  120. makeMap(files, function (err, map) {
  121. if (err) {
  122. return callback(err);
  123. }
  124. return processFiles(map, callback);
  125. });
  126. });
  127. }
  128. module.exports = {
  129. run: run
  130. };