tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_video_low_power_telemetry.html (7736B)


      1 <!DOCTYPE HTML>
      2 <html>
      3 <!--
      4 https://bugzilla.mozilla.org/show_bug.cgi?id=1737682
      5 
      6 This is a test of the macOS video low power telemetry, defined as GFX_MACOS_VIDEO_LOW_POWER
      7 enums in Histograms.json. The test will load some video media files, play them for some
      8 number of frames, then check that appropriate telemetry has been recorded. Since the
      9 telemetry fires based on the number of frames painted, the test is structured around
     10 the `media.video_stats.enabled` pref, which allows us to monitor the number of frames
     11 that have been shown. The telemetry fires every 600 frames. To make sure we have enough
     12 painted frames to trigger telemetry, we run the media for many frames and then check
     13 that the expected telemetry value has been recorded at least once.
     14 
     15 The tests are run via the MediaTestManager, which loads and plays the video sequentially.
     16 Since we want to test different telemetry outcomes, we have some special setup and teardown
     17 steps we run before each video. We keep track of which test we are running with a simple
     18 1-based index, `testIndex`. So our test structure is:
     19 
     20 1) Set some initial preferences that need to be set for the whole test series.
     21 2) Start MediaTestManager with a set of media to play.
     22 3) When a video arrives, increment testIndex and call preTest.
     23 4) Run the video for PAINTED_FRAMES frames, then call postTest, which checks telemetry
     24   and cleans up.
     25 5) Tell the MediaTestManager we've finished with that video, which triggers the next.
     26 -->
     27 
     28 <head>
     29  <title>Test of macOS video low power telemetry</title>
     30  <script src="/tests/SimpleTest/SimpleTest.js"></script>
     31  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
     32  <script type="text/javascript" src="manifest.js"></script>
     33 </head>
     34 <body>
     35 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1737682">Mozilla Bug 1737682</a>
     36 <pre id="test">
     37 <script class="testbody" type="text/javascript">
     38  // Parallel test must be disabled for media.video_stats.enabled is a global setting
     39  // to prevent the setting from changing unexpectedly in the middle of the test.
     40  PARALLEL_TESTS = 1;
     41  SimpleTest.waitForExplicitFinish();
     42 
     43  // How many frames do we run? Must be more than the value set by the
     44  // gfx.core-animation.low-power-telemetry-frames pref, because that's how
     45  // many frames must be shown before telemetry is emitted. We present for
     46  // longer than that to ensure that the telemetry is emitted at least once.
     47  // This also has to be enough frames to overcome the animated "<video> is
     48  // now fullscreen." popup that appears over the video. While that modal
     49  // is visible and animating away, it will register as an overlay and
     50  // prevent low power mode. As an estimate, we need at least 200 frames to
     51  // ensure that animation has finished and there has been time to fire the
     52  // telemetry at least once after it has finished.
     53  const TELEMETRY_FRAMES = SpecialPowers.getIntPref("gfx.core-animation.low-power-telemetry-frames");
     54  const PAINTED_FRAMES = Math.max(Math.floor(TELEMETRY_FRAMES * 1.1), 200 + TELEMETRY_FRAMES);
     55  info(`Running each video for ${PAINTED_FRAMES} frames.`);
     56 
     57  // Taken from TelemetryHistogramEnums.h
     58  const GFX_MACOS_VIDEO_LOW_POWER_LowPower = 1;
     59  const GFX_MACOS_VIDEO_LOW_POWER_FailWindowed = 3;
     60 
     61  var manager = new MediaTestManager;
     62 
     63  // Define some state variables that we'll use to manage multiple setup,
     64  // check, and teardown steps using the same preTest and postTest functions.
     65  var testIndex = 0;
     66  var testsComplete = false;
     67 
     68  async function retrieveSnapshotAndClearTelemetry() {
     69    return SpecialPowers.spawnChrome([], () => {
     70      let hist = Services.telemetry.getHistogramById("GFX_MACOS_VIDEO_LOW_POWER");
     71      let snap = hist.snapshot();
     72      hist.clear();
     73      return snap;
     74    });
     75  }
     76 
     77  function checkSnapshot(snap, category) {
     78    if (!snap) {
     79      return;
     80    }
     81 
     82    // For debugging purposes, build a string of the snapshot values hashmap.
     83    let valuesString = '';
     84    let keys = Object.keys(snap.values);
     85    keys.forEach(k => {
     86      valuesString += `[${k}] = ${snap.values[k]}, `;
     87    });
     88    info(`Test ${testIndex} telemetry values are ${valuesString}`);
     89 
     90    // Since we've run the media for more frames than the telemetry needs to
     91    // fire, we should have at least 1 of the expected value.
     92    let val = snap.values[category];
     93    if (!val) {
     94      val = 0;
     95    }
     96    ok(val > 0, `Test ${testIndex} enum ${category} should have at least 1 occurrence; found ${val}.`);
     97  }
     98 
     99  async function doPreTest(v) {
    100    if (testsComplete) {
    101      manager.finished(v.token);
    102      return;
    103    }
    104 
    105    switch (testIndex) {
    106      case 1: {
    107        // Test 1 (FailWindowed): No special setup.
    108        break;
    109      }
    110 
    111      case 2: {
    112        // Test 2 (LowPower): Enter fullscreen.
    113 
    114        // Wait one frame to ensure the old video is no longer in the layer tree, which
    115        // causes problems with the telemetry.
    116        await new Promise(resolve => requestAnimationFrame(resolve));
    117 
    118        info("Attempting to enter fullscreen.");
    119        ok(document.fullscreenEnabled, "Document should permit fullscreen-ing of elements.");
    120        await SpecialPowers.wrap(v).requestFullscreen();
    121        ok(document.fullscreenElement, "Document should have one element in fullscreen.");
    122        break;
    123      }
    124    }
    125  }
    126 
    127  async function doPostTest() {
    128    info(`Test ${testIndex} attempting to retrieve telemetry.`);
    129    let snap = await retrieveSnapshotAndClearTelemetry();
    130    ok(snap, `Test ${testIndex} should have telemetry.`);
    131 
    132    switch (testIndex) {
    133      case 1: {
    134        // Test 1 (FailWindowed): Just check.
    135        checkSnapshot(snap, GFX_MACOS_VIDEO_LOW_POWER_FailWindowed);
    136        break;
    137      }
    138 
    139      case 2: {
    140        // Test 2 (LowPower): Check, then exit fullscreen.
    141        checkSnapshot(snap, GFX_MACOS_VIDEO_LOW_POWER_LowPower);
    142 
    143        info("Attempting to exit fullscreen.");
    144        await SpecialPowers.wrap(document).exitFullscreen();
    145        ok(!document.fullscreenElement, "Document should be out of fullscreen.");
    146 
    147        // This is the last test.
    148        testsComplete = true;
    149        break;
    150      }
    151    }
    152  }
    153 
    154  function ontimeupdate(event) {
    155    let v = event.target;
    156    // Count painted frames to see when we should hit some telemetry threshholds.
    157    if (v.mozPaintedFrames >= PAINTED_FRAMES) {
    158      v.pause();
    159      v.removeEventListener("timeupdate", ontimeupdate);
    160 
    161      doPostTest(v).then(() => {
    162        let token = v.token;
    163        removeNodeAndSource(v);
    164        manager.finished(token);
    165      });
    166    }
    167  }
    168 
    169  function startTest(test, token) {
    170    manager.started(token);
    171 
    172    testIndex++;
    173    info(`Starting test ${testIndex} video ${test.name}.`);
    174 
    175    let v = document.createElement('video');
    176    v.addEventListener("timeupdate", ontimeupdate);
    177    v.token = token;
    178    v.src = test.name;
    179    v.loop = true;
    180    document.body.appendChild(v);
    181 
    182    doPreTest(v).then(() => {
    183      info(`Playing test ${testIndex}.`);
    184      v.play();
    185    });
    186  }
    187 
    188  SpecialPowers.pushPrefEnv({"set": [
    189        ["media.video_stats.enabled", true],
    190        ["gfx.core-animation.specialize-video", true],
    191    ]},
    192    async function() {
    193      // Clear out existing telemetry in case previous tests were displaying
    194      // video.
    195      info("Clearing initial telemetry.");
    196      await retrieveSnapshotAndClearTelemetry();
    197 
    198      if (!gVideoLowPowerTests.length) {
    199        ok(false, "Need at least one video in gVideoLowPowerTests.");
    200        return;
    201      }
    202 
    203      let atLeast2Videos = gVideoLowPowerTests.slice();
    204      if (atLeast2Videos.length < 2) {
    205        atLeast2Videos.splice(1, 0, atLeast2Videos[0]);
    206      }
    207 
    208      manager.runTests(atLeast2Videos, startTest);
    209  });
    210 
    211 </script>
    212 </pre>
    213 </body>
    214 </html>