/* Copyright 2012-2015, Yahoo Inc. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var path = require('path'), fs = require('fs'), filesFor = require('./file-matcher').filesFor, libCoverage = require('istanbul-lib-coverage'), inputError = require('./input-error'), isAbsolute = path.isAbsolute || function (file) { return path.resolve(file) === path.normalize(file); }; function removeFiles(origMap, root, files) { var filesObj = {}, ret = libCoverage.createCoverageMap(); // Create lookup table. files.forEach(function (file) { filesObj[file] = true; }); origMap.files().forEach(function (key) { // Exclude keys will always be relative, but covObj keys can be absolute or relative var excludeKey = isAbsolute(key) ? path.relative(root, key) : key; // Also normalize for files that start with `./`, etc. excludeKey = path.normalize(excludeKey); if (filesObj[excludeKey] !== true) { ret.addFileCoverage(origMap.fileCoverageFor(key)); } }); return ret; } function run(config, opts, callback) { if (!callback && typeof(opts) === 'function') { callback = opts; opts = {}; } opts = opts || {}; var root = opts.root || config.instrumentation.root() || process.cwd(), includePattern = opts.include || '**/coverage*.json', errors = [], check, makeMap, processFiles; check = function (name, thresholds, actuals) { [ "statements", "branches", "lines", "functions" ].forEach(function (key) { var actual = actuals[key].pct, actualUncovered = actuals[key].total - actuals[key].covered, threshold = thresholds[key]; if (threshold < 0) { if (threshold * -1 < actualUncovered) { errors.push('ERROR: Uncovered count for ' + key + ' (' + actualUncovered + ') exceeds ' + name + ' threshold (' + -1 * threshold + ')'); } } else { if (actual < threshold) { errors.push('ERROR: Coverage for ' + key + ' (' + actual + '%) does not meet ' + name + ' threshold (' + threshold + '%)'); } } }); }; makeMap = function (files, callback) { var coverageMap = libCoverage.createCoverageMap(); if (files.length === 0) { return callback(inputError.create('ERROR: No coverage files found.')); } files.forEach(function (file) { var coverageObject = JSON.parse(fs.readFileSync(file, 'utf8')); coverageMap.merge(coverageObject); }); return callback(null, coverageMap); }; processFiles = function (coverageMap, callback) { var thresholds = { global: { statements: config.check.global.statements || 0, branches: config.check.global.branches || 0, lines: config.check.global.lines || 0, functions: config.check.global.functions || 0, excludes: config.check.global.excludes || [] }, each: { statements: config.check.each.statements || 0, branches: config.check.each.branches || 0, lines: config.check.each.lines || 0, functions: config.check.each.functions || 0, excludes: config.check.each.excludes || [] } }, globalResults = removeFiles(coverageMap, root, thresholds.global.excludes), eachResults = removeFiles(coverageMap, root, thresholds.each.excludes), finalError; if (config.verbose) { console.error('Compare actuals against thresholds'); console.error(JSON.stringify({ global: globalResults, each: eachResults, thresholds: thresholds }, undefined, 4)); } check("global", thresholds.global, globalResults.getCoverageSummary()); eachResults.files().forEach(function (key) { var summary = eachResults.fileCoverageFor(key).toSummary(); check("per-file" + " (" + key + ") ", thresholds.each, summary); }); finalError = errors.length === 0 ? null : errors.join("\n"); return callback(finalError); }; filesFor({ root: root, includes: [includePattern] }, function (err, files) { if (err) { return callback(err); } makeMap(files, function (err, map) { if (err) { return callback(err); } return processFiles(map, callback); }); }); } module.exports = { run: run };