/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */

const ROOT_URL = "http://example.com/browser/dom/tests/browser/perfmetrics";
const DUMMY_URL = ROOT_URL + "/dummy.html";
const WORKER_URL = ROOT_URL + "/ping_worker.html";
const WORKER_URL2 = ROOT_URL + "/ping_worker2.html";
const INTERVAL_URL = ROOT_URL + "/setinterval.html";
const TIMEOUT_URL = ROOT_URL + "/settimeout.html";
const SOUND_URL = ROOT_URL + "/sound.html";
const CATEGORY_TIMER = 2;

add_task(async function test() {
  waitForExplicitFinish();

  // Load 3 pages and wait. The 3rd one has a worker
  let page1 = await BrowserTestUtils.openNewForegroundTab({
    gBrowser,
    opening: "about:about",
    forceNewProcess: false,
  });

  let page2 = await BrowserTestUtils.openNewForegroundTab({
    gBrowser,
    opening: "about:memory",
    forceNewProcess: false,
  });

  let page3 = await BrowserTestUtils.openNewForegroundTab({
    gBrowser,
    opening: WORKER_URL,
  });
  // load a 4th tab with a worker
  await BrowserTestUtils.withNewTab(
    { gBrowser, url: WORKER_URL2 },
    async function(browser) {
      // grab events..
      let workerDuration = 0;
      let workerTotal = 0;
      let duration = 0;
      let total = 0;
      let isTopLevel = false;
      let aboutMemoryFound = false;
      let parentProcessEvent = false;
      let subFrameIds = [];
      let topLevelIds = [];
      let sharedWorker = false;
      let counterIds = [];
      let timerCalls = 0;
      let heapUsage = 0;
      let mediaMemory = 0;

      function exploreResults(data, filterByWindowId) {
        for (let entry of data) {
          if (filterByWindowId && entry.windowId != filterByWindowId) {
            continue;
          }
          if (!counterIds.includes(entry.pid + ":" + entry.counterId)) {
            counterIds.push(entry.pid + ":" + entry.counterId);
          }
          sharedWorker =
            entry.host.endsWith("shared_worker.js") || sharedWorker;
          heapUsage += entry.memoryInfo.GCHeapUsage;
          mediaMemory +=
            entry.memoryInfo.media.audioSize +
            entry.memoryInfo.media.resourcesSize;
          Assert.ok(
            entry.host != "" || entry.windowId != 0,
            "An entry should have a host or a windowId"
          );
          if (
            entry.windowId != 0 &&
            !entry.isToplevel &&
            !entry.isWorker &&
            !subFrameIds.includes(entry.windowId)
          ) {
            subFrameIds.push(entry.windowId);
          }
          if (entry.isTopLevel && !topLevelIds.includes(entry.windowId)) {
            topLevelIds.push(entry.windowId);
          }
          if (entry.host == "example.com" && entry.isTopLevel) {
            isTopLevel = true;
          }
          if (entry.host == "about:memory") {
            aboutMemoryFound = true;
          }
          if (entry.pid == Services.appinfo.processID) {
            parentProcessEvent = true;
          }
          if (entry.isWorker) {
            workerDuration += entry.duration;
          } else {
            duration += entry.duration;
          }
          // let's look at the data we got back
          for (let item of entry.items) {
            Assert.ok(
              item.count > 0,
              "Categories with an empty count are dropped"
            );
            if (entry.isWorker) {
              workerTotal += item.count;
            } else {
              total += item.count;
            }
            if (item.category == CATEGORY_TIMER) {
              timerCalls += item.count;
            }
          }
        }
      }

      // get all metrics via the promise
      let results = await ChromeUtils.requestPerformanceMetrics();
      exploreResults(results);

      Assert.greater(workerDuration, 0, "Worker duration should be positive");
      Assert.greater(workerTotal, 0, "Worker count should be positive");
      Assert.greater(duration, 0, "Duration should be positive");
      Assert.greater(total, 0, "Should get a positive count");
      Assert.ok(parentProcessEvent, "parent process sent back some events");
      Assert.ok(isTopLevel, "example.com as a top level window");
      Assert.ok(aboutMemoryFound, "about:memory");
      Assert.greater(heapUsage, 0, "got some memory value reported");
      Assert.ok(sharedWorker, "We got some info from a shared worker");
      let numCounters = counterIds.length;
      Assert.ok(
        numCounters > 5,
        "This test generated at least " + numCounters + " unique counters"
      );

      // checking that subframes are not orphans
      for (let frameId of subFrameIds) {
        Assert.ok(topLevelIds.includes(frameId), "subframe is not orphan ");
      }

      // Doing a second call, we shoud get bigger values
      let previousWorkerDuration = workerDuration;
      let previousWorkerTotal = workerTotal;
      let previousDuration = duration;
      let previousTotal = total;

      results = await ChromeUtils.requestPerformanceMetrics();
      exploreResults(results);

      Assert.ok(
        workerDuration > previousWorkerDuration,
        "Worker duration should be positive"
      );
      Assert.ok(
        workerTotal > previousWorkerTotal,
        "Worker count should be positive"
      );
      Assert.greater(duration, previousDuration, "Duration should be positive");
      Assert.greater(total, previousTotal, "Should get a positive count");

      // load a tab with a setInterval, we should get counters on TaskCategory::Timer
      await BrowserTestUtils.withNewTab(
        { gBrowser, url: INTERVAL_URL },
        async function(browser) {
          let tabId = gBrowser.selectedBrowser.outerWindowID;
          let previousTimerCalls = timerCalls;
          results = await ChromeUtils.requestPerformanceMetrics();
          exploreResults(results, tabId);
          Assert.greater(timerCalls, previousTimerCalls, "Got timer calls");
        }
      );

      // load a tab with a setTimeout, we should get counters on TaskCategory::Timer
      await BrowserTestUtils.withNewTab(
        { gBrowser, url: TIMEOUT_URL },
        async function(browser) {
          let tabId = gBrowser.selectedBrowser.outerWindowID;
          let previousTimerCalls = timerCalls;
          results = await ChromeUtils.requestPerformanceMetrics();
          exploreResults(results, tabId);
          Assert.greater(timerCalls, previousTimerCalls, "Got timer calls");
        }
      );

      // load a tab with a sound
      await BrowserTestUtils.withNewTab(
        { gBrowser, url: SOUND_URL },
        async function(browser) {
          let tabId = gBrowser.selectedBrowser.outerWindowID;
          results = await ChromeUtils.requestPerformanceMetrics();
          exploreResults(results, tabId);
          Assert.greater(mediaMemory, 0, "Got some memory used for media");
        }
      );
    }
  );

  BrowserTestUtils.removeTab(page1);
  BrowserTestUtils.removeTab(page2);
  BrowserTestUtils.removeTab(page3);
});
