summaryrefslogtreecommitdiffstats
path: root/dom/media/test/test_video_low_power_telemetry.html
blob: be609f7ceb89e67d1d839f020b62737a8b12f0ec (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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1737682

This is a test of the macOS video low power telemetry, defined as GFX_MACOS_VIDEO_LOW_POWER
enums in Histograms.json. The test will load some video media files, play them for some
number of frames, then check that appropriate telemetry has been recorded. Since the
telemetry fires based on the number of frames painted, the test is structured around
the `media.video_stats.enabled` pref, which allows us to monitor the number of frames
that have been shown. The telemetry fires every 600 frames. To make sure we have enough
painted frames to trigger telemetry, we run the media for many frames and then check
that the expected telemetry value has been recorded at least once.

The tests are run via the MediaTestManager, which loads and plays the video sequentially.
Since we want to test different telemetry outcomes, we have some special setup and teardown
steps we run before each video. We keep track of which test we are running with a simple
1-based index, `testIndex`. So our test structure is:

1) Set some initial preferences that need to be set for the whole test series.
2) Start MediaTestManager with a set of media to play.
3) When a video arrives, increment testIndex and call preTest.
4) Run the video for PAINTED_FRAMES frames, then call postTest, which checks telemetry
   and cleans up.
5) Tell the MediaTestManager we've finished with that video, which triggers the next.
-->

<head>
  <title>Test of macOS video low power telemetry</title>
  <script src="/tests/SimpleTest/SimpleTest.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
  <script type="text/javascript" src="manifest.js"></script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1737682">Mozilla Bug 1737682</a>
<pre id="test">
<script class="testbody" type="text/javascript">
  // Parallel test must be disabled for media.video_stats.enabled is a global setting
  // to prevent the setting from changing unexpectedly in the middle of the test.
  PARALLEL_TESTS = 1;
  SimpleTest.waitForExplicitFinish();

  // How many frames do we run? Must be more than the value set by the
  // gfx.core-animation.low-power-telemetry-frames pref, because that's how
  // many frames must be shown before telemetry is emitted. We present for
  // longer than that to ensure that the telemetry is emitted at least once.
  // This also has to be enough frames to overcome the animated "<video> is
  // now fullscreen." popup that appears over the video. While that modal
  // is visible and animating away, it will register as an overlay and
  // prevent low power mode. As an estimate, we need at least 200 frames to
  // ensure that animation has finished and there has been time to fire the
  // telemetry at least once after it has finished.
  const TELEMETRY_FRAMES = SpecialPowers.getIntPref("gfx.core-animation.low-power-telemetry-frames");
  const PAINTED_FRAMES = Math.max(Math.floor(TELEMETRY_FRAMES * 1.1), 200 + TELEMETRY_FRAMES);
  info(`Running each video for ${PAINTED_FRAMES} frames.`);

  // Taken from TelemetryHistogramEnums.h
  const GFX_MACOS_VIDEO_LOW_POWER_LowPower = 1;
  const GFX_MACOS_VIDEO_LOW_POWER_FailWindowed = 3;

  var manager = new MediaTestManager;

  // Define some state variables that we'll use to manage multiple setup,
  // check, and teardown steps using the same preTest and postTest functions.
  var testIndex = 0;
  var testsComplete = false;

  async function retrieveSnapshotAndClearTelemetry() {
    return SpecialPowers.spawnChrome([], () => {
      let hist = Services.telemetry.getHistogramById("GFX_MACOS_VIDEO_LOW_POWER");
      let snap = hist.snapshot();
      hist.clear();
      return snap;
    });
  }

  function checkSnapshot(snap, category) {
    if (!snap) {
      return;
    }

    // For debugging purposes, build a string of the snapshot values hashmap.
    let valuesString = '';
    let keys = Object.keys(snap.values);
    keys.forEach(k => {
      valuesString += `[${k}] = ${snap.values[k]}, `;
    });
    info(`Test ${testIndex} telemetry values are ${valuesString}`);

    // Since we've run the media for more frames than the telemetry needs to
    // fire, we should have at least 1 of the expected value.
    let val = snap.values[category];
    if (!val) {
      val = 0;
    }
    ok(val > 0, `Test ${testIndex} enum ${category} should have at least 1 occurrence; found ${val}.`);
  }

  async function doPreTest(v) {
    if (testsComplete) {
      manager.finished(v.token);
      return;
    }

    switch (testIndex) {
      case 1: {
        // Test 1 (FailWindowed): No special setup.
        break;
      }

      case 2: {
        // Test 2 (LowPower): Enter fullscreen.

        // Wait one frame to ensure the old video is no longer in the layer tree, which
        // causes problems with the telemetry.
        await new Promise(resolve => requestAnimationFrame(resolve));

        info("Attempting to enter fullscreen.");
        ok(document.fullscreenEnabled, "Document should permit fullscreen-ing of elements.");
        await SpecialPowers.wrap(v).requestFullscreen();
        ok(document.fullscreenElement, "Document should have one element in fullscreen.");
        break;
      }
    }
  }

  async function doPostTest(v) {
    info(`Test ${testIndex} attempting to retrieve telemetry.`);
    let snap = await retrieveSnapshotAndClearTelemetry();
    ok(snap, `Test ${testIndex} should have telemetry.`);

    switch (testIndex) {
      case 1: {
        // Test 1 (FailWindowed): Just check.
        checkSnapshot(snap, GFX_MACOS_VIDEO_LOW_POWER_FailWindowed);
        break;
      }

      case 2: {
        // Test 2 (LowPower): Check, then exit fullscreen.
        checkSnapshot(snap, GFX_MACOS_VIDEO_LOW_POWER_LowPower);

        info("Attempting to exit fullscreen.");
        await SpecialPowers.wrap(document).exitFullscreen();
        ok(!document.fullscreenElement, "Document should be out of fullscreen.");

        // This is the last test.
        testsComplete = true;
        break;
      }
    }
  }

  function ontimeupdate(event) {
    let v = event.target;
    // Count painted frames to see when we should hit some telemetry threshholds.
    if (v.mozPaintedFrames >= PAINTED_FRAMES) {
      v.pause();
      v.removeEventListener("timeupdate", ontimeupdate);

      doPostTest(v).then(() => {
        let token = v.token;
        removeNodeAndSource(v);
        manager.finished(token);
      });
    }
  }

  function startTest(test, token) {
    manager.started(token);

    testIndex++;
    info(`Starting test ${testIndex} video ${test.name}.`);

    let v = document.createElement('video');
    v.addEventListener("timeupdate", ontimeupdate);
    v.token = token;
    v.src = test.name;
    v.loop = true;
    document.body.appendChild(v);

    doPreTest(v).then(() => {
      info(`Playing test ${testIndex}.`);
      v.play();
    });
  }

  SpecialPowers.pushPrefEnv({"set": [
        ["media.video_stats.enabled", true],
        ["gfx.core-animation.specialize-video", true],
    ]},
    async function() {
      // Clear out existing telemetry in case previous tests were displaying
      // video.
      info("Clearing initial telemetry.");
      await retrieveSnapshotAndClearTelemetry();

      ok(gVideoLowPowerTests.length >= 2, "Should have enough videos to test.");
      manager.runTests(gVideoLowPowerTests, startTest);
  });

</script>
</pre>
</body>
</html>