summaryrefslogtreecommitdiffstats
path: root/devtools/client/performance/test/browser_perf-marker-details.js
blob: 8607f269d0f2f47f03720fdfc35eaa5a791f2491 (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
/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/* eslint-disable */
/**
 * Tests if the Marker Details view renders all properties expected
 * for each marker.
 */

function* spawnTest() {
  let { target, panel } = yield initPerformance(MARKERS_URL);
  let { $, $$, EVENTS, PerformanceController, OverviewView, WaterfallView } = panel.panelWin;

  // Hijack the markers massaging part of creating the waterfall view,
  // to prevent collapsing markers and allowing this test to verify
  // everything individually. A better solution would be to just expand
  // all markers first and then skip the meta nodes, but I'm lazy.
  WaterfallView._prepareWaterfallTree = markers => {
    return { submarkers: markers };
  };

  const MARKER_TYPES = [
    "Styles", "Reflow", "ConsoleTime", "TimeStamp"
  ];

  yield startRecording(panel);
  ok(true, "Recording has started.");

  yield waitUntil(() => {
    // Wait until we get all the different markers.
    let markers = PerformanceController.getCurrentRecording().getMarkers();
    return MARKER_TYPES.every(type => markers.some(m => m.name === type));
  });

  yield stopRecording(panel);
  ok(true, "Recording has ended.");

  info("No need to select everything in the timeline.");
  info("All the markers should be displayed by default.");

  let bars = Array.prototype.filter.call($$(".waterfall-marker-bar"),
             (bar) => MARKER_TYPES.indexOf(bar.getAttribute("type")) !== -1);
  let markers = PerformanceController.getCurrentRecording().getMarkers()
                .filter(m => MARKER_TYPES.indexOf(m.name) !== -1);

  info(`Got ${bars.length} bars and ${markers.length} markers.`);
  info("Markers types from datasrc: " + Array.map(markers, e => e.name));
  info("Markers names from sidebar: " + Array.map(bars, e => e.parentNode.parentNode.querySelector(".waterfall-marker-name").getAttribute("value")));

  ok(bars.length >= MARKER_TYPES.length, `Got at least ${MARKER_TYPES.length} markers (1)`);
  ok(markers.length >= MARKER_TYPES.length, `Got at least ${MARKER_TYPES.length} markers (2)`);

  // Sanity check that markers are in chronologically ascending order
  markers.reduce((previous, m) => {
    if (m.start <= previous) {
      ok(false, "Markers are not in order");
      info(markers);
    }
    return m.start;
  }, 0);

  // Override the timestamp marker's stack with our own recursive stack, which
  // can happen for unknown reasons (bug 1246555); we should not cause a crash
  // when attempting to render a recursive stack trace
  let timestampMarker = markers.find(m => m.name === "ConsoleTime");
  ok(typeof timestampMarker.stack === "number", "ConsoleTime marker has a stack before overwriting.");
  let frames = PerformanceController.getCurrentRecording().getFrames();
  let frameIndex = timestampMarker.stack = frames.length;
  frames.push({ line: 1, column: 1, source: "file.js", functionDisplayName: "test", parent: frameIndex + 1});
  frames.push({ line: 1, column: 1, source: "file.js", functionDisplayName: "test", parent: frameIndex + 2 });
  frames.push({ line: 1, column: 1, source: "file.js", functionDisplayName: "test", parent: frameIndex });

  const tests = {
    ConsoleTime: function (marker) {
      info("Got `ConsoleTime` marker with data: " + JSON.stringify(marker));
      ok(marker.stack === frameIndex, "Should have the ConsoleTime marker with recursive stack");
      shouldHaveStack($, "startStack", marker);
      shouldHaveStack($, "endStack", marker);
      shouldHaveLabel($, "Timer Name:", "!!!", marker);
      return true;
    },
    TimeStamp: function (marker) {
      info("Got `TimeStamp` marker with data: " + JSON.stringify(marker));
      shouldHaveLabel($, "Label:", "go", marker);
      shouldHaveStack($, "stack", marker);
      return true;
    },
    Styles: function (marker) {
      info("Got `Styles` marker with data: " + JSON.stringify(marker));
      if (marker.restyleHint) {
        shouldHaveLabel($, "Restyle Hint:", marker.restyleHint.replace(/eRestyle_/g, ""), marker);
      }
      if (marker.stack) {
        shouldHaveStack($, "stack", marker);
        return true;
      }
    },
    Reflow: function (marker) {
      info("Got `Reflow` marker with data: " + JSON.stringify(marker));
      if (marker.stack) {
        shouldHaveStack($, "stack", marker);
        return true;
      }
    }
  };

  // Keep track of all marker tests that are finished so we only
  // run through each marker test once, so we don't spam 500 redundant
  // tests.
  let testsDone = [];

  for (let i = 0; i < bars.length; i++) {
    let bar = bars[i];
    let m = markers[i];
    EventUtils.sendMouseEvent({ type: "mousedown" }, bar);

    if (tests[m.name]) {
      if (testsDone.indexOf(m.name) === -1) {
        let fullTestComplete = tests[m.name](m);
        if (fullTestComplete) {
          testsDone.push(m.name);
        }
      }
    } else {
      throw new Error(`No tests for ${m.name} -- should be filtered out.`);
    }

    if (testsDone.length === Object.keys(tests).length) {
      break;
    }
  }

  yield teardown(panel);
  finish();
}

function shouldHaveStack($, type, marker) {
  ok($(`#waterfall-details .marker-details-stack[type=${type}]`), `${marker.name} has a stack: ${type}`);
}

function shouldHaveLabel($, name, value, marker) {
  let $name = $(`#waterfall-details .marker-details-labelcontainer .marker-details-labelname[value="${name}"]`);
  let $value = $name.parentNode.querySelector(".marker-details-labelvalue");
  is($value.getAttribute("value"), value, `${marker.name} has correct label for ${name}:${value}`);
}
/* eslint-enable */