summaryrefslogtreecommitdiffstats
path: root/toolkit/components/perfmonitoring/tests/browser/browser_AddonWatcher.js
blob: b4e80faa7af46489815e99cf8468753066cddc8c (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
/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

// Tests for AddonWatcher.jsm

"use strict";

requestLongerTimeout(2);

Cu.import("resource://gre/modules/Promise.jsm", this);
Cu.import("resource://gre/modules/AddonManager.jsm", this);
Cu.import("resource://gre/modules/Services.jsm", this);

const ADDON_URL = "http://example.com/browser/toolkit/components/perfmonitoring/tests/browser/browser_Addons_sample.xpi";
const ADDON_ID = "addonwatcher-test@mozilla.com";

add_task(function* init() {
  info("Installing test add-on");
  let installer = yield new Promise(resolve => AddonManager.getInstallForURL(ADDON_URL, resolve, "application/x-xpinstall"));
  if (installer.error) {
    throw installer.error;
  }
  let installed = new Promise((resolve, reject) => installer.addListener({
    onInstallEnded: (_, addon) => resolve(addon),
    onInstallFailed: reject,
    onDownloadFailed: reject
  }));

  // We also need to wait for the add-on to report that it's ready
  // to be used in the test.
  let ready = TestUtils.topicObserved("test-addonwatcher-ready");
  installer.install();

  info("Waiting for installation to terminate");
  let addon = yield installed;

  yield ready;

  registerCleanupFunction(() => {
    info("Uninstalling test add-on");
    addon.uninstall()
  });

  Preferences.set("browser.addon-watch.warmup-ms", 0);
  Preferences.set("browser.addon-watch.freeze-threshold-micros", 0);
  Preferences.set("browser.addon-watch.jank-threshold-micros", 0);
  Preferences.set("browser.addon-watch.occurrences-between-alerts", 0);
  Preferences.set("browser.addon-watch.delay-between-alerts-ms", 0);
  Preferences.set("browser.addon-watch.delay-between-freeze-alerts-ms", 0);
  Preferences.set("browser.addon-watch.max-simultaneous-reports", 10000);
  Preferences.set("browser.addon-watch.deactivate-after-idle-ms", 100000000);
  registerCleanupFunction(() => {
    for (let k of [
      "browser.addon-watch.warmup-ms",
      "browser.addon-watch.freeze-threshold-micros",
      "browser.addon-watch.jank-threshold-micros",
      "browser.addon-watch.occurrences-between-alerts",
      "browser.addon-watch.delay-between-alerts-ms",
      "browser.addon-watch.delay-between-freeze-alerts-ms",
      "browser.addon-watch.max-simultaneous-reports",
      "browser.addon-watch.deactivate-after-idle-ms"
    ]) {
      Preferences.reset(k);
    }
  });

  let oldCanRecord = Services.telemetry.canRecordExtended;
  Services.telemetry.canRecordExtended = true;
  AddonWatcher.init();

  registerCleanupFunction(function () {
    AddonWatcher.paused = true;
    Services.telemetry.canRecordExtended = oldCanRecord;
  });
});

// Utility function to burn some resource, trigger a reaction of the add-on watcher
// and check both its notification and telemetry.
let burn_rubber = Task.async(function*({histogramName, topic, expectedMinSum}) {
  let detected = false;
  let observer = (_, topic, id) => {
    Assert.equal(id, ADDON_ID, "The add-on watcher has detected the misbehaving addon");
    detected = true;
  };

  try {
    info("Preparing add-on watcher");

    Services.obs.addObserver(observer, AddonWatcher.TOPIC_SLOW_ADDON_DETECTED, false);

    let histogram = Services.telemetry.getKeyedHistogramById(histogramName);
    histogram.clear();
    let snap1 = histogram.snapshot(ADDON_ID);
    Assert.equal(snap1.sum, 0, `Histogram ${histogramName} is initially empty for the add-on`);

    let histogramUpdated = false;
    do {
      info(`Burning some CPU with ${topic}. This should cause an add-on watcher notification`);
      yield new Promise(resolve => setTimeout(resolve, 100));
      Services.obs.notifyObservers(null, topic, "");
      yield new Promise(resolve => setTimeout(resolve, 100));

      let snap2 = histogram.snapshot(ADDON_ID);
      histogramUpdated = snap2.sum > 0;
      info(`For the moment, histogram ${histogramName} shows ${snap2.sum} => ${histogramUpdated}`);
      info(`For the moment, we have ${detected?"":"NOT "}detected the slow add-on`);
    } while (!histogramUpdated || !detected);

    let snap3 = histogram.snapshot(ADDON_ID);
    Assert.ok(snap3.sum >= expectedMinSum, `Histogram ${histogramName} recorded a gravity of ${snap3.sum}, expecting at least ${expectedMinSum}.`);
  } finally {
    Services.obs.removeObserver(observer, AddonWatcher.TOPIC_SLOW_ADDON_DETECTED);
  }
});

// Test that burning CPU will cause the add-on watcher to notice that
// the add-on is misbehaving.
add_task(function* test_burn_CPU() {
  yield burn_rubber({
    histogramName: "PERF_MONITORING_SLOW_ADDON_JANK_US",
    topic: "test-addonwatcher-burn-some-cpu",
    expectedMinSum: 7,
  });
});

// Test that burning content CPU will cause the add-on watcher to notice that
// the add-on is misbehaving.
/*
Blocked by bug 1227283.
add_task(function* test_burn_content_CPU() {
  yield burn_rubber({
    histogramName: "PERF_MONITORING_SLOW_ADDON_JANK_US",
    topic: "test-addonwatcher-burn-some-content-cpu",
    expectedMinSum: 7,
  });
});
*/

// Test that burning CPOW will cause the add-on watcher to notice that
// the add-on is misbehaving.
add_task(function* test_burn_CPOW() {
  if (!gMultiProcessBrowser) {
    info("This is a single-process Firefox, we can't test for CPOW");
    return;
  }
  yield burn_rubber({
    histogramName: "PERF_MONITORING_SLOW_ADDON_CPOW_US",
    topic: "test-addonwatcher-burn-some-cpow",
    expectedMinSum: 400,
  });
});