mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-11-11 05:39:41 +02:00
This patch first adds an argument to the 'do_get_file(...)' function call in 'test_chrome_bookmarks.js' that simply allows the 'chromefiles' folder to be non-existent if it does not exist. The 'CoverageUtils.jsm' file is then modified so that the import of 'osfile.jsm' does not interfere with any tests. So, it is now imported into the script after the test has completed. Two other tests have unwanted behaviour that cause code coverage collection to fail so they are also skipped through this patch. MozReview-Commit-ID: H42HN1solkh --HG-- extra : rebase_source : 82706778961cd5d7dee4f66eb691d8ec62bde365
226 lines
6.2 KiB
JavaScript
226 lines
6.2 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
"use strict";
|
|
|
|
this.EXPORTED_SYMBOLS = [
|
|
"CoverageCollector",
|
|
]
|
|
|
|
const Cc = Components.classes;
|
|
const Ci = Components.interfaces;
|
|
const Cu = Components.utils;
|
|
|
|
const {addDebuggerToGlobal} = Cu.import("resource://gre/modules/jsdebugger.jsm",
|
|
{});
|
|
addDebuggerToGlobal(this);
|
|
|
|
/**
|
|
* Records coverage for each test by way of the js debugger.
|
|
*/
|
|
this.CoverageCollector = function (prefix) {
|
|
this._prefix = prefix;
|
|
this._dbg = new Debugger();
|
|
this._dbg.collectCoverageInfo = true;
|
|
this._dbg.addAllGlobalsAsDebuggees();
|
|
this._scripts = this._dbg.findScripts();
|
|
|
|
this._dbg.onNewScript = (script) => {
|
|
this._scripts.push(script);
|
|
};
|
|
|
|
// Source -> coverage data;
|
|
this._allCoverage = {};
|
|
this._encoder = null;
|
|
this._testIndex = 0;
|
|
}
|
|
|
|
CoverageCollector.prototype._getLinesCovered = function () {
|
|
let coveredLines = {};
|
|
let currentCoverage = {};
|
|
this._scripts.forEach(s => {
|
|
let scriptName = s.url;
|
|
let cov = s.getOffsetsCoverage();
|
|
if (!cov) {
|
|
return;
|
|
}
|
|
|
|
cov.forEach(covered => {
|
|
let {lineNumber, columnNumber, offset, count} = covered;
|
|
if (!count) {
|
|
return;
|
|
}
|
|
|
|
if (!currentCoverage[scriptName]) {
|
|
currentCoverage[scriptName] = {};
|
|
}
|
|
if (!this._allCoverage[scriptName]) {
|
|
this._allCoverage[scriptName] = {};
|
|
}
|
|
|
|
let key = [lineNumber, columnNumber, offset].join('#');
|
|
if (!currentCoverage[scriptName][key]) {
|
|
currentCoverage[scriptName][key] = count;
|
|
} else {
|
|
currentCoverage[scriptName][key] += count;
|
|
}
|
|
});
|
|
|
|
});
|
|
|
|
// Covered lines are determined by comparing every offset mentioned as of the
|
|
// the completion of a test to the last time we measured coverage. If an
|
|
// offset in a line is novel as of this test, or a count has increased for
|
|
// any offset on a particular line, that line must have been covered.
|
|
for (let scriptName in currentCoverage) {
|
|
for (let key in currentCoverage[scriptName]) {
|
|
if (!this._allCoverage[scriptName] ||
|
|
!this._allCoverage[scriptName][key] ||
|
|
(this._allCoverage[scriptName][key] <
|
|
currentCoverage[scriptName][key])) {
|
|
let [lineNumber, colNumber, offset] = key.split('#');
|
|
if (!coveredLines[scriptName]) {
|
|
coveredLines[scriptName] = new Set();
|
|
}
|
|
coveredLines[scriptName].add(parseInt(lineNumber, 10));
|
|
this._allCoverage[scriptName][key] = currentCoverage[scriptName][key];
|
|
}
|
|
}
|
|
}
|
|
|
|
return coveredLines;
|
|
}
|
|
|
|
CoverageCollector.prototype._getUncoveredLines = function() {
|
|
let uncoveredLines = {};
|
|
this._scripts.forEach(s => {
|
|
let scriptName = s.url;
|
|
let scriptOffsets = s.getAllOffsets();
|
|
|
|
if (!uncoveredLines[scriptName]) {
|
|
uncoveredLines[scriptName] = new Set();
|
|
}
|
|
|
|
// Get all lines in the script
|
|
scriptOffsets.forEach( function(element, index) {
|
|
if (!element) {
|
|
return;
|
|
}
|
|
uncoveredLines[scriptName].add(index);
|
|
});
|
|
});
|
|
|
|
// For all covered lines, delete their entry
|
|
for (let scriptName in this._allCoverage) {
|
|
for (let key in this._allCoverage[scriptName]) {
|
|
let [lineNumber, columnNumber, offset] = key.split('#');
|
|
uncoveredLines[scriptName].delete(parseInt(lineNumber, 10));
|
|
}
|
|
}
|
|
|
|
return uncoveredLines;
|
|
}
|
|
|
|
CoverageCollector.prototype._getMethodNames = function() {
|
|
let methodNames = {};
|
|
this._scripts.forEach(s => {
|
|
let method = s.displayName;
|
|
// If the method name is undefined, we return early
|
|
if (!method) {
|
|
return;
|
|
}
|
|
|
|
let scriptName = s.url;
|
|
let tempMethodCov = [];
|
|
let scriptOffsets = s.getAllOffsets();
|
|
|
|
if (!methodNames[scriptName]) {
|
|
methodNames[scriptName] = {};
|
|
}
|
|
|
|
/**
|
|
* Get all lines contained within the method and
|
|
* push a record of the form:
|
|
* <method name> : <lines covered>
|
|
*/
|
|
scriptOffsets.forEach( function (element, index) {
|
|
if (!element) {
|
|
return;
|
|
}
|
|
tempMethodCov.push(index);
|
|
});
|
|
methodNames[scriptName][method] = tempMethodCov;
|
|
});
|
|
|
|
return methodNames;
|
|
}
|
|
|
|
/**
|
|
* Implements an iterator for objects. It is
|
|
* used to iterate over the elements of the object obtained
|
|
* from the function _getMethodNames.
|
|
*/
|
|
Object.prototype[Symbol.iterator] = function * () {
|
|
for (var [key, value] of Object.entries(this)) {
|
|
yield [key, value];
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Records lines covered since the last time coverage was recorded,
|
|
* associating them with the given test name. The result is written
|
|
* to a json file in a specified directory.
|
|
*/
|
|
CoverageCollector.prototype.recordTestCoverage = function (testName) {
|
|
let ccov_scope = {};
|
|
const {TextEncoder, OS} = Cu.import("resource://gre/modules/osfile.jsm", ccov_scope);
|
|
this._encoder = new TextEncoder();
|
|
|
|
dump("Collecting coverage for: " + testName + "\n");
|
|
let rawLines = this._getLinesCovered(testName);
|
|
let methods = this._getMethodNames();
|
|
let uncoveredLines = this._getUncoveredLines();
|
|
let result = [];
|
|
let versionControlBlock = {version: 1.0};
|
|
result.push(versionControlBlock);
|
|
|
|
for (let scriptName in rawLines) {
|
|
let rec = {
|
|
testUrl: testName,
|
|
sourceFile: scriptName,
|
|
methods: {},
|
|
covered: [],
|
|
uncovered: []
|
|
};
|
|
|
|
if (typeof(methods[scriptName]) != 'undefined' && methods[scriptName] != null) {
|
|
for (let [methodName, methodLines] of methods[scriptName]) {
|
|
rec.methods[methodName] = methodLines;
|
|
}
|
|
}
|
|
|
|
for (let line of rawLines[scriptName]) {
|
|
rec.covered.push(line);
|
|
}
|
|
|
|
for (let line of uncoveredLines[scriptName]) {
|
|
rec.uncovered.push(line);
|
|
}
|
|
|
|
result.push(rec);
|
|
}
|
|
let arr = this._encoder.encode(JSON.stringify(result, null, 2));
|
|
let path = this._prefix + '/' + 'jscov_' + Date.now() + '.json';
|
|
dump("Writing coverage to: " + path + "\n");
|
|
return OS.File.writeAtomic(path, arr, {tmpPath: path + '.tmp'});
|
|
}
|
|
|
|
/**
|
|
* Tear down the debugger after all tests are complete.
|
|
*/
|
|
CoverageCollector.prototype.finalize = function () {
|
|
this._dbg.removeAllDebuggees();
|
|
this._dbg.enabled = false;
|
|
}
|