1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5/* exported PerTestCoverageUtils */
6
7"use strict";
8
9var EXPORTED_SYMBOLS = ["PerTestCoverageUtils"];
10
11const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
12
13const env = Cc["@mozilla.org/process/environment;1"].getService(
14  Ci.nsIEnvironment
15);
16// This is the directory where gcov is emitting the gcda files.
17const gcovPrefixPath = env.get("GCOV_PREFIX");
18// This is the directory where codecoverage.py is expecting to see the gcda files.
19const gcovResultsPath = env.get("GCOV_RESULTS_DIR");
20// This is the directory where the JS engine is emitting the lcov files.
21const jsvmPrefixPath = env.get("JS_CODE_COVERAGE_OUTPUT_DIR");
22// This is the directory where codecoverage.py is expecting to see the lcov files.
23const jsvmResultsPath = env.get("JSVM_RESULTS_DIR");
24
25const gcovPrefixDir = Cc["@mozilla.org/file/local;1"].createInstance(
26  Ci.nsIFile
27);
28if (gcovPrefixPath) {
29  gcovPrefixDir.initWithPath(gcovPrefixPath);
30}
31
32let gcovResultsDir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
33if (gcovResultsPath) {
34  gcovResultsDir.initWithPath(gcovResultsPath);
35}
36
37const jsvmPrefixDir = Cc["@mozilla.org/file/local;1"].createInstance(
38  Ci.nsIFile
39);
40if (jsvmPrefixPath) {
41  jsvmPrefixDir.initWithPath(jsvmPrefixPath);
42}
43
44let jsvmResultsDir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
45if (jsvmResultsPath) {
46  jsvmResultsDir.initWithPath(jsvmResultsPath);
47}
48
49function awaitPromise(promise) {
50  let ret;
51  let complete = false;
52  let error = null;
53  promise
54    .catch(e => (error = e))
55    .then(v => {
56      ret = v;
57      complete = true;
58    });
59  Services.tm.spinEventLoopUntil(
60    "PerTestCoverageUtils.jsm:awaitPromise",
61    () => complete
62  );
63  if (error) {
64    throw new Error(error);
65  }
66  return ret;
67}
68
69function removeDirectoryContents(dir) {
70  let entries = dir.directoryEntries;
71  while (entries.hasMoreElements()) {
72    entries.nextFile.remove(true);
73  }
74}
75
76function moveDirectoryContents(src, dst) {
77  let entries = src.directoryEntries;
78  while (entries.hasMoreElements()) {
79    entries.nextFile.moveTo(dst, null);
80  }
81}
82
83var PerTestCoverageUtils = class PerTestCoverageUtilsClass {
84  // Resets the counters to 0.
85  static async beforeTest() {
86    if (!PerTestCoverageUtils.enabled) {
87      return;
88    }
89
90    // Flush the counters.
91    let codeCoverageService = Cc[
92      "@mozilla.org/tools/code-coverage;1"
93    ].getService(Ci.nsICodeCoverage);
94    await codeCoverageService.flushCounters();
95
96    // Remove coverage files created by the flush, and those that might have been created between the end of a previous test and the beginning of the next one (e.g. some tests can create a new content process for every sub-test).
97    removeDirectoryContents(gcovPrefixDir);
98    removeDirectoryContents(jsvmPrefixDir);
99
100    // Move coverage files from the GCOV_RESULTS_DIR and JSVM_RESULTS_DIR directories, so we can accumulate the counters.
101    moveDirectoryContents(gcovResultsDir, gcovPrefixDir);
102    moveDirectoryContents(jsvmResultsDir, jsvmPrefixDir);
103  }
104
105  static beforeTestSync() {
106    awaitPromise(this.beforeTest());
107  }
108
109  // Dumps counters and moves the gcda files in the directory expected by codecoverage.py.
110  static async afterTest() {
111    if (!PerTestCoverageUtils.enabled) {
112      return;
113    }
114
115    // Flush the counters.
116    let codeCoverageService = Cc[
117      "@mozilla.org/tools/code-coverage;1"
118    ].getService(Ci.nsICodeCoverage);
119    await codeCoverageService.flushCounters();
120
121    // Move the coverage files in GCOV_RESULTS_DIR and JSVM_RESULTS_DIR, so that the execution from now to shutdown (or next test) is not counted.
122    moveDirectoryContents(gcovPrefixDir, gcovResultsDir);
123    moveDirectoryContents(jsvmPrefixDir, jsvmResultsDir);
124  }
125
126  static afterTestSync() {
127    awaitPromise(this.afterTest());
128  }
129};
130
131PerTestCoverageUtils.enabled = !!gcovResultsPath;
132