"use strict";

/**
 * Tests for PerformanceWatcher watching slow web pages.
 */

 /**
  * Simulate a slow webpage.
  */
function WebpageBurner() {
  CPUBurner.call(this, "http://example.com/browser/toolkit/components/perfmonitoring/tests/browser/browser_compartments.html?test=" + Math.random(), 300000);
}
WebpageBurner.prototype = Object.create(CPUBurner.prototype);
WebpageBurner.prototype.promiseBurnContentCPU = function() {
  return promiseContentResponse(this._browser, "test-performance-watcher:burn-content-cpu", {});
};

function WebpageListener(windowId, accept) {
  info(`Creating WebpageListener for ${windowId}`);
  AlertListener.call(this, accept, {
    register: () => PerformanceWatcher.addPerformanceListener({windowId}, this.listener),
    unregister: () => PerformanceWatcher.removePerformanceListener({windowId}, this.listener)
  });
}
WebpageListener.prototype = Object.create(AlertListener.prototype);

add_task(async function init() {
  // Get rid of buffering.
  let service = Cc["@mozilla.org/toolkit/performance-stats-service;1"].getService(
    Ci.nsIPerformanceStatsService);
  let oldDelay = service.jankAlertBufferingDelay;

  service.jankAlertBufferingDelay = 0 /* ms */;
  registerCleanupFunction(() => {
    info("Cleanup");
    service.jankAlertBufferingDelay = oldDelay;
  });
});

add_task(async function test_open_window_then_watch_it() {
  let burner = new WebpageBurner();
  await burner.promiseInitialized;
  await burner.promiseBurnContentCPU();

  info(`Check that burning CPU triggers the real listener, but not the fake listener`);
  let realListener = new WebpageListener(burner.windowId, (group, details) => {
    info(`test: realListener for ${burner.tab.linkedBrowser.outerWindowID}: ${group}, ${details}\n`);
    Assert.equal(group.windowId, burner.windowId, "We should not receive data meant for another group");
    return details;
  }); // This listener should be triggered.

  info(`Creating fake burner`);
  let otherTab = BrowserTestUtils.addTab(gBrowser);
  await BrowserTestUtils.browserLoaded(otherTab.linkedBrowser);
  info(`Check that burning CPU triggers the real listener, but not the fake listener`);
  let fakeListener = new WebpageListener(otherTab.linkedBrowser.outerWindowID, group => group.windowId == burner.windowId); // This listener should never be triggered.
  let universalListener = new WebpageListener(0, alerts =>
    alerts.find(alert => alert.source.windowId == burner.windowId)
  );

  // Waiting a little – listeners are buffered.
  await new Promise(resolve => setTimeout(resolve, 100));

  await burner.run("promiseBurnContentCPU", 20, realListener);
  Assert.ok(realListener.triggered, `1. The real listener was triggered`);
  Assert.ok(universalListener.triggered, `1. The universal listener was triggered`);
  Assert.ok(!fakeListener.triggered, `1. The fake listener was not triggered`);

  if (realListener.result) {
    Assert.ok(realListener.result.highestJank >= 300, `1. jank is at least 300ms (${realListener.result.highestJank}ms)`);
  }

  info(`Attempting to remove a performance listener incorrectly, check that this does not hurt our real listener`);
  Assert.throws(() => PerformanceWatcher.removePerformanceListener({windowId: burner.windowId}, () => {}),
                /No listener for target/, "should throw an error for a different listener");
  Assert.throws(() => PerformanceWatcher.removePerformanceListener({windowId: burner.windowId + "-unbound-id-" + Math.random()}, realListener.listener),
                /No listener for target/, "should throw an error for a different window id");

  // Waiting a little – listeners are buffered.
  await new Promise(resolve => setTimeout(resolve, 100));
  await burner.run("promiseBurnContentCPU", 20, realListener);
  // Waiting a little – listeners are buffered.
  await new Promise(resolve => setTimeout(resolve, 100));

  Assert.ok(realListener.triggered, `2. The real listener was triggered`);
  Assert.ok(universalListener.triggered, `2. The universal listener was triggered`);
  Assert.ok(!fakeListener.triggered, `2. The fake listener was not triggered`);
  if (realListener.result) {
    Assert.ok(realListener.result.highestJank >= 300, `2. jank is at least 300ms (${realListener.jank}ms)`);
  }

  info(`Attempting to remove correctly, check if the listener is still triggered`);
  // Waiting a little – listeners are buffered.
  await new Promise(resolve => setTimeout(resolve, 100));
  realListener.unregister();

  // Waiting a little – listeners are buffered.
  await new Promise(resolve => setTimeout(resolve, 100));
  await burner.run("promiseBurnContentCPU", 3, realListener);
  Assert.ok(!realListener.triggered, `3. After being unregistered, the real listener was not triggered`);
  Assert.ok(universalListener.triggered, `3. The universal listener is still triggered`);

  universalListener.unregister();

  // Waiting a little – listeners are buffered.
  await new Promise(resolve => setTimeout(resolve, 100));
  await burner.run("promiseBurnContentCPU", 3, realListener);
  Assert.ok(!universalListener.triggered, `4. After being unregistered, the universal listener is not triggered`);

  fakeListener.unregister();
  burner.dispose();
  gBrowser.removeTab(otherTab);
});
