summaryrefslogtreecommitdiffstats
path: root/dom/media/test/browser/browser_encrypted_play_time_telemetry.js
blob: 1a64717419305fa99cd4cb8aef94972d69c89cef (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
/* 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/. */

// This test verifies that telemetry gathered around encrypted media playtime
// is gathered as expected.

"use strict";

/* import-globals-from ../eme_standalone.js */

// Clears any existing telemetry data that has been accumulated. Returns a
// promise the will be resolved once the telemetry store is clear.
async function clearTelemetry() {
  // There's an arbitrary interval of 2 seconds in which the content
  // processes sync their event data with the parent process, we wait
  // this out to ensure that we clear everything that is left over from
  // previous tests and don't receive random events in the middle of our tests.
  // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
  await new Promise(resolve => setTimeout(resolve, 2000));

  Services.telemetry.clearEvents();
  return TestUtils.waitForCondition(() => {
    let events = Services.telemetry.snapshotEvents(
      Ci.nsITelemetry.DATASET_ALL_CHANNELS,
      true
    ).content;
    return !events || !events.length;
  });
}

// Plays the media in `tab` until the 'ended' event is fire. Returns a promise
// that resolves once that state has been reached.
async function playMediaThrough(tab) {
  return SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
    let video = content.document.getElementById("media");
    await Promise.all([new Promise(r => (video.onended = r)), video.play()]);
  });
}

// Plays the media in `tab` until the 'timeupdate' event is fire. Returns a
// promise that resolves once that state has been reached.
async function playMediaToTimeUpdate(tab) {
  return SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
    let video = content.document.getElementById("media");
    await Promise.all([
      new Promise(r => (video.ontimeupdate = r)),
      video.play(),
    ]);
  });
}

// Aborts existing loads and replaces the media on the media element with an
// unencrypted file.
async function replaceMediaWithUnencrypted(tab) {
  return SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
    let video = content.document.getElementById("media");
    video.src = "gizmo.mp4";
    video.load();
  });
}

// Clears/nulls the media keys on the media in `tab`.
async function clearMediaKeys(tab) {
  return SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
    let video = content.document.getElementById("media");
    await video.setMediaKeys(null);
  });
}

// Wait for telemetry information to be received from the content process
// then get the relevant histograms for the tests and return the sums of
// those histograms. If a histogram does not exist this will return a 0
// sum. Returns a promise the resolves to an object with sums for
// - VIDEO_PLAY_TIME_MS
// - VIDEO_ENCRYPTED_PLAY_TIME_MS
// - VIDEO_CLEARKEY_PLAY_TIME_MS
// This function clears the histograms as it gets them.
async function getTelemetrySums() {
  // The telemetry was gathered in the content process, so we have to wait
  // until is arrived in the parent to check it. At time of writing there's
  // not a more elegant way of doing this than polling.
  return TestUtils.waitForCondition(() => {
    let histograms = Services.telemetry.getSnapshotForHistograms(
      "main",
      true
    ).content;
    // All the histogram data should come at the same time, so we just check
    // for playtime here as we always expect it in these tests, but we'll
    // grab other values if present.
    if (histograms.VIDEO_PLAY_TIME_MS) {
      // We only expect to have one value for each histogram, so returning the
      // sums is a short hand for returning that one value.
      return {
        VIDEO_PLAY_TIME_MS: histograms.VIDEO_PLAY_TIME_MS.sum,
        VIDEO_ENCRYPTED_PLAY_TIME_MS: histograms.VIDEO_ENCRYPTED_PLAY_TIME_MS
          ? histograms.VIDEO_ENCRYPTED_PLAY_TIME_MS.sum
          : 0,
        VIDEO_CLEARKEY_PLAY_TIME_MS: histograms.VIDEO_CLEARKEY_PLAY_TIME_MS
          ? histograms.VIDEO_CLEARKEY_PLAY_TIME_MS.sum
          : 0,
      };
    }
    return null;
  }, "recorded telemetry from playing media");
}

// Clear telemetry before other tests. Internally the tests clear the telemetry
// when they check it, so we shouldn't need to do this between tests.
add_task(clearTelemetry);

add_task(async function testEncryptedMediaPlayback() {
  let testTab = await openTab();

  await loadEmeVideo(testTab);
  await playMediaThrough(testTab);

  BrowserTestUtils.removeTab(testTab);

  let telemetrySums = await getTelemetrySums();

  ok(telemetrySums, "Should get play time telemetry");
  is(
    telemetrySums.VIDEO_PLAY_TIME_MS,
    telemetrySums.VIDEO_ENCRYPTED_PLAY_TIME_MS,
    "Play time should be the same as encrypted play time"
  );
  is(
    telemetrySums.VIDEO_PLAY_TIME_MS,
    telemetrySums.VIDEO_CLEARKEY_PLAY_TIME_MS,
    "Play time should be the same as clearkey play time"
  );
  Assert.greater(
    telemetrySums.VIDEO_PLAY_TIME_MS,
    0,
    "Should have a play time greater than zero"
  );
});

add_task(async function testChangingFromEncryptedToUnencrypted() {
  let testTab = await openTab();

  await loadEmeVideo(testTab);
  await replaceMediaWithUnencrypted(testTab);
  await playMediaToTimeUpdate(testTab);

  BrowserTestUtils.removeTab(testTab);

  let telemetrySums = await getTelemetrySums();

  ok(telemetrySums, "Should get play time telemetry");
  is(
    telemetrySums.VIDEO_ENCRYPTED_PLAY_TIME_MS,
    0,
    "Encrypted play time should be 0"
  );
  is(
    telemetrySums.VIDEO_PLAY_TIME_MS,
    telemetrySums.VIDEO_CLEARKEY_PLAY_TIME_MS,
    "Play time should be the same as clearkey play time because the media element still has a media keys attached"
  );
  Assert.greater(
    telemetrySums.VIDEO_PLAY_TIME_MS,
    0,
    "Should have a play time greater than zero"
  );
});

add_task(
  async function testChangingFromEncryptedToUnencryptedAndClearingMediaKeys() {
    let testTab = await openTab();

    await loadEmeVideo(testTab);
    await replaceMediaWithUnencrypted(testTab);
    await clearMediaKeys(testTab);
    await playMediaToTimeUpdate(testTab);

    BrowserTestUtils.removeTab(testTab);

    let telemetrySums = await getTelemetrySums();

    ok(telemetrySums, "Should get play time telemetry");
    is(
      telemetrySums.VIDEO_ENCRYPTED_PLAY_TIME_MS,
      0,
      "Encrypted play time should be 0"
    );
    is(
      telemetrySums.VIDEO_CLEARKEY_PLAY_TIME_MS,
      0,
      "Clearkey play time should be 0"
    );
    Assert.greater(
      telemetrySums.VIDEO_PLAY_TIME_MS,
      0,
      "Should have a play time greater than zero"
    );
  }
);