summaryrefslogtreecommitdiffstats
path: root/devtools/client/framework/test/metrics/head.js
blob: 0246190b31abadd72f7936e8c9d097feec2a8a13 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

// shared-head.js handles imports, constants, and utility functions
Services.scriptloader.loadSubScript(
  "chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js",
  this
);

// So that PERFHERDER data can be extracted from the logs.
SimpleTest.requestCompleteLog();

function getFilteredModules(filters, loaders) {
  let modules = [];
  for (const l of loaders) {
    const loaderModulesMap = l.modules;
    const loaderModulesPaths = Object.keys(loaderModulesMap);
    modules = modules.concat(loaderModulesPaths);
  }
  return modules.filter(url => filters.some(filter => url.includes(filter)));
}

function countCharsInModules(modules) {
  return modules.reduce((sum, uri) => {
    try {
      return sum + require("raw!" + uri).length;
    } catch (e) {
      // Ignore failures
      return sum;
    }
  }, 0);
}

/**
 * Record module loading data.
 *
 * @param {Object}
 * - filterString {String} path to use to filter modules specific to the current panel
 * - loaders {Array} Array of Loaders to check for modules
 * - panelName {String} reused in identifiers for perfherder data
 */
function runMetricsTest({ filterString, loaders, panelName }) {
  const allModules = getFilteredModules([""], loaders);
  const panelModules = getFilteredModules([filterString], loaders);
  const vendoredModules = getFilteredModules(
    ["devtools/client/debugger/dist/vendors", "devtools/client/shared/vendor/"],
    loaders
  );

  const allModulesCount = allModules.length;
  const panelModulesCount = panelModules.length;
  const vendoredModulesCount = vendoredModules.length;

  const allModulesChars = countCharsInModules(allModules);
  const panelModulesChars = countCharsInModules(panelModules);
  const vendoredModulesChars = countCharsInModules(vendoredModules);

  const PERFHERDER_DATA = {
    framework: {
      name: "devtools",
    },
    suites: [
      {
        name: panelName + "-metrics",
        value: allModulesChars,
        subtests: [
          {
            name: panelName + "-modules",
            value: panelModulesCount,
          },
          {
            name: panelName + "-chars",
            value: panelModulesChars,
          },
          {
            name: "all-modules",
            value: allModulesCount,
          },
          {
            name: "all-chars",
            value: allModulesChars,
          },
          {
            name: "vendored-modules",
            value: vendoredModulesCount,
          },
          {
            name: "vendored-chars",
            value: vendoredModulesChars,
          },
        ],
      },
    ],
  };
  info("PERFHERDER_DATA: " + JSON.stringify(PERFHERDER_DATA));

  // Simply check that we found valid values.
  ok(
    allModulesCount > panelModulesCount && panelModulesCount > 0,
    "Successfully recorded module count for " + panelName
  );
  ok(
    allModulesChars > panelModulesChars && panelModulesChars > 0,
    "Successfully recorded char count for " + panelName
  );

  // Easy way to check how many vendored chars we have for a given panel.
  const percentage = ((100 * vendoredModulesChars) / allModulesChars).toFixed(
    1
  );
  info(`Percentage of vendored chars for ${panelName}: ${percentage}%`);
}

function getDuplicatedModules(loaders) {
  const allModules = getFilteredModules([""], loaders);

  const uniqueModules = new Set();
  const duplicatedModules = new Set();
  for (const mod of allModules) {
    if (uniqueModules.has(mod)) {
      duplicatedModules.add(mod);
    }

    uniqueModules.add(mod);
  }

  return duplicatedModules;
}

/**
 * Check that modules are only loaded once in a given set of loaders.
 * Panels might load the same module twice by mistake if they are both using
 * a BrowserLoader and the regular DevTools Loader.
 *
 * @param {Array} loaders
 *        Array of Loader instances.
 * @param {Array} allowedDupes
 *        Array of Strings which are paths to known duplicated modules.
 *        The test will also fail if a allowedDupesed module is not found in the
 *        duplicated modules.
 */
function runDuplicatedModulesTest(loaders, allowedDupes) {
  const duplicatedModules = getDuplicatedModules(loaders);

  // Remove allowedDupes entries, and fail if an allowed entry is not found.
  for (const mod of allowedDupes) {
    const deleted = duplicatedModules.delete(mod);
    if (!deleted) {
      ok(
        false,
        "module not found in the duplicated modules: [" +
          mod +
          "]. The allowedDupes array should be updated to remove it."
      );
    }
  }

  // Prepare a log string with the paths of all duplicated modules.
  let duplicatedModulesLog = "";
  for (const mod of duplicatedModules) {
    duplicatedModulesLog += `  [duplicated module] ${mod}\n`;
  }

  // Check that duplicatedModules Set is empty.
  is(
    duplicatedModules.size,
    0,
    "Duplicated module load detected. List of duplicated modules:\n" +
      duplicatedModulesLog
  );
}